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         this.fireEvent("load", this, r, options, o);
758         if(options.callback){
759             options.callback.call(options.scope || this, r, options, true);
760         }
761     },
762
763
764     /**
765      * Loads data from a passed data block. A Reader which understands the format of the data
766      * must have been configured in the constructor.
767      * @param {Object} data The data block from which to read the Records.  The format of the data expected
768      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
769      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
770      */
771     loadData : function(o, append){
772         var r = this.reader.readRecords(o);
773         this.loadRecords(r, {add: append}, true);
774     },
775
776     /**
777      * Gets the number of cached records.
778      * <p>
779      * <em>If using paging, this may not be the total size of the dataset. If the data object
780      * used by the Reader contains the dataset size, then the getTotalCount() function returns
781      * the data set size</em>
782      */
783     getCount : function(){
784         return this.data.length || 0;
785     },
786
787     /**
788      * Gets the total number of records in the dataset as returned by the server.
789      * <p>
790      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
791      * the dataset size</em>
792      */
793     getTotalCount : function(){
794         return this.totalLength || 0;
795     },
796
797     /**
798      * Returns the sort state of the Store as an object with two properties:
799      * <pre><code>
800  field {String} The name of the field by which the Records are sorted
801  direction {String} The sort order, "ASC" or "DESC"
802      * </code></pre>
803      */
804     getSortState : function(){
805         return this.sortInfo;
806     },
807
808     // private
809     applySort : function(){
810         if(this.sortInfo && !this.remoteSort){
811             var s = this.sortInfo, f = s.field;
812             var st = this.fields.get(f).sortType;
813             var fn = function(r1, r2){
814                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
815                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
816             };
817             this.data.sort(s.direction, fn);
818             if(this.snapshot && this.snapshot != this.data){
819                 this.snapshot.sort(s.direction, fn);
820             }
821         }
822     },
823
824     /**
825      * Sets the default sort column and order to be used by the next load operation.
826      * @param {String} fieldName The name of the field to sort by.
827      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
828      */
829     setDefaultSort : function(field, dir){
830         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
831     },
832
833     /**
834      * Sort the Records.
835      * If remote sorting is used, the sort is performed on the server, and the cache is
836      * reloaded. If local sorting is used, the cache is sorted internally.
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     sort : function(fieldName, dir){
841         var f = this.fields.get(fieldName);
842         if(!dir){
843             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
844             
845             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
846                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
847             }else{
848                 dir = f.sortDir;
849             }
850         }
851         this.sortToggle[f.name] = dir;
852         this.sortInfo = {field: f.name, direction: dir};
853         if(!this.remoteSort){
854             this.applySort();
855             this.fireEvent("datachanged", this);
856         }else{
857             this.load(this.lastOptions);
858         }
859     },
860
861     /**
862      * Calls the specified function for each of the Records in the cache.
863      * @param {Function} fn The function to call. The Record is passed as the first parameter.
864      * Returning <em>false</em> aborts and exits the iteration.
865      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
866      */
867     each : function(fn, scope){
868         this.data.each(fn, scope);
869     },
870
871     /**
872      * Gets all records modified since the last commit.  Modified records are persisted across load operations
873      * (e.g., during paging).
874      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
875      */
876     getModifiedRecords : function(){
877         return this.modified;
878     },
879
880     // private
881     createFilterFn : function(property, value, anyMatch){
882         if(!value.exec){ // not a regex
883             value = String(value);
884             if(value.length == 0){
885                 return false;
886             }
887             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
888         }
889         return function(r){
890             return value.test(r.data[property]);
891         };
892     },
893
894     /**
895      * Sums the value of <i>property</i> for each record between start and end and returns the result.
896      * @param {String} property A field on your records
897      * @param {Number} start The record index to start at (defaults to 0)
898      * @param {Number} end The last record index to include (defaults to length - 1)
899      * @return {Number} The sum
900      */
901     sum : function(property, start, end){
902         var rs = this.data.items, v = 0;
903         start = start || 0;
904         end = (end || end === 0) ? end : rs.length-1;
905
906         for(var i = start; i <= end; i++){
907             v += (rs[i].data[property] || 0);
908         }
909         return v;
910     },
911
912     /**
913      * Filter the records by a specified property.
914      * @param {String} field A field on your records
915      * @param {String/RegExp} value Either a string that the field
916      * should start with or a RegExp to test against the field
917      * @param {Boolean} anyMatch True to match any part not just the beginning
918      */
919     filter : function(property, value, anyMatch){
920         var fn = this.createFilterFn(property, value, anyMatch);
921         return fn ? this.filterBy(fn) : this.clearFilter();
922     },
923
924     /**
925      * Filter by a function. The specified function will be called with each
926      * record in this data source. If the function returns true the record is included,
927      * otherwise it is filtered.
928      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
929      * @param {Object} scope (optional) The scope of the function (defaults to this)
930      */
931     filterBy : function(fn, scope){
932         this.snapshot = this.snapshot || this.data;
933         this.data = this.queryBy(fn, scope||this);
934         this.fireEvent("datachanged", this);
935     },
936
937     /**
938      * Query the records by a specified property.
939      * @param {String} field A field on your records
940      * @param {String/RegExp} value Either a string that the field
941      * should start with or a RegExp to test against the field
942      * @param {Boolean} anyMatch True to match any part not just the beginning
943      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
944      */
945     query : function(property, value, anyMatch){
946         var fn = this.createFilterFn(property, value, anyMatch);
947         return fn ? this.queryBy(fn) : this.data.clone();
948     },
949
950     /**
951      * Query by a function. The specified function will be called with each
952      * record in this data source. If the function returns true the record is included
953      * in the results.
954      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
955      * @param {Object} scope (optional) The scope of the function (defaults to this)
956       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
957      **/
958     queryBy : function(fn, scope){
959         var data = this.snapshot || this.data;
960         return data.filterBy(fn, scope||this);
961     },
962
963     /**
964      * Collects unique values for a particular dataIndex from this store.
965      * @param {String} dataIndex The property to collect
966      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
967      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
968      * @return {Array} An array of the unique values
969      **/
970     collect : function(dataIndex, allowNull, bypassFilter){
971         var d = (bypassFilter === true && this.snapshot) ?
972                 this.snapshot.items : this.data.items;
973         var v, sv, r = [], l = {};
974         for(var i = 0, len = d.length; i < len; i++){
975             v = d[i].data[dataIndex];
976             sv = String(v);
977             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
978                 l[sv] = true;
979                 r[r.length] = v;
980             }
981         }
982         return r;
983     },
984
985     /**
986      * Revert to a view of the Record cache with no filtering applied.
987      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
988      */
989     clearFilter : function(suppressEvent){
990         if(this.snapshot && this.snapshot != this.data){
991             this.data = this.snapshot;
992             delete this.snapshot;
993             if(suppressEvent !== true){
994                 this.fireEvent("datachanged", this);
995             }
996         }
997     },
998
999     // private
1000     afterEdit : function(record){
1001         if(this.modified.indexOf(record) == -1){
1002             this.modified.push(record);
1003         }
1004         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1005     },
1006     
1007     // private
1008     afterReject : function(record){
1009         this.modified.remove(record);
1010         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1011     },
1012
1013     // private
1014     afterCommit : function(record){
1015         this.modified.remove(record);
1016         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1017     },
1018
1019     /**
1020      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1021      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1022      */
1023     commitChanges : function(){
1024         var m = this.modified.slice(0);
1025         this.modified = [];
1026         for(var i = 0, len = m.length; i < len; i++){
1027             m[i].commit();
1028         }
1029     },
1030
1031     /**
1032      * Cancel outstanding changes on all changed records.
1033      */
1034     rejectChanges : 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].reject();
1039         }
1040     },
1041
1042     onMetaChange : function(meta, rtype, o){
1043         this.recordType = rtype;
1044         this.fields = rtype.prototype.fields;
1045         delete this.snapshot;
1046         this.sortInfo = meta.sortInfo || this.sortInfo;
1047         this.modified = [];
1048         this.fireEvent('metachange', this, this.reader.meta);
1049     },
1050     
1051     moveIndex : function(data, type)
1052     {
1053         var index = this.indexOf(data);
1054         
1055         var newIndex = index + type;
1056         
1057         this.remove(data);
1058         
1059         this.insert(newIndex, data);
1060         
1061     }
1062 });/*
1063  * Based on:
1064  * Ext JS Library 1.1.1
1065  * Copyright(c) 2006-2007, Ext JS, LLC.
1066  *
1067  * Originally Released Under LGPL - original licence link has changed is not relivant.
1068  *
1069  * Fork - LGPL
1070  * <script type="text/javascript">
1071  */
1072
1073 /**
1074  * @class Roo.data.SimpleStore
1075  * @extends Roo.data.Store
1076  * Small helper class to make creating Stores from Array data easier.
1077  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1078  * @cfg {Array} fields An array of field definition objects, or field name strings.
1079  * @cfg {Array} data The multi-dimensional array of data
1080  * @constructor
1081  * @param {Object} config
1082  */
1083 Roo.data.SimpleStore = function(config){
1084     Roo.data.SimpleStore.superclass.constructor.call(this, {
1085         isLocal : true,
1086         reader: new Roo.data.ArrayReader({
1087                 id: config.id
1088             },
1089             Roo.data.Record.create(config.fields)
1090         ),
1091         proxy : new Roo.data.MemoryProxy(config.data)
1092     });
1093     this.load();
1094 };
1095 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1096  * Based on:
1097  * Ext JS Library 1.1.1
1098  * Copyright(c) 2006-2007, Ext JS, LLC.
1099  *
1100  * Originally Released Under LGPL - original licence link has changed is not relivant.
1101  *
1102  * Fork - LGPL
1103  * <script type="text/javascript">
1104  */
1105
1106 /**
1107 /**
1108  * @extends Roo.data.Store
1109  * @class Roo.data.JsonStore
1110  * Small helper class to make creating Stores for JSON data easier. <br/>
1111 <pre><code>
1112 var store = new Roo.data.JsonStore({
1113     url: 'get-images.php',
1114     root: 'images',
1115     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1116 });
1117 </code></pre>
1118  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1119  * JsonReader and HttpProxy (unless inline data is provided).</b>
1120  * @cfg {Array} fields An array of field definition objects, or field name strings.
1121  * @constructor
1122  * @param {Object} config
1123  */
1124 Roo.data.JsonStore = function(c){
1125     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1126         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1127         reader: new Roo.data.JsonReader(c, c.fields)
1128     }));
1129 };
1130 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1131  * Based on:
1132  * Ext JS Library 1.1.1
1133  * Copyright(c) 2006-2007, Ext JS, LLC.
1134  *
1135  * Originally Released Under LGPL - original licence link has changed is not relivant.
1136  *
1137  * Fork - LGPL
1138  * <script type="text/javascript">
1139  */
1140
1141  
1142 Roo.data.Field = function(config){
1143     if(typeof config == "string"){
1144         config = {name: config};
1145     }
1146     Roo.apply(this, config);
1147     
1148     if(!this.type){
1149         this.type = "auto";
1150     }
1151     
1152     var st = Roo.data.SortTypes;
1153     // named sortTypes are supported, here we look them up
1154     if(typeof this.sortType == "string"){
1155         this.sortType = st[this.sortType];
1156     }
1157     
1158     // set default sortType for strings and dates
1159     if(!this.sortType){
1160         switch(this.type){
1161             case "string":
1162                 this.sortType = st.asUCString;
1163                 break;
1164             case "date":
1165                 this.sortType = st.asDate;
1166                 break;
1167             default:
1168                 this.sortType = st.none;
1169         }
1170     }
1171
1172     // define once
1173     var stripRe = /[\$,%]/g;
1174
1175     // prebuilt conversion function for this field, instead of
1176     // switching every time we're reading a value
1177     if(!this.convert){
1178         var cv, dateFormat = this.dateFormat;
1179         switch(this.type){
1180             case "":
1181             case "auto":
1182             case undefined:
1183                 cv = function(v){ return v; };
1184                 break;
1185             case "string":
1186                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1187                 break;
1188             case "int":
1189                 cv = function(v){
1190                     return v !== undefined && v !== null && v !== '' ?
1191                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1192                     };
1193                 break;
1194             case "float":
1195                 cv = function(v){
1196                     return v !== undefined && v !== null && v !== '' ?
1197                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1198                     };
1199                 break;
1200             case "bool":
1201             case "boolean":
1202                 cv = function(v){ return v === true || v === "true" || v == 1; };
1203                 break;
1204             case "date":
1205                 cv = function(v){
1206                     if(!v){
1207                         return '';
1208                     }
1209                     if(v instanceof Date){
1210                         return v;
1211                     }
1212                     if(dateFormat){
1213                         if(dateFormat == "timestamp"){
1214                             return new Date(v*1000);
1215                         }
1216                         return Date.parseDate(v, dateFormat);
1217                     }
1218                     var parsed = Date.parse(v);
1219                     return parsed ? new Date(parsed) : null;
1220                 };
1221              break;
1222             
1223         }
1224         this.convert = cv;
1225     }
1226 };
1227
1228 Roo.data.Field.prototype = {
1229     dateFormat: null,
1230     defaultValue: "",
1231     mapping: null,
1232     sortType : null,
1233     sortDir : "ASC"
1234 };/*
1235  * Based on:
1236  * Ext JS Library 1.1.1
1237  * Copyright(c) 2006-2007, Ext JS, LLC.
1238  *
1239  * Originally Released Under LGPL - original licence link has changed is not relivant.
1240  *
1241  * Fork - LGPL
1242  * <script type="text/javascript">
1243  */
1244  
1245 // Base class for reading structured data from a data source.  This class is intended to be
1246 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1247
1248 /**
1249  * @class Roo.data.DataReader
1250  * Base class for reading structured data from a data source.  This class is intended to be
1251  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1252  */
1253
1254 Roo.data.DataReader = function(meta, recordType){
1255     
1256     this.meta = meta;
1257     
1258     this.recordType = recordType instanceof Array ? 
1259         Roo.data.Record.create(recordType) : recordType;
1260 };
1261
1262 Roo.data.DataReader.prototype = {
1263      /**
1264      * Create an empty record
1265      * @param {Object} data (optional) - overlay some values
1266      * @return {Roo.data.Record} record created.
1267      */
1268     newRow :  function(d) {
1269         var da =  {};
1270         this.recordType.prototype.fields.each(function(c) {
1271             switch( c.type) {
1272                 case 'int' : da[c.name] = 0; break;
1273                 case 'date' : da[c.name] = new Date(); break;
1274                 case 'float' : da[c.name] = 0.0; break;
1275                 case 'boolean' : da[c.name] = false; break;
1276                 default : da[c.name] = ""; break;
1277             }
1278             
1279         });
1280         return new this.recordType(Roo.apply(da, d));
1281     }
1282     
1283 };/*
1284  * Based on:
1285  * Ext JS Library 1.1.1
1286  * Copyright(c) 2006-2007, Ext JS, LLC.
1287  *
1288  * Originally Released Under LGPL - original licence link has changed is not relivant.
1289  *
1290  * Fork - LGPL
1291  * <script type="text/javascript">
1292  */
1293
1294 /**
1295  * @class Roo.data.DataProxy
1296  * @extends Roo.data.Observable
1297  * This class is an abstract base class for implementations which provide retrieval of
1298  * unformatted data objects.<br>
1299  * <p>
1300  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1301  * (of the appropriate type which knows how to parse the data object) to provide a block of
1302  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1303  * <p>
1304  * Custom implementations must implement the load method as described in
1305  * {@link Roo.data.HttpProxy#load}.
1306  */
1307 Roo.data.DataProxy = function(){
1308     this.addEvents({
1309         /**
1310          * @event beforeload
1311          * Fires before a network request is made to retrieve a data object.
1312          * @param {Object} This DataProxy object.
1313          * @param {Object} params The params parameter to the load function.
1314          */
1315         beforeload : true,
1316         /**
1317          * @event load
1318          * Fires before the load method's callback is called.
1319          * @param {Object} This DataProxy object.
1320          * @param {Object} o The data object.
1321          * @param {Object} arg The callback argument object passed to the load function.
1322          */
1323         load : true,
1324         /**
1325          * @event loadexception
1326          * Fires if an Exception occurs during data retrieval.
1327          * @param {Object} This DataProxy object.
1328          * @param {Object} o The data object.
1329          * @param {Object} arg The callback argument object passed to the load function.
1330          * @param {Object} e The Exception.
1331          */
1332         loadexception : true
1333     });
1334     Roo.data.DataProxy.superclass.constructor.call(this);
1335 };
1336
1337 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1338
1339     /**
1340      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1341      */
1342 /*
1343  * Based on:
1344  * Ext JS Library 1.1.1
1345  * Copyright(c) 2006-2007, Ext JS, LLC.
1346  *
1347  * Originally Released Under LGPL - original licence link has changed is not relivant.
1348  *
1349  * Fork - LGPL
1350  * <script type="text/javascript">
1351  */
1352 /**
1353  * @class Roo.data.MemoryProxy
1354  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1355  * to the Reader when its load method is called.
1356  * @constructor
1357  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1358  */
1359 Roo.data.MemoryProxy = function(data){
1360     if (data.data) {
1361         data = data.data;
1362     }
1363     Roo.data.MemoryProxy.superclass.constructor.call(this);
1364     this.data = data;
1365 };
1366
1367 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1368     
1369     /**
1370      * Load data from the requested source (in this case an in-memory
1371      * data object passed to the constructor), read the data object into
1372      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1373      * process that block using the passed callback.
1374      * @param {Object} params This parameter is not used by the MemoryProxy class.
1375      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1376      * object into a block of Roo.data.Records.
1377      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1378      * The function must be passed <ul>
1379      * <li>The Record block object</li>
1380      * <li>The "arg" argument from the load function</li>
1381      * <li>A boolean success indicator</li>
1382      * </ul>
1383      * @param {Object} scope The scope in which to call the callback
1384      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1385      */
1386     load : function(params, reader, callback, scope, arg){
1387         params = params || {};
1388         var result;
1389         try {
1390             result = reader.readRecords(this.data);
1391         }catch(e){
1392             this.fireEvent("loadexception", this, arg, null, e);
1393             callback.call(scope, null, arg, false);
1394             return;
1395         }
1396         callback.call(scope, result, arg, true);
1397     },
1398     
1399     // private
1400     update : function(params, records){
1401         
1402     }
1403 });/*
1404  * Based on:
1405  * Ext JS Library 1.1.1
1406  * Copyright(c) 2006-2007, Ext JS, LLC.
1407  *
1408  * Originally Released Under LGPL - original licence link has changed is not relivant.
1409  *
1410  * Fork - LGPL
1411  * <script type="text/javascript">
1412  */
1413 /**
1414  * @class Roo.data.HttpProxy
1415  * @extends Roo.data.DataProxy
1416  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1417  * configured to reference a certain URL.<br><br>
1418  * <p>
1419  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1420  * from which the running page was served.<br><br>
1421  * <p>
1422  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1423  * <p>
1424  * Be aware that to enable the browser to parse an XML document, the server must set
1425  * the Content-Type header in the HTTP response to "text/xml".
1426  * @constructor
1427  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1428  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1429  * will be used to make the request.
1430  */
1431 Roo.data.HttpProxy = function(conn){
1432     Roo.data.HttpProxy.superclass.constructor.call(this);
1433     // is conn a conn config or a real conn?
1434     this.conn = conn;
1435     this.useAjax = !conn || !conn.events;
1436   
1437 };
1438
1439 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1440     // thse are take from connection...
1441     
1442     /**
1443      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1444      */
1445     /**
1446      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1447      * extra parameters to each request made by this object. (defaults to undefined)
1448      */
1449     /**
1450      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1451      *  to each request made by this object. (defaults to undefined)
1452      */
1453     /**
1454      * @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)
1455      */
1456     /**
1457      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1458      */
1459      /**
1460      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1461      * @type Boolean
1462      */
1463   
1464
1465     /**
1466      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1467      * @type Boolean
1468      */
1469     /**
1470      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1471      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1472      * a finer-grained basis than the DataProxy events.
1473      */
1474     getConnection : function(){
1475         return this.useAjax ? Roo.Ajax : this.conn;
1476     },
1477
1478     /**
1479      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1480      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1481      * process that block using the passed callback.
1482      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1483      * for the request to the remote server.
1484      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1485      * object into a block of Roo.data.Records.
1486      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1487      * The function must be passed <ul>
1488      * <li>The Record block object</li>
1489      * <li>The "arg" argument from the load function</li>
1490      * <li>A boolean success indicator</li>
1491      * </ul>
1492      * @param {Object} scope The scope in which to call the callback
1493      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1494      */
1495     load : function(params, reader, callback, scope, arg){
1496         if(this.fireEvent("beforeload", this, params) !== false){
1497             var  o = {
1498                 params : params || {},
1499                 request: {
1500                     callback : callback,
1501                     scope : scope,
1502                     arg : arg
1503                 },
1504                 reader: reader,
1505                 callback : this.loadResponse,
1506                 scope: this
1507             };
1508             if(this.useAjax){
1509                 Roo.applyIf(o, this.conn);
1510                 if(this.activeRequest){
1511                     Roo.Ajax.abort(this.activeRequest);
1512                 }
1513                 this.activeRequest = Roo.Ajax.request(o);
1514             }else{
1515                 this.conn.request(o);
1516             }
1517         }else{
1518             callback.call(scope||this, null, arg, false);
1519         }
1520     },
1521
1522     // private
1523     loadResponse : function(o, success, response){
1524         delete this.activeRequest;
1525         if(!success){
1526             this.fireEvent("loadexception", this, o, response);
1527             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1528             return;
1529         }
1530         var result;
1531         try {
1532             result = o.reader.read(response);
1533         }catch(e){
1534             this.fireEvent("loadexception", this, o, response, e);
1535             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1536             return;
1537         }
1538         
1539         this.fireEvent("load", this, o, o.request.arg);
1540         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1541     },
1542
1543     // private
1544     update : function(dataSet){
1545
1546     },
1547
1548     // private
1549     updateResponse : function(dataSet){
1550
1551     }
1552 });/*
1553  * Based on:
1554  * Ext JS Library 1.1.1
1555  * Copyright(c) 2006-2007, Ext JS, LLC.
1556  *
1557  * Originally Released Under LGPL - original licence link has changed is not relivant.
1558  *
1559  * Fork - LGPL
1560  * <script type="text/javascript">
1561  */
1562
1563 /**
1564  * @class Roo.data.ScriptTagProxy
1565  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1566  * other than the originating domain of the running page.<br><br>
1567  * <p>
1568  * <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
1569  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1570  * <p>
1571  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1572  * source code that is used as the source inside a &lt;script> tag.<br><br>
1573  * <p>
1574  * In order for the browser to process the returned data, the server must wrap the data object
1575  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1576  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1577  * depending on whether the callback name was passed:
1578  * <p>
1579  * <pre><code>
1580 boolean scriptTag = false;
1581 String cb = request.getParameter("callback");
1582 if (cb != null) {
1583     scriptTag = true;
1584     response.setContentType("text/javascript");
1585 } else {
1586     response.setContentType("application/x-json");
1587 }
1588 Writer out = response.getWriter();
1589 if (scriptTag) {
1590     out.write(cb + "(");
1591 }
1592 out.print(dataBlock.toJsonString());
1593 if (scriptTag) {
1594     out.write(");");
1595 }
1596 </pre></code>
1597  *
1598  * @constructor
1599  * @param {Object} config A configuration object.
1600  */
1601 Roo.data.ScriptTagProxy = function(config){
1602     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1603     Roo.apply(this, config);
1604     this.head = document.getElementsByTagName("head")[0];
1605 };
1606
1607 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1608
1609 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1610     /**
1611      * @cfg {String} url The URL from which to request the data object.
1612      */
1613     /**
1614      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1615      */
1616     timeout : 30000,
1617     /**
1618      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1619      * the server the name of the callback function set up by the load call to process the returned data object.
1620      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1621      * javascript output which calls this named function passing the data object as its only parameter.
1622      */
1623     callbackParam : "callback",
1624     /**
1625      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1626      * name to the request.
1627      */
1628     nocache : true,
1629
1630     /**
1631      * Load data from the configured URL, read the data object into
1632      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1633      * process that block using the passed callback.
1634      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1635      * for the request to the remote server.
1636      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1637      * object into a block of Roo.data.Records.
1638      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1639      * The function must be passed <ul>
1640      * <li>The Record block object</li>
1641      * <li>The "arg" argument from the load function</li>
1642      * <li>A boolean success indicator</li>
1643      * </ul>
1644      * @param {Object} scope The scope in which to call the callback
1645      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1646      */
1647     load : function(params, reader, callback, scope, arg){
1648         if(this.fireEvent("beforeload", this, params) !== false){
1649
1650             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1651
1652             var url = this.url;
1653             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1654             if(this.nocache){
1655                 url += "&_dc=" + (new Date().getTime());
1656             }
1657             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1658             var trans = {
1659                 id : transId,
1660                 cb : "stcCallback"+transId,
1661                 scriptId : "stcScript"+transId,
1662                 params : params,
1663                 arg : arg,
1664                 url : url,
1665                 callback : callback,
1666                 scope : scope,
1667                 reader : reader
1668             };
1669             var conn = this;
1670
1671             window[trans.cb] = function(o){
1672                 conn.handleResponse(o, trans);
1673             };
1674
1675             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1676
1677             if(this.autoAbort !== false){
1678                 this.abort();
1679             }
1680
1681             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1682
1683             var script = document.createElement("script");
1684             script.setAttribute("src", url);
1685             script.setAttribute("type", "text/javascript");
1686             script.setAttribute("id", trans.scriptId);
1687             this.head.appendChild(script);
1688
1689             this.trans = trans;
1690         }else{
1691             callback.call(scope||this, null, arg, false);
1692         }
1693     },
1694
1695     // private
1696     isLoading : function(){
1697         return this.trans ? true : false;
1698     },
1699
1700     /**
1701      * Abort the current server request.
1702      */
1703     abort : function(){
1704         if(this.isLoading()){
1705             this.destroyTrans(this.trans);
1706         }
1707     },
1708
1709     // private
1710     destroyTrans : function(trans, isLoaded){
1711         this.head.removeChild(document.getElementById(trans.scriptId));
1712         clearTimeout(trans.timeoutId);
1713         if(isLoaded){
1714             window[trans.cb] = undefined;
1715             try{
1716                 delete window[trans.cb];
1717             }catch(e){}
1718         }else{
1719             // if hasn't been loaded, wait for load to remove it to prevent script error
1720             window[trans.cb] = function(){
1721                 window[trans.cb] = undefined;
1722                 try{
1723                     delete window[trans.cb];
1724                 }catch(e){}
1725             };
1726         }
1727     },
1728
1729     // private
1730     handleResponse : function(o, trans){
1731         this.trans = false;
1732         this.destroyTrans(trans, true);
1733         var result;
1734         try {
1735             result = trans.reader.readRecords(o);
1736         }catch(e){
1737             this.fireEvent("loadexception", this, o, trans.arg, e);
1738             trans.callback.call(trans.scope||window, null, trans.arg, false);
1739             return;
1740         }
1741         this.fireEvent("load", this, o, trans.arg);
1742         trans.callback.call(trans.scope||window, result, trans.arg, true);
1743     },
1744
1745     // private
1746     handleFailure : function(trans){
1747         this.trans = false;
1748         this.destroyTrans(trans, false);
1749         this.fireEvent("loadexception", this, null, trans.arg);
1750         trans.callback.call(trans.scope||window, null, trans.arg, false);
1751     }
1752 });/*
1753  * Based on:
1754  * Ext JS Library 1.1.1
1755  * Copyright(c) 2006-2007, Ext JS, LLC.
1756  *
1757  * Originally Released Under LGPL - original licence link has changed is not relivant.
1758  *
1759  * Fork - LGPL
1760  * <script type="text/javascript">
1761  */
1762
1763 /**
1764  * @class Roo.data.JsonReader
1765  * @extends Roo.data.DataReader
1766  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1767  * based on mappings in a provided Roo.data.Record constructor.
1768  * 
1769  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1770  * in the reply previously. 
1771  * 
1772  * <p>
1773  * Example code:
1774  * <pre><code>
1775 var RecordDef = Roo.data.Record.create([
1776     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1777     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1778 ]);
1779 var myReader = new Roo.data.JsonReader({
1780     totalProperty: "results",    // The property which contains the total dataset size (optional)
1781     root: "rows",                // The property which contains an Array of row objects
1782     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1783 }, RecordDef);
1784 </code></pre>
1785  * <p>
1786  * This would consume a JSON file like this:
1787  * <pre><code>
1788 { 'results': 2, 'rows': [
1789     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1790     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1791 }
1792 </code></pre>
1793  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1794  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1795  * paged from the remote server.
1796  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1797  * @cfg {String} root name of the property which contains the Array of row objects.
1798  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1799  * @cfg {Array} fields Array of field definition objects
1800  * @constructor
1801  * Create a new JsonReader
1802  * @param {Object} meta Metadata configuration options
1803  * @param {Object} recordType Either an Array of field definition objects,
1804  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1805  */
1806 Roo.data.JsonReader = function(meta, recordType){
1807     
1808     meta = meta || {};
1809     // set some defaults:
1810     Roo.applyIf(meta, {
1811         totalProperty: 'total',
1812         successProperty : 'success',
1813         root : 'data',
1814         id : 'id'
1815     });
1816     
1817     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1818 };
1819 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1820     
1821     /**
1822      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1823      * Used by Store query builder to append _requestMeta to params.
1824      * 
1825      */
1826     metaFromRemote : false,
1827     /**
1828      * This method is only used by a DataProxy which has retrieved data from a remote server.
1829      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1830      * @return {Object} data A data block which is used by an Roo.data.Store object as
1831      * a cache of Roo.data.Records.
1832      */
1833     read : function(response){
1834         var json = response.responseText;
1835        
1836         var o = /* eval:var:o */ eval("("+json+")");
1837         if(!o) {
1838             throw {message: "JsonReader.read: Json object not found"};
1839         }
1840         
1841         if(o.metaData){
1842             
1843             delete this.ef;
1844             this.metaFromRemote = true;
1845             this.meta = o.metaData;
1846             this.recordType = Roo.data.Record.create(o.metaData.fields);
1847             this.onMetaChange(this.meta, this.recordType, o);
1848         }
1849         return this.readRecords(o);
1850     },
1851
1852     // private function a store will implement
1853     onMetaChange : function(meta, recordType, o){
1854
1855     },
1856
1857     /**
1858          * @ignore
1859          */
1860     simpleAccess: function(obj, subsc) {
1861         return obj[subsc];
1862     },
1863
1864         /**
1865          * @ignore
1866          */
1867     getJsonAccessor: function(){
1868         var re = /[\[\.]/;
1869         return function(expr) {
1870             try {
1871                 return(re.test(expr))
1872                     ? new Function("obj", "return obj." + expr)
1873                     : function(obj){
1874                         return obj[expr];
1875                     };
1876             } catch(e){}
1877             return Roo.emptyFn;
1878         };
1879     }(),
1880
1881     /**
1882      * Create a data block containing Roo.data.Records from an XML document.
1883      * @param {Object} o An object which contains an Array of row objects in the property specified
1884      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1885      * which contains the total size of the dataset.
1886      * @return {Object} data A data block which is used by an Roo.data.Store object as
1887      * a cache of Roo.data.Records.
1888      */
1889     readRecords : function(o){
1890         /**
1891          * After any data loads, the raw JSON data is available for further custom processing.
1892          * @type Object
1893          */
1894         this.o = o;
1895         var s = this.meta, Record = this.recordType,
1896             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1897
1898 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1899         if (!this.ef) {
1900             if(s.totalProperty) {
1901                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1902                 }
1903                 if(s.successProperty) {
1904                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1905                 }
1906                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1907                 if (s.id) {
1908                         var g = this.getJsonAccessor(s.id);
1909                         this.getId = function(rec) {
1910                                 var r = g(rec);  
1911                                 return (r === undefined || r === "") ? null : r;
1912                         };
1913                 } else {
1914                         this.getId = function(){return null;};
1915                 }
1916             this.ef = [];
1917             for(var jj = 0; jj < fl; jj++){
1918                 f = fi[jj];
1919                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1920                 this.ef[jj] = this.getJsonAccessor(map);
1921             }
1922         }
1923
1924         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1925         if(s.totalProperty){
1926             var vt = parseInt(this.getTotal(o), 10);
1927             if(!isNaN(vt)){
1928                 totalRecords = vt;
1929             }
1930         }
1931         if(s.successProperty){
1932             var vs = this.getSuccess(o);
1933             if(vs === false || vs === 'false'){
1934                 success = false;
1935             }
1936         }
1937         var records = [];
1938         for(var i = 0; i < c; i++){
1939                 var n = root[i];
1940             var values = {};
1941             var id = this.getId(n);
1942             for(var j = 0; j < fl; j++){
1943                 f = fi[j];
1944             var v = this.ef[j](n);
1945             if (!f.convert) {
1946                 Roo.log('missing convert for ' + f.name);
1947                 Roo.log(f);
1948                 continue;
1949             }
1950             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1951             }
1952             var record = new Record(values, id);
1953             record.json = n;
1954             records[i] = record;
1955         }
1956         return {
1957             raw : o,
1958             success : success,
1959             records : records,
1960             totalRecords : totalRecords
1961         };
1962     }
1963 });/*
1964  * Based on:
1965  * Ext JS Library 1.1.1
1966  * Copyright(c) 2006-2007, Ext JS, LLC.
1967  *
1968  * Originally Released Under LGPL - original licence link has changed is not relivant.
1969  *
1970  * Fork - LGPL
1971  * <script type="text/javascript">
1972  */
1973
1974 /**
1975  * @class Roo.data.XmlReader
1976  * @extends Roo.data.DataReader
1977  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1978  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1979  * <p>
1980  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1981  * header in the HTTP response must be set to "text/xml".</em>
1982  * <p>
1983  * Example code:
1984  * <pre><code>
1985 var RecordDef = Roo.data.Record.create([
1986    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1987    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1988 ]);
1989 var myReader = new Roo.data.XmlReader({
1990    totalRecords: "results", // The element which contains the total dataset size (optional)
1991    record: "row",           // The repeated element which contains row information
1992    id: "id"                 // The element within the row that provides an ID for the record (optional)
1993 }, RecordDef);
1994 </code></pre>
1995  * <p>
1996  * This would consume an XML file like this:
1997  * <pre><code>
1998 &lt;?xml?>
1999 &lt;dataset>
2000  &lt;results>2&lt;/results>
2001  &lt;row>
2002    &lt;id>1&lt;/id>
2003    &lt;name>Bill&lt;/name>
2004    &lt;occupation>Gardener&lt;/occupation>
2005  &lt;/row>
2006  &lt;row>
2007    &lt;id>2&lt;/id>
2008    &lt;name>Ben&lt;/name>
2009    &lt;occupation>Horticulturalist&lt;/occupation>
2010  &lt;/row>
2011 &lt;/dataset>
2012 </code></pre>
2013  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2014  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2015  * paged from the remote server.
2016  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2017  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2018  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2019  * a record identifier value.
2020  * @constructor
2021  * Create a new XmlReader
2022  * @param {Object} meta Metadata configuration options
2023  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2024  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2025  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2026  */
2027 Roo.data.XmlReader = function(meta, recordType){
2028     meta = meta || {};
2029     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2030 };
2031 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2032     /**
2033      * This method is only used by a DataProxy which has retrieved data from a remote server.
2034          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2035          * to contain a method called 'responseXML' that returns an XML document object.
2036      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2037      * a cache of Roo.data.Records.
2038      */
2039     read : function(response){
2040         var doc = response.responseXML;
2041         if(!doc) {
2042             throw {message: "XmlReader.read: XML Document not available"};
2043         }
2044         return this.readRecords(doc);
2045     },
2046
2047     /**
2048      * Create a data block containing Roo.data.Records from an XML document.
2049          * @param {Object} doc A parsed XML document.
2050      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2051      * a cache of Roo.data.Records.
2052      */
2053     readRecords : function(doc){
2054         /**
2055          * After any data loads/reads, the raw XML Document is available for further custom processing.
2056          * @type XMLDocument
2057          */
2058         this.xmlData = doc;
2059         var root = doc.documentElement || doc;
2060         var q = Roo.DomQuery;
2061         var recordType = this.recordType, fields = recordType.prototype.fields;
2062         var sid = this.meta.id;
2063         var totalRecords = 0, success = true;
2064         if(this.meta.totalRecords){
2065             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2066         }
2067         
2068         if(this.meta.success){
2069             var sv = q.selectValue(this.meta.success, root, true);
2070             success = sv !== false && sv !== 'false';
2071         }
2072         var records = [];
2073         var ns = q.select(this.meta.record, root);
2074         for(var i = 0, len = ns.length; i < len; i++) {
2075                 var n = ns[i];
2076                 var values = {};
2077                 var id = sid ? q.selectValue(sid, n) : undefined;
2078                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2079                     var f = fields.items[j];
2080                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2081                     v = f.convert(v);
2082                     values[f.name] = v;
2083                 }
2084                 var record = new recordType(values, id);
2085                 record.node = n;
2086                 records[records.length] = record;
2087             }
2088
2089             return {
2090                 success : success,
2091                 records : records,
2092                 totalRecords : totalRecords || records.length
2093             };
2094     }
2095 });/*
2096  * Based on:
2097  * Ext JS Library 1.1.1
2098  * Copyright(c) 2006-2007, Ext JS, LLC.
2099  *
2100  * Originally Released Under LGPL - original licence link has changed is not relivant.
2101  *
2102  * Fork - LGPL
2103  * <script type="text/javascript">
2104  */
2105
2106 /**
2107  * @class Roo.data.ArrayReader
2108  * @extends Roo.data.DataReader
2109  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2110  * Each element of that Array represents a row of data fields. The
2111  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2112  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2113  * <p>
2114  * Example code:.
2115  * <pre><code>
2116 var RecordDef = Roo.data.Record.create([
2117     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2118     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2119 ]);
2120 var myReader = new Roo.data.ArrayReader({
2121     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2122 }, RecordDef);
2123 </code></pre>
2124  * <p>
2125  * This would consume an Array like this:
2126  * <pre><code>
2127 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2128   </code></pre>
2129  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
2130  * @constructor
2131  * Create a new JsonReader
2132  * @param {Object} meta Metadata configuration options.
2133  * @param {Object} recordType Either an Array of field definition objects
2134  * as specified to {@link Roo.data.Record#create},
2135  * or an {@link Roo.data.Record} object
2136  * created using {@link Roo.data.Record#create}.
2137  */
2138 Roo.data.ArrayReader = function(meta, recordType){
2139     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
2140 };
2141
2142 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2143     /**
2144      * Create a data block containing Roo.data.Records from an XML document.
2145      * @param {Object} o An Array of row objects which represents the dataset.
2146      * @return {Object} data A data block which is used by an Roo.data.Store object as
2147      * a cache of Roo.data.Records.
2148      */
2149     readRecords : function(o){
2150         var sid = this.meta ? this.meta.id : null;
2151         var recordType = this.recordType, fields = recordType.prototype.fields;
2152         var records = [];
2153         var root = o;
2154             for(var i = 0; i < root.length; i++){
2155                     var n = root[i];
2156                 var values = {};
2157                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2158                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2159                 var f = fields.items[j];
2160                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2161                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2162                 v = f.convert(v);
2163                 values[f.name] = v;
2164             }
2165                 var record = new recordType(values, id);
2166                 record.json = n;
2167                 records[records.length] = record;
2168             }
2169             return {
2170                 records : records,
2171                 totalRecords : records.length
2172             };
2173     }
2174 });/*
2175  * Based on:
2176  * Ext JS Library 1.1.1
2177  * Copyright(c) 2006-2007, Ext JS, LLC.
2178  *
2179  * Originally Released Under LGPL - original licence link has changed is not relivant.
2180  *
2181  * Fork - LGPL
2182  * <script type="text/javascript">
2183  */
2184
2185
2186 /**
2187  * @class Roo.data.Tree
2188  * @extends Roo.util.Observable
2189  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2190  * in the tree have most standard DOM functionality.
2191  * @constructor
2192  * @param {Node} root (optional) The root node
2193  */
2194 Roo.data.Tree = function(root){
2195    this.nodeHash = {};
2196    /**
2197     * The root node for this tree
2198     * @type Node
2199     */
2200    this.root = null;
2201    if(root){
2202        this.setRootNode(root);
2203    }
2204    this.addEvents({
2205        /**
2206         * @event append
2207         * Fires when a new child node is appended to a node in this tree.
2208         * @param {Tree} tree The owner tree
2209         * @param {Node} parent The parent node
2210         * @param {Node} node The newly appended node
2211         * @param {Number} index The index of the newly appended node
2212         */
2213        "append" : true,
2214        /**
2215         * @event remove
2216         * Fires when a child node is removed from a node in this tree.
2217         * @param {Tree} tree The owner tree
2218         * @param {Node} parent The parent node
2219         * @param {Node} node The child node removed
2220         */
2221        "remove" : true,
2222        /**
2223         * @event move
2224         * Fires when a node is moved to a new location in the tree
2225         * @param {Tree} tree The owner tree
2226         * @param {Node} node The node moved
2227         * @param {Node} oldParent The old parent of this node
2228         * @param {Node} newParent The new parent of this node
2229         * @param {Number} index The index it was moved to
2230         */
2231        "move" : true,
2232        /**
2233         * @event insert
2234         * Fires when a new child node is inserted in a node in this tree.
2235         * @param {Tree} tree The owner tree
2236         * @param {Node} parent The parent node
2237         * @param {Node} node The child node inserted
2238         * @param {Node} refNode The child node the node was inserted before
2239         */
2240        "insert" : true,
2241        /**
2242         * @event beforeappend
2243         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2244         * @param {Tree} tree The owner tree
2245         * @param {Node} parent The parent node
2246         * @param {Node} node The child node to be appended
2247         */
2248        "beforeappend" : true,
2249        /**
2250         * @event beforeremove
2251         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2252         * @param {Tree} tree The owner tree
2253         * @param {Node} parent The parent node
2254         * @param {Node} node The child node to be removed
2255         */
2256        "beforeremove" : true,
2257        /**
2258         * @event beforemove
2259         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2260         * @param {Tree} tree The owner tree
2261         * @param {Node} node The node being moved
2262         * @param {Node} oldParent The parent of the node
2263         * @param {Node} newParent The new parent the node is moving to
2264         * @param {Number} index The index it is being moved to
2265         */
2266        "beforemove" : true,
2267        /**
2268         * @event beforeinsert
2269         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2270         * @param {Tree} tree The owner tree
2271         * @param {Node} parent The parent node
2272         * @param {Node} node The child node to be inserted
2273         * @param {Node} refNode The child node the node is being inserted before
2274         */
2275        "beforeinsert" : true
2276    });
2277
2278     Roo.data.Tree.superclass.constructor.call(this);
2279 };
2280
2281 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2282     pathSeparator: "/",
2283
2284     proxyNodeEvent : function(){
2285         return this.fireEvent.apply(this, arguments);
2286     },
2287
2288     /**
2289      * Returns the root node for this tree.
2290      * @return {Node}
2291      */
2292     getRootNode : function(){
2293         return this.root;
2294     },
2295
2296     /**
2297      * Sets the root node for this tree.
2298      * @param {Node} node
2299      * @return {Node}
2300      */
2301     setRootNode : function(node){
2302         this.root = node;
2303         node.ownerTree = this;
2304         node.isRoot = true;
2305         this.registerNode(node);
2306         return node;
2307     },
2308
2309     /**
2310      * Gets a node in this tree by its id.
2311      * @param {String} id
2312      * @return {Node}
2313      */
2314     getNodeById : function(id){
2315         return this.nodeHash[id];
2316     },
2317
2318     registerNode : function(node){
2319         this.nodeHash[node.id] = node;
2320     },
2321
2322     unregisterNode : function(node){
2323         delete this.nodeHash[node.id];
2324     },
2325
2326     toString : function(){
2327         return "[Tree"+(this.id?" "+this.id:"")+"]";
2328     }
2329 });
2330
2331 /**
2332  * @class Roo.data.Node
2333  * @extends Roo.util.Observable
2334  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2335  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2336  * @constructor
2337  * @param {Object} attributes The attributes/config for the node
2338  */
2339 Roo.data.Node = function(attributes){
2340     /**
2341      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2342      * @type {Object}
2343      */
2344     this.attributes = attributes || {};
2345     this.leaf = this.attributes.leaf;
2346     /**
2347      * The node id. @type String
2348      */
2349     this.id = this.attributes.id;
2350     if(!this.id){
2351         this.id = Roo.id(null, "ynode-");
2352         this.attributes.id = this.id;
2353     }
2354      
2355     
2356     /**
2357      * All child nodes of this node. @type Array
2358      */
2359     this.childNodes = [];
2360     if(!this.childNodes.indexOf){ // indexOf is a must
2361         this.childNodes.indexOf = function(o){
2362             for(var i = 0, len = this.length; i < len; i++){
2363                 if(this[i] == o) {
2364                     return i;
2365                 }
2366             }
2367             return -1;
2368         };
2369     }
2370     /**
2371      * The parent node for this node. @type Node
2372      */
2373     this.parentNode = null;
2374     /**
2375      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2376      */
2377     this.firstChild = null;
2378     /**
2379      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2380      */
2381     this.lastChild = null;
2382     /**
2383      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2384      */
2385     this.previousSibling = null;
2386     /**
2387      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2388      */
2389     this.nextSibling = null;
2390
2391     this.addEvents({
2392        /**
2393         * @event append
2394         * Fires when a new child node is appended
2395         * @param {Tree} tree The owner tree
2396         * @param {Node} this This node
2397         * @param {Node} node The newly appended node
2398         * @param {Number} index The index of the newly appended node
2399         */
2400        "append" : true,
2401        /**
2402         * @event remove
2403         * Fires when a child node is removed
2404         * @param {Tree} tree The owner tree
2405         * @param {Node} this This node
2406         * @param {Node} node The removed node
2407         */
2408        "remove" : true,
2409        /**
2410         * @event move
2411         * Fires when this node is moved to a new location in the tree
2412         * @param {Tree} tree The owner tree
2413         * @param {Node} this This node
2414         * @param {Node} oldParent The old parent of this node
2415         * @param {Node} newParent The new parent of this node
2416         * @param {Number} index The index it was moved to
2417         */
2418        "move" : true,
2419        /**
2420         * @event insert
2421         * Fires when a new child node is inserted.
2422         * @param {Tree} tree The owner tree
2423         * @param {Node} this This node
2424         * @param {Node} node The child node inserted
2425         * @param {Node} refNode The child node the node was inserted before
2426         */
2427        "insert" : true,
2428        /**
2429         * @event beforeappend
2430         * Fires before a new child is appended, return false to cancel the append.
2431         * @param {Tree} tree The owner tree
2432         * @param {Node} this This node
2433         * @param {Node} node The child node to be appended
2434         */
2435        "beforeappend" : true,
2436        /**
2437         * @event beforeremove
2438         * Fires before a child is removed, return false to cancel the remove.
2439         * @param {Tree} tree The owner tree
2440         * @param {Node} this This node
2441         * @param {Node} node The child node to be removed
2442         */
2443        "beforeremove" : true,
2444        /**
2445         * @event beforemove
2446         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2447         * @param {Tree} tree The owner tree
2448         * @param {Node} this This node
2449         * @param {Node} oldParent The parent of this node
2450         * @param {Node} newParent The new parent this node is moving to
2451         * @param {Number} index The index it is being moved to
2452         */
2453        "beforemove" : true,
2454        /**
2455         * @event beforeinsert
2456         * Fires before a new child is inserted, return false to cancel the insert.
2457         * @param {Tree} tree The owner tree
2458         * @param {Node} this This node
2459         * @param {Node} node The child node to be inserted
2460         * @param {Node} refNode The child node the node is being inserted before
2461         */
2462        "beforeinsert" : true
2463    });
2464     this.listeners = this.attributes.listeners;
2465     Roo.data.Node.superclass.constructor.call(this);
2466 };
2467
2468 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2469     fireEvent : function(evtName){
2470         // first do standard event for this node
2471         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2472             return false;
2473         }
2474         // then bubble it up to the tree if the event wasn't cancelled
2475         var ot = this.getOwnerTree();
2476         if(ot){
2477             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2478                 return false;
2479             }
2480         }
2481         return true;
2482     },
2483
2484     /**
2485      * Returns true if this node is a leaf
2486      * @return {Boolean}
2487      */
2488     isLeaf : function(){
2489         return this.leaf === true;
2490     },
2491
2492     // private
2493     setFirstChild : function(node){
2494         this.firstChild = node;
2495     },
2496
2497     //private
2498     setLastChild : function(node){
2499         this.lastChild = node;
2500     },
2501
2502
2503     /**
2504      * Returns true if this node is the last child of its parent
2505      * @return {Boolean}
2506      */
2507     isLast : function(){
2508        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2509     },
2510
2511     /**
2512      * Returns true if this node is the first child of its parent
2513      * @return {Boolean}
2514      */
2515     isFirst : function(){
2516        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2517     },
2518
2519     hasChildNodes : function(){
2520         return !this.isLeaf() && this.childNodes.length > 0;
2521     },
2522
2523     /**
2524      * Insert node(s) as the last child node of this node.
2525      * @param {Node/Array} node The node or Array of nodes to append
2526      * @return {Node} The appended node if single append, or null if an array was passed
2527      */
2528     appendChild : function(node){
2529         var multi = false;
2530         if(node instanceof Array){
2531             multi = node;
2532         }else if(arguments.length > 1){
2533             multi = arguments;
2534         }
2535         // if passed an array or multiple args do them one by one
2536         if(multi){
2537             for(var i = 0, len = multi.length; i < len; i++) {
2538                 this.appendChild(multi[i]);
2539             }
2540         }else{
2541             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2542                 return false;
2543             }
2544             var index = this.childNodes.length;
2545             var oldParent = node.parentNode;
2546             // it's a move, make sure we move it cleanly
2547             if(oldParent){
2548                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2549                     return false;
2550                 }
2551                 oldParent.removeChild(node);
2552             }
2553             index = this.childNodes.length;
2554             if(index == 0){
2555                 this.setFirstChild(node);
2556             }
2557             this.childNodes.push(node);
2558             node.parentNode = this;
2559             var ps = this.childNodes[index-1];
2560             if(ps){
2561                 node.previousSibling = ps;
2562                 ps.nextSibling = node;
2563             }else{
2564                 node.previousSibling = null;
2565             }
2566             node.nextSibling = null;
2567             this.setLastChild(node);
2568             node.setOwnerTree(this.getOwnerTree());
2569             this.fireEvent("append", this.ownerTree, this, node, index);
2570             if(oldParent){
2571                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2572             }
2573             return node;
2574         }
2575     },
2576
2577     /**
2578      * Removes a child node from this node.
2579      * @param {Node} node The node to remove
2580      * @return {Node} The removed node
2581      */
2582     removeChild : function(node){
2583         var index = this.childNodes.indexOf(node);
2584         if(index == -1){
2585             return false;
2586         }
2587         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2588             return false;
2589         }
2590
2591         // remove it from childNodes collection
2592         this.childNodes.splice(index, 1);
2593
2594         // update siblings
2595         if(node.previousSibling){
2596             node.previousSibling.nextSibling = node.nextSibling;
2597         }
2598         if(node.nextSibling){
2599             node.nextSibling.previousSibling = node.previousSibling;
2600         }
2601
2602         // update child refs
2603         if(this.firstChild == node){
2604             this.setFirstChild(node.nextSibling);
2605         }
2606         if(this.lastChild == node){
2607             this.setLastChild(node.previousSibling);
2608         }
2609
2610         node.setOwnerTree(null);
2611         // clear any references from the node
2612         node.parentNode = null;
2613         node.previousSibling = null;
2614         node.nextSibling = null;
2615         this.fireEvent("remove", this.ownerTree, this, node);
2616         return node;
2617     },
2618
2619     /**
2620      * Inserts the first node before the second node in this nodes childNodes collection.
2621      * @param {Node} node The node to insert
2622      * @param {Node} refNode The node to insert before (if null the node is appended)
2623      * @return {Node} The inserted node
2624      */
2625     insertBefore : function(node, refNode){
2626         if(!refNode){ // like standard Dom, refNode can be null for append
2627             return this.appendChild(node);
2628         }
2629         // nothing to do
2630         if(node == refNode){
2631             return false;
2632         }
2633
2634         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2635             return false;
2636         }
2637         var index = this.childNodes.indexOf(refNode);
2638         var oldParent = node.parentNode;
2639         var refIndex = index;
2640
2641         // when moving internally, indexes will change after remove
2642         if(oldParent == this && this.childNodes.indexOf(node) < index){
2643             refIndex--;
2644         }
2645
2646         // it's a move, make sure we move it cleanly
2647         if(oldParent){
2648             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2649                 return false;
2650             }
2651             oldParent.removeChild(node);
2652         }
2653         if(refIndex == 0){
2654             this.setFirstChild(node);
2655         }
2656         this.childNodes.splice(refIndex, 0, node);
2657         node.parentNode = this;
2658         var ps = this.childNodes[refIndex-1];
2659         if(ps){
2660             node.previousSibling = ps;
2661             ps.nextSibling = node;
2662         }else{
2663             node.previousSibling = null;
2664         }
2665         node.nextSibling = refNode;
2666         refNode.previousSibling = node;
2667         node.setOwnerTree(this.getOwnerTree());
2668         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2669         if(oldParent){
2670             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2671         }
2672         return node;
2673     },
2674
2675     /**
2676      * Returns the child node at the specified index.
2677      * @param {Number} index
2678      * @return {Node}
2679      */
2680     item : function(index){
2681         return this.childNodes[index];
2682     },
2683
2684     /**
2685      * Replaces one child node in this node with another.
2686      * @param {Node} newChild The replacement node
2687      * @param {Node} oldChild The node to replace
2688      * @return {Node} The replaced node
2689      */
2690     replaceChild : function(newChild, oldChild){
2691         this.insertBefore(newChild, oldChild);
2692         this.removeChild(oldChild);
2693         return oldChild;
2694     },
2695
2696     /**
2697      * Returns the index of a child node
2698      * @param {Node} node
2699      * @return {Number} The index of the node or -1 if it was not found
2700      */
2701     indexOf : function(child){
2702         return this.childNodes.indexOf(child);
2703     },
2704
2705     /**
2706      * Returns the tree this node is in.
2707      * @return {Tree}
2708      */
2709     getOwnerTree : function(){
2710         // if it doesn't have one, look for one
2711         if(!this.ownerTree){
2712             var p = this;
2713             while(p){
2714                 if(p.ownerTree){
2715                     this.ownerTree = p.ownerTree;
2716                     break;
2717                 }
2718                 p = p.parentNode;
2719             }
2720         }
2721         return this.ownerTree;
2722     },
2723
2724     /**
2725      * Returns depth of this node (the root node has a depth of 0)
2726      * @return {Number}
2727      */
2728     getDepth : function(){
2729         var depth = 0;
2730         var p = this;
2731         while(p.parentNode){
2732             ++depth;
2733             p = p.parentNode;
2734         }
2735         return depth;
2736     },
2737
2738     // private
2739     setOwnerTree : function(tree){
2740         // if it's move, we need to update everyone
2741         if(tree != this.ownerTree){
2742             if(this.ownerTree){
2743                 this.ownerTree.unregisterNode(this);
2744             }
2745             this.ownerTree = tree;
2746             var cs = this.childNodes;
2747             for(var i = 0, len = cs.length; i < len; i++) {
2748                 cs[i].setOwnerTree(tree);
2749             }
2750             if(tree){
2751                 tree.registerNode(this);
2752             }
2753         }
2754     },
2755
2756     /**
2757      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2758      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2759      * @return {String} The path
2760      */
2761     getPath : function(attr){
2762         attr = attr || "id";
2763         var p = this.parentNode;
2764         var b = [this.attributes[attr]];
2765         while(p){
2766             b.unshift(p.attributes[attr]);
2767             p = p.parentNode;
2768         }
2769         var sep = this.getOwnerTree().pathSeparator;
2770         return sep + b.join(sep);
2771     },
2772
2773     /**
2774      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2775      * function call will be the scope provided or the current node. The arguments to the function
2776      * will be the args provided or the current node. If the function returns false at any point,
2777      * the bubble is stopped.
2778      * @param {Function} fn The function to call
2779      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2780      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2781      */
2782     bubble : function(fn, scope, args){
2783         var p = this;
2784         while(p){
2785             if(fn.call(scope || p, args || p) === false){
2786                 break;
2787             }
2788             p = p.parentNode;
2789         }
2790     },
2791
2792     /**
2793      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2794      * function call will be the scope provided or the current node. The arguments to the function
2795      * will be the args provided or the current node. If the function returns false at any point,
2796      * the cascade is stopped on that branch.
2797      * @param {Function} fn The function to call
2798      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2799      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2800      */
2801     cascade : function(fn, scope, args){
2802         if(fn.call(scope || this, args || this) !== false){
2803             var cs = this.childNodes;
2804             for(var i = 0, len = cs.length; i < len; i++) {
2805                 cs[i].cascade(fn, scope, args);
2806             }
2807         }
2808     },
2809
2810     /**
2811      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2812      * function call will be the scope provided or the current node. The arguments to the function
2813      * will be the args provided or the current node. If the function returns false at any point,
2814      * the iteration stops.
2815      * @param {Function} fn The function to call
2816      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2817      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2818      */
2819     eachChild : function(fn, scope, args){
2820         var cs = this.childNodes;
2821         for(var i = 0, len = cs.length; i < len; i++) {
2822                 if(fn.call(scope || this, args || cs[i]) === false){
2823                     break;
2824                 }
2825         }
2826     },
2827
2828     /**
2829      * Finds the first child that has the attribute with the specified value.
2830      * @param {String} attribute The attribute name
2831      * @param {Mixed} value The value to search for
2832      * @return {Node} The found child or null if none was found
2833      */
2834     findChild : function(attribute, value){
2835         var cs = this.childNodes;
2836         for(var i = 0, len = cs.length; i < len; i++) {
2837                 if(cs[i].attributes[attribute] == value){
2838                     return cs[i];
2839                 }
2840         }
2841         return null;
2842     },
2843
2844     /**
2845      * Finds the first child by a custom function. The child matches if the function passed
2846      * returns true.
2847      * @param {Function} fn
2848      * @param {Object} scope (optional)
2849      * @return {Node} The found child or null if none was found
2850      */
2851     findChildBy : function(fn, scope){
2852         var cs = this.childNodes;
2853         for(var i = 0, len = cs.length; i < len; i++) {
2854                 if(fn.call(scope||cs[i], cs[i]) === true){
2855                     return cs[i];
2856                 }
2857         }
2858         return null;
2859     },
2860
2861     /**
2862      * Sorts this nodes children using the supplied sort function
2863      * @param {Function} fn
2864      * @param {Object} scope (optional)
2865      */
2866     sort : function(fn, scope){
2867         var cs = this.childNodes;
2868         var len = cs.length;
2869         if(len > 0){
2870             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2871             cs.sort(sortFn);
2872             for(var i = 0; i < len; i++){
2873                 var n = cs[i];
2874                 n.previousSibling = cs[i-1];
2875                 n.nextSibling = cs[i+1];
2876                 if(i == 0){
2877                     this.setFirstChild(n);
2878                 }
2879                 if(i == len-1){
2880                     this.setLastChild(n);
2881                 }
2882             }
2883         }
2884     },
2885
2886     /**
2887      * Returns true if this node is an ancestor (at any point) of the passed node.
2888      * @param {Node} node
2889      * @return {Boolean}
2890      */
2891     contains : function(node){
2892         return node.isAncestor(this);
2893     },
2894
2895     /**
2896      * Returns true if the passed node is an ancestor (at any point) of this node.
2897      * @param {Node} node
2898      * @return {Boolean}
2899      */
2900     isAncestor : function(node){
2901         var p = this.parentNode;
2902         while(p){
2903             if(p == node){
2904                 return true;
2905             }
2906             p = p.parentNode;
2907         }
2908         return false;
2909     },
2910
2911     toString : function(){
2912         return "[Node"+(this.id?" "+this.id:"")+"]";
2913     }
2914 });/*
2915  * Based on:
2916  * Ext JS Library 1.1.1
2917  * Copyright(c) 2006-2007, Ext JS, LLC.
2918  *
2919  * Originally Released Under LGPL - original licence link has changed is not relivant.
2920  *
2921  * Fork - LGPL
2922  * <script type="text/javascript">
2923  */
2924  (function(){ 
2925 /**
2926  * @class Roo.Layer
2927  * @extends Roo.Element
2928  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2929  * automatic maintaining of shadow/shim positions.
2930  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2931  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2932  * you can pass a string with a CSS class name. False turns off the shadow.
2933  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2934  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2935  * @cfg {String} cls CSS class to add to the element
2936  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2937  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2938  * @constructor
2939  * @param {Object} config An object with config options.
2940  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2941  */
2942
2943 Roo.Layer = function(config, existingEl){
2944     config = config || {};
2945     var dh = Roo.DomHelper;
2946     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2947     if(existingEl){
2948         this.dom = Roo.getDom(existingEl);
2949     }
2950     if(!this.dom){
2951         var o = config.dh || {tag: "div", cls: "x-layer"};
2952         this.dom = dh.append(pel, o);
2953     }
2954     if(config.cls){
2955         this.addClass(config.cls);
2956     }
2957     this.constrain = config.constrain !== false;
2958     this.visibilityMode = Roo.Element.VISIBILITY;
2959     if(config.id){
2960         this.id = this.dom.id = config.id;
2961     }else{
2962         this.id = Roo.id(this.dom);
2963     }
2964     this.zindex = config.zindex || this.getZIndex();
2965     this.position("absolute", this.zindex);
2966     if(config.shadow){
2967         this.shadowOffset = config.shadowOffset || 4;
2968         this.shadow = new Roo.Shadow({
2969             offset : this.shadowOffset,
2970             mode : config.shadow
2971         });
2972     }else{
2973         this.shadowOffset = 0;
2974     }
2975     this.useShim = config.shim !== false && Roo.useShims;
2976     this.useDisplay = config.useDisplay;
2977     this.hide();
2978 };
2979
2980 var supr = Roo.Element.prototype;
2981
2982 // shims are shared among layer to keep from having 100 iframes
2983 var shims = [];
2984
2985 Roo.extend(Roo.Layer, Roo.Element, {
2986
2987     getZIndex : function(){
2988         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
2989     },
2990
2991     getShim : function(){
2992         if(!this.useShim){
2993             return null;
2994         }
2995         if(this.shim){
2996             return this.shim;
2997         }
2998         var shim = shims.shift();
2999         if(!shim){
3000             shim = this.createShim();
3001             shim.enableDisplayMode('block');
3002             shim.dom.style.display = 'none';
3003             shim.dom.style.visibility = 'visible';
3004         }
3005         var pn = this.dom.parentNode;
3006         if(shim.dom.parentNode != pn){
3007             pn.insertBefore(shim.dom, this.dom);
3008         }
3009         shim.setStyle('z-index', this.getZIndex()-2);
3010         this.shim = shim;
3011         return shim;
3012     },
3013
3014     hideShim : function(){
3015         if(this.shim){
3016             this.shim.setDisplayed(false);
3017             shims.push(this.shim);
3018             delete this.shim;
3019         }
3020     },
3021
3022     disableShadow : function(){
3023         if(this.shadow){
3024             this.shadowDisabled = true;
3025             this.shadow.hide();
3026             this.lastShadowOffset = this.shadowOffset;
3027             this.shadowOffset = 0;
3028         }
3029     },
3030
3031     enableShadow : function(show){
3032         if(this.shadow){
3033             this.shadowDisabled = false;
3034             this.shadowOffset = this.lastShadowOffset;
3035             delete this.lastShadowOffset;
3036             if(show){
3037                 this.sync(true);
3038             }
3039         }
3040     },
3041
3042     // private
3043     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3044     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3045     sync : function(doShow){
3046         var sw = this.shadow;
3047         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3048             var sh = this.getShim();
3049
3050             var w = this.getWidth(),
3051                 h = this.getHeight();
3052
3053             var l = this.getLeft(true),
3054                 t = this.getTop(true);
3055
3056             if(sw && !this.shadowDisabled){
3057                 if(doShow && !sw.isVisible()){
3058                     sw.show(this);
3059                 }else{
3060                     sw.realign(l, t, w, h);
3061                 }
3062                 if(sh){
3063                     if(doShow){
3064                        sh.show();
3065                     }
3066                     // fit the shim behind the shadow, so it is shimmed too
3067                     var a = sw.adjusts, s = sh.dom.style;
3068                     s.left = (Math.min(l, l+a.l))+"px";
3069                     s.top = (Math.min(t, t+a.t))+"px";
3070                     s.width = (w+a.w)+"px";
3071                     s.height = (h+a.h)+"px";
3072                 }
3073             }else if(sh){
3074                 if(doShow){
3075                    sh.show();
3076                 }
3077                 sh.setSize(w, h);
3078                 sh.setLeftTop(l, t);
3079             }
3080             
3081         }
3082     },
3083
3084     // private
3085     destroy : function(){
3086         this.hideShim();
3087         if(this.shadow){
3088             this.shadow.hide();
3089         }
3090         this.removeAllListeners();
3091         var pn = this.dom.parentNode;
3092         if(pn){
3093             pn.removeChild(this.dom);
3094         }
3095         Roo.Element.uncache(this.id);
3096     },
3097
3098     remove : function(){
3099         this.destroy();
3100     },
3101
3102     // private
3103     beginUpdate : function(){
3104         this.updating = true;
3105     },
3106
3107     // private
3108     endUpdate : function(){
3109         this.updating = false;
3110         this.sync(true);
3111     },
3112
3113     // private
3114     hideUnders : function(negOffset){
3115         if(this.shadow){
3116             this.shadow.hide();
3117         }
3118         this.hideShim();
3119     },
3120
3121     // private
3122     constrainXY : function(){
3123         if(this.constrain){
3124             var vw = Roo.lib.Dom.getViewWidth(),
3125                 vh = Roo.lib.Dom.getViewHeight();
3126             var s = Roo.get(document).getScroll();
3127
3128             var xy = this.getXY();
3129             var x = xy[0], y = xy[1];   
3130             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3131             // only move it if it needs it
3132             var moved = false;
3133             // first validate right/bottom
3134             if((x + w) > vw+s.left){
3135                 x = vw - w - this.shadowOffset;
3136                 moved = true;
3137             }
3138             if((y + h) > vh+s.top){
3139                 y = vh - h - this.shadowOffset;
3140                 moved = true;
3141             }
3142             // then make sure top/left isn't negative
3143             if(x < s.left){
3144                 x = s.left;
3145                 moved = true;
3146             }
3147             if(y < s.top){
3148                 y = s.top;
3149                 moved = true;
3150             }
3151             if(moved){
3152                 if(this.avoidY){
3153                     var ay = this.avoidY;
3154                     if(y <= ay && (y+h) >= ay){
3155                         y = ay-h-5;   
3156                     }
3157                 }
3158                 xy = [x, y];
3159                 this.storeXY(xy);
3160                 supr.setXY.call(this, xy);
3161                 this.sync();
3162             }
3163         }
3164     },
3165
3166     isVisible : function(){
3167         return this.visible;    
3168     },
3169
3170     // private
3171     showAction : function(){
3172         this.visible = true; // track visibility to prevent getStyle calls
3173         if(this.useDisplay === true){
3174             this.setDisplayed("");
3175         }else if(this.lastXY){
3176             supr.setXY.call(this, this.lastXY);
3177         }else if(this.lastLT){
3178             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3179         }
3180     },
3181
3182     // private
3183     hideAction : function(){
3184         this.visible = false;
3185         if(this.useDisplay === true){
3186             this.setDisplayed(false);
3187         }else{
3188             this.setLeftTop(-10000,-10000);
3189         }
3190     },
3191
3192     // overridden Element method
3193     setVisible : function(v, a, d, c, e){
3194         if(v){
3195             this.showAction();
3196         }
3197         if(a && v){
3198             var cb = function(){
3199                 this.sync(true);
3200                 if(c){
3201                     c();
3202                 }
3203             }.createDelegate(this);
3204             supr.setVisible.call(this, true, true, d, cb, e);
3205         }else{
3206             if(!v){
3207                 this.hideUnders(true);
3208             }
3209             var cb = c;
3210             if(a){
3211                 cb = function(){
3212                     this.hideAction();
3213                     if(c){
3214                         c();
3215                     }
3216                 }.createDelegate(this);
3217             }
3218             supr.setVisible.call(this, v, a, d, cb, e);
3219             if(v){
3220                 this.sync(true);
3221             }else if(!a){
3222                 this.hideAction();
3223             }
3224         }
3225     },
3226
3227     storeXY : function(xy){
3228         delete this.lastLT;
3229         this.lastXY = xy;
3230     },
3231
3232     storeLeftTop : function(left, top){
3233         delete this.lastXY;
3234         this.lastLT = [left, top];
3235     },
3236
3237     // private
3238     beforeFx : function(){
3239         this.beforeAction();
3240         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3241     },
3242
3243     // private
3244     afterFx : function(){
3245         Roo.Layer.superclass.afterFx.apply(this, arguments);
3246         this.sync(this.isVisible());
3247     },
3248
3249     // private
3250     beforeAction : function(){
3251         if(!this.updating && this.shadow){
3252             this.shadow.hide();
3253         }
3254     },
3255
3256     // overridden Element method
3257     setLeft : function(left){
3258         this.storeLeftTop(left, this.getTop(true));
3259         supr.setLeft.apply(this, arguments);
3260         this.sync();
3261     },
3262
3263     setTop : function(top){
3264         this.storeLeftTop(this.getLeft(true), top);
3265         supr.setTop.apply(this, arguments);
3266         this.sync();
3267     },
3268
3269     setLeftTop : function(left, top){
3270         this.storeLeftTop(left, top);
3271         supr.setLeftTop.apply(this, arguments);
3272         this.sync();
3273     },
3274
3275     setXY : function(xy, a, d, c, e){
3276         this.fixDisplay();
3277         this.beforeAction();
3278         this.storeXY(xy);
3279         var cb = this.createCB(c);
3280         supr.setXY.call(this, xy, a, d, cb, e);
3281         if(!a){
3282             cb();
3283         }
3284     },
3285
3286     // private
3287     createCB : function(c){
3288         var el = this;
3289         return function(){
3290             el.constrainXY();
3291             el.sync(true);
3292             if(c){
3293                 c();
3294             }
3295         };
3296     },
3297
3298     // overridden Element method
3299     setX : function(x, a, d, c, e){
3300         this.setXY([x, this.getY()], a, d, c, e);
3301     },
3302
3303     // overridden Element method
3304     setY : function(y, a, d, c, e){
3305         this.setXY([this.getX(), y], a, d, c, e);
3306     },
3307
3308     // overridden Element method
3309     setSize : function(w, h, a, d, c, e){
3310         this.beforeAction();
3311         var cb = this.createCB(c);
3312         supr.setSize.call(this, w, h, a, d, cb, e);
3313         if(!a){
3314             cb();
3315         }
3316     },
3317
3318     // overridden Element method
3319     setWidth : function(w, a, d, c, e){
3320         this.beforeAction();
3321         var cb = this.createCB(c);
3322         supr.setWidth.call(this, w, a, d, cb, e);
3323         if(!a){
3324             cb();
3325         }
3326     },
3327
3328     // overridden Element method
3329     setHeight : function(h, a, d, c, e){
3330         this.beforeAction();
3331         var cb = this.createCB(c);
3332         supr.setHeight.call(this, h, a, d, cb, e);
3333         if(!a){
3334             cb();
3335         }
3336     },
3337
3338     // overridden Element method
3339     setBounds : function(x, y, w, h, a, d, c, e){
3340         this.beforeAction();
3341         var cb = this.createCB(c);
3342         if(!a){
3343             this.storeXY([x, y]);
3344             supr.setXY.call(this, [x, y]);
3345             supr.setSize.call(this, w, h, a, d, cb, e);
3346             cb();
3347         }else{
3348             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3349         }
3350         return this;
3351     },
3352     
3353     /**
3354      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3355      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3356      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3357      * @param {Number} zindex The new z-index to set
3358      * @return {this} The Layer
3359      */
3360     setZIndex : function(zindex){
3361         this.zindex = zindex;
3362         this.setStyle("z-index", zindex + 2);
3363         if(this.shadow){
3364             this.shadow.setZIndex(zindex + 1);
3365         }
3366         if(this.shim){
3367             this.shim.setStyle("z-index", zindex);
3368         }
3369     }
3370 });
3371 })();/*
3372  * Based on:
3373  * Ext JS Library 1.1.1
3374  * Copyright(c) 2006-2007, Ext JS, LLC.
3375  *
3376  * Originally Released Under LGPL - original licence link has changed is not relivant.
3377  *
3378  * Fork - LGPL
3379  * <script type="text/javascript">
3380  */
3381
3382
3383 /**
3384  * @class Roo.Shadow
3385  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3386  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3387  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3388  * @constructor
3389  * Create a new Shadow
3390  * @param {Object} config The config object
3391  */
3392 Roo.Shadow = function(config){
3393     Roo.apply(this, config);
3394     if(typeof this.mode != "string"){
3395         this.mode = this.defaultMode;
3396     }
3397     var o = this.offset, a = {h: 0};
3398     var rad = Math.floor(this.offset/2);
3399     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3400         case "drop":
3401             a.w = 0;
3402             a.l = a.t = o;
3403             a.t -= 1;
3404             if(Roo.isIE){
3405                 a.l -= this.offset + rad;
3406                 a.t -= this.offset + rad;
3407                 a.w -= rad;
3408                 a.h -= rad;
3409                 a.t += 1;
3410             }
3411         break;
3412         case "sides":
3413             a.w = (o*2);
3414             a.l = -o;
3415             a.t = o-1;
3416             if(Roo.isIE){
3417                 a.l -= (this.offset - rad);
3418                 a.t -= this.offset + rad;
3419                 a.l += 1;
3420                 a.w -= (this.offset - rad)*2;
3421                 a.w -= rad + 1;
3422                 a.h -= 1;
3423             }
3424         break;
3425         case "frame":
3426             a.w = a.h = (o*2);
3427             a.l = a.t = -o;
3428             a.t += 1;
3429             a.h -= 2;
3430             if(Roo.isIE){
3431                 a.l -= (this.offset - rad);
3432                 a.t -= (this.offset - rad);
3433                 a.l += 1;
3434                 a.w -= (this.offset + rad + 1);
3435                 a.h -= (this.offset + rad);
3436                 a.h += 1;
3437             }
3438         break;
3439     };
3440
3441     this.adjusts = a;
3442 };
3443
3444 Roo.Shadow.prototype = {
3445     /**
3446      * @cfg {String} mode
3447      * The shadow display mode.  Supports the following options:<br />
3448      * sides: Shadow displays on both sides and bottom only<br />
3449      * frame: Shadow displays equally on all four sides<br />
3450      * drop: Traditional bottom-right drop shadow (default)
3451      */
3452     /**
3453      * @cfg {String} offset
3454      * The number of pixels to offset the shadow from the element (defaults to 4)
3455      */
3456     offset: 4,
3457
3458     // private
3459     defaultMode: "drop",
3460
3461     /**
3462      * Displays the shadow under the target element
3463      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3464      */
3465     show : function(target){
3466         target = Roo.get(target);
3467         if(!this.el){
3468             this.el = Roo.Shadow.Pool.pull();
3469             if(this.el.dom.nextSibling != target.dom){
3470                 this.el.insertBefore(target);
3471             }
3472         }
3473         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3474         if(Roo.isIE){
3475             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3476         }
3477         this.realign(
3478             target.getLeft(true),
3479             target.getTop(true),
3480             target.getWidth(),
3481             target.getHeight()
3482         );
3483         this.el.dom.style.display = "block";
3484     },
3485
3486     /**
3487      * Returns true if the shadow is visible, else false
3488      */
3489     isVisible : function(){
3490         return this.el ? true : false;  
3491     },
3492
3493     /**
3494      * Direct alignment when values are already available. Show must be called at least once before
3495      * calling this method to ensure it is initialized.
3496      * @param {Number} left The target element left position
3497      * @param {Number} top The target element top position
3498      * @param {Number} width The target element width
3499      * @param {Number} height The target element height
3500      */
3501     realign : function(l, t, w, h){
3502         if(!this.el){
3503             return;
3504         }
3505         var a = this.adjusts, d = this.el.dom, s = d.style;
3506         var iea = 0;
3507         s.left = (l+a.l)+"px";
3508         s.top = (t+a.t)+"px";
3509         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3510  
3511         if(s.width != sws || s.height != shs){
3512             s.width = sws;
3513             s.height = shs;
3514             if(!Roo.isIE){
3515                 var cn = d.childNodes;
3516                 var sww = Math.max(0, (sw-12))+"px";
3517                 cn[0].childNodes[1].style.width = sww;
3518                 cn[1].childNodes[1].style.width = sww;
3519                 cn[2].childNodes[1].style.width = sww;
3520                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3521             }
3522         }
3523     },
3524
3525     /**
3526      * Hides this shadow
3527      */
3528     hide : function(){
3529         if(this.el){
3530             this.el.dom.style.display = "none";
3531             Roo.Shadow.Pool.push(this.el);
3532             delete this.el;
3533         }
3534     },
3535
3536     /**
3537      * Adjust the z-index of this shadow
3538      * @param {Number} zindex The new z-index
3539      */
3540     setZIndex : function(z){
3541         this.zIndex = z;
3542         if(this.el){
3543             this.el.setStyle("z-index", z);
3544         }
3545     }
3546 };
3547
3548 // Private utility class that manages the internal Shadow cache
3549 Roo.Shadow.Pool = function(){
3550     var p = [];
3551     var markup = Roo.isIE ?
3552                  '<div class="x-ie-shadow"></div>' :
3553                  '<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>';
3554     return {
3555         pull : function(){
3556             var sh = p.shift();
3557             if(!sh){
3558                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3559                 sh.autoBoxAdjust = false;
3560             }
3561             return sh;
3562         },
3563
3564         push : function(sh){
3565             p.push(sh);
3566         }
3567     };
3568 }();/*
3569  * Based on:
3570  * Ext JS Library 1.1.1
3571  * Copyright(c) 2006-2007, Ext JS, LLC.
3572  *
3573  * Originally Released Under LGPL - original licence link has changed is not relivant.
3574  *
3575  * Fork - LGPL
3576  * <script type="text/javascript">
3577  */
3578
3579
3580 /**
3581  * @class Roo.SplitBar
3582  * @extends Roo.util.Observable
3583  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3584  * <br><br>
3585  * Usage:
3586  * <pre><code>
3587 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3588                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3589 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3590 split.minSize = 100;
3591 split.maxSize = 600;
3592 split.animate = true;
3593 split.on('moved', splitterMoved);
3594 </code></pre>
3595  * @constructor
3596  * Create a new SplitBar
3597  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3598  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3599  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3600  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3601                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3602                         position of the SplitBar).
3603  */
3604 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3605     
3606     /** @private */
3607     this.el = Roo.get(dragElement, true);
3608     this.el.dom.unselectable = "on";
3609     /** @private */
3610     this.resizingEl = Roo.get(resizingElement, true);
3611
3612     /**
3613      * @private
3614      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3615      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3616      * @type Number
3617      */
3618     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3619     
3620     /**
3621      * The minimum size of the resizing element. (Defaults to 0)
3622      * @type Number
3623      */
3624     this.minSize = 0;
3625     
3626     /**
3627      * The maximum size of the resizing element. (Defaults to 2000)
3628      * @type Number
3629      */
3630     this.maxSize = 2000;
3631     
3632     /**
3633      * Whether to animate the transition to the new size
3634      * @type Boolean
3635      */
3636     this.animate = false;
3637     
3638     /**
3639      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3640      * @type Boolean
3641      */
3642     this.useShim = false;
3643     
3644     /** @private */
3645     this.shim = null;
3646     
3647     if(!existingProxy){
3648         /** @private */
3649         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3650     }else{
3651         this.proxy = Roo.get(existingProxy).dom;
3652     }
3653     /** @private */
3654     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3655     
3656     /** @private */
3657     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3658     
3659     /** @private */
3660     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3661     
3662     /** @private */
3663     this.dragSpecs = {};
3664     
3665     /**
3666      * @private The adapter to use to positon and resize elements
3667      */
3668     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3669     this.adapter.init(this);
3670     
3671     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3672         /** @private */
3673         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3674         this.el.addClass("x-splitbar-h");
3675     }else{
3676         /** @private */
3677         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3678         this.el.addClass("x-splitbar-v");
3679     }
3680     
3681     this.addEvents({
3682         /**
3683          * @event resize
3684          * Fires when the splitter is moved (alias for {@link #event-moved})
3685          * @param {Roo.SplitBar} this
3686          * @param {Number} newSize the new width or height
3687          */
3688         "resize" : true,
3689         /**
3690          * @event moved
3691          * Fires when the splitter is moved
3692          * @param {Roo.SplitBar} this
3693          * @param {Number} newSize the new width or height
3694          */
3695         "moved" : true,
3696         /**
3697          * @event beforeresize
3698          * Fires before the splitter is dragged
3699          * @param {Roo.SplitBar} this
3700          */
3701         "beforeresize" : true,
3702
3703         "beforeapply" : true
3704     });
3705
3706     Roo.util.Observable.call(this);
3707 };
3708
3709 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3710     onStartProxyDrag : function(x, y){
3711         this.fireEvent("beforeresize", this);
3712         if(!this.overlay){
3713             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3714             o.unselectable();
3715             o.enableDisplayMode("block");
3716             // all splitbars share the same overlay
3717             Roo.SplitBar.prototype.overlay = o;
3718         }
3719         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3720         this.overlay.show();
3721         Roo.get(this.proxy).setDisplayed("block");
3722         var size = this.adapter.getElementSize(this);
3723         this.activeMinSize = this.getMinimumSize();;
3724         this.activeMaxSize = this.getMaximumSize();;
3725         var c1 = size - this.activeMinSize;
3726         var c2 = Math.max(this.activeMaxSize - size, 0);
3727         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3728             this.dd.resetConstraints();
3729             this.dd.setXConstraint(
3730                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3731                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3732             );
3733             this.dd.setYConstraint(0, 0);
3734         }else{
3735             this.dd.resetConstraints();
3736             this.dd.setXConstraint(0, 0);
3737             this.dd.setYConstraint(
3738                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3739                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3740             );
3741          }
3742         this.dragSpecs.startSize = size;
3743         this.dragSpecs.startPoint = [x, y];
3744         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3745     },
3746     
3747     /** 
3748      * @private Called after the drag operation by the DDProxy
3749      */
3750     onEndProxyDrag : function(e){
3751         Roo.get(this.proxy).setDisplayed(false);
3752         var endPoint = Roo.lib.Event.getXY(e);
3753         if(this.overlay){
3754             this.overlay.hide();
3755         }
3756         var newSize;
3757         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3758             newSize = this.dragSpecs.startSize + 
3759                 (this.placement == Roo.SplitBar.LEFT ?
3760                     endPoint[0] - this.dragSpecs.startPoint[0] :
3761                     this.dragSpecs.startPoint[0] - endPoint[0]
3762                 );
3763         }else{
3764             newSize = this.dragSpecs.startSize + 
3765                 (this.placement == Roo.SplitBar.TOP ?
3766                     endPoint[1] - this.dragSpecs.startPoint[1] :
3767                     this.dragSpecs.startPoint[1] - endPoint[1]
3768                 );
3769         }
3770         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3771         if(newSize != this.dragSpecs.startSize){
3772             if(this.fireEvent('beforeapply', this, newSize) !== false){
3773                 this.adapter.setElementSize(this, newSize);
3774                 this.fireEvent("moved", this, newSize);
3775                 this.fireEvent("resize", this, newSize);
3776             }
3777         }
3778     },
3779     
3780     /**
3781      * Get the adapter this SplitBar uses
3782      * @return The adapter object
3783      */
3784     getAdapter : function(){
3785         return this.adapter;
3786     },
3787     
3788     /**
3789      * Set the adapter this SplitBar uses
3790      * @param {Object} adapter A SplitBar adapter object
3791      */
3792     setAdapter : function(adapter){
3793         this.adapter = adapter;
3794         this.adapter.init(this);
3795     },
3796     
3797     /**
3798      * Gets the minimum size for the resizing element
3799      * @return {Number} The minimum size
3800      */
3801     getMinimumSize : function(){
3802         return this.minSize;
3803     },
3804     
3805     /**
3806      * Sets the minimum size for the resizing element
3807      * @param {Number} minSize The minimum size
3808      */
3809     setMinimumSize : function(minSize){
3810         this.minSize = minSize;
3811     },
3812     
3813     /**
3814      * Gets the maximum size for the resizing element
3815      * @return {Number} The maximum size
3816      */
3817     getMaximumSize : function(){
3818         return this.maxSize;
3819     },
3820     
3821     /**
3822      * Sets the maximum size for the resizing element
3823      * @param {Number} maxSize The maximum size
3824      */
3825     setMaximumSize : function(maxSize){
3826         this.maxSize = maxSize;
3827     },
3828     
3829     /**
3830      * Sets the initialize size for the resizing element
3831      * @param {Number} size The initial size
3832      */
3833     setCurrentSize : function(size){
3834         var oldAnimate = this.animate;
3835         this.animate = false;
3836         this.adapter.setElementSize(this, size);
3837         this.animate = oldAnimate;
3838     },
3839     
3840     /**
3841      * Destroy this splitbar. 
3842      * @param {Boolean} removeEl True to remove the element
3843      */
3844     destroy : function(removeEl){
3845         if(this.shim){
3846             this.shim.remove();
3847         }
3848         this.dd.unreg();
3849         this.proxy.parentNode.removeChild(this.proxy);
3850         if(removeEl){
3851             this.el.remove();
3852         }
3853     }
3854 });
3855
3856 /**
3857  * @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.
3858  */
3859 Roo.SplitBar.createProxy = function(dir){
3860     var proxy = new Roo.Element(document.createElement("div"));
3861     proxy.unselectable();
3862     var cls = 'x-splitbar-proxy';
3863     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3864     document.body.appendChild(proxy.dom);
3865     return proxy.dom;
3866 };
3867
3868 /** 
3869  * @class Roo.SplitBar.BasicLayoutAdapter
3870  * Default Adapter. It assumes the splitter and resizing element are not positioned
3871  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3872  */
3873 Roo.SplitBar.BasicLayoutAdapter = function(){
3874 };
3875
3876 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3877     // do nothing for now
3878     init : function(s){
3879     
3880     },
3881     /**
3882      * Called before drag operations to get the current size of the resizing element. 
3883      * @param {Roo.SplitBar} s The SplitBar using this adapter
3884      */
3885      getElementSize : function(s){
3886         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3887             return s.resizingEl.getWidth();
3888         }else{
3889             return s.resizingEl.getHeight();
3890         }
3891     },
3892     
3893     /**
3894      * Called after drag operations to set the size of the resizing element.
3895      * @param {Roo.SplitBar} s The SplitBar using this adapter
3896      * @param {Number} newSize The new size to set
3897      * @param {Function} onComplete A function to be invoked when resizing is complete
3898      */
3899     setElementSize : function(s, newSize, onComplete){
3900         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3901             if(!s.animate){
3902                 s.resizingEl.setWidth(newSize);
3903                 if(onComplete){
3904                     onComplete(s, newSize);
3905                 }
3906             }else{
3907                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3908             }
3909         }else{
3910             
3911             if(!s.animate){
3912                 s.resizingEl.setHeight(newSize);
3913                 if(onComplete){
3914                     onComplete(s, newSize);
3915                 }
3916             }else{
3917                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3918             }
3919         }
3920     }
3921 };
3922
3923 /** 
3924  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3925  * @extends Roo.SplitBar.BasicLayoutAdapter
3926  * Adapter that  moves the splitter element to align with the resized sizing element. 
3927  * Used with an absolute positioned SplitBar.
3928  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3929  * document.body, make sure you assign an id to the body element.
3930  */
3931 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3932     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3933     this.container = Roo.get(container);
3934 };
3935
3936 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3937     init : function(s){
3938         this.basic.init(s);
3939     },
3940     
3941     getElementSize : function(s){
3942         return this.basic.getElementSize(s);
3943     },
3944     
3945     setElementSize : function(s, newSize, onComplete){
3946         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3947     },
3948     
3949     moveSplitter : function(s){
3950         var yes = Roo.SplitBar;
3951         switch(s.placement){
3952             case yes.LEFT:
3953                 s.el.setX(s.resizingEl.getRight());
3954                 break;
3955             case yes.RIGHT:
3956                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3957                 break;
3958             case yes.TOP:
3959                 s.el.setY(s.resizingEl.getBottom());
3960                 break;
3961             case yes.BOTTOM:
3962                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3963                 break;
3964         }
3965     }
3966 };
3967
3968 /**
3969  * Orientation constant - Create a vertical SplitBar
3970  * @static
3971  * @type Number
3972  */
3973 Roo.SplitBar.VERTICAL = 1;
3974
3975 /**
3976  * Orientation constant - Create a horizontal SplitBar
3977  * @static
3978  * @type Number
3979  */
3980 Roo.SplitBar.HORIZONTAL = 2;
3981
3982 /**
3983  * Placement constant - The resizing element is to the left of the splitter element
3984  * @static
3985  * @type Number
3986  */
3987 Roo.SplitBar.LEFT = 1;
3988
3989 /**
3990  * Placement constant - The resizing element is to the right of the splitter element
3991  * @static
3992  * @type Number
3993  */
3994 Roo.SplitBar.RIGHT = 2;
3995
3996 /**
3997  * Placement constant - The resizing element is positioned above the splitter element
3998  * @static
3999  * @type Number
4000  */
4001 Roo.SplitBar.TOP = 3;
4002
4003 /**
4004  * Placement constant - The resizing element is positioned under splitter element
4005  * @static
4006  * @type Number
4007  */
4008 Roo.SplitBar.BOTTOM = 4;
4009 /*
4010  * Based on:
4011  * Ext JS Library 1.1.1
4012  * Copyright(c) 2006-2007, Ext JS, LLC.
4013  *
4014  * Originally Released Under LGPL - original licence link has changed is not relivant.
4015  *
4016  * Fork - LGPL
4017  * <script type="text/javascript">
4018  */
4019
4020 /**
4021  * @class Roo.View
4022  * @extends Roo.util.Observable
4023  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4024  * This class also supports single and multi selection modes. <br>
4025  * Create a data model bound view:
4026  <pre><code>
4027  var store = new Roo.data.Store(...);
4028
4029  var view = new Roo.View({
4030     el : "my-element",
4031     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4032  
4033     singleSelect: true,
4034     selectedClass: "ydataview-selected",
4035     store: store
4036  });
4037
4038  // listen for node click?
4039  view.on("click", function(vw, index, node, e){
4040  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4041  });
4042
4043  // load XML data
4044  dataModel.load("foobar.xml");
4045  </code></pre>
4046  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4047  * <br><br>
4048  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4049  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4050  * 
4051  * Note: old style constructor is still suported (container, template, config)
4052  * 
4053  * @constructor
4054  * Create a new View
4055  * @param {Object} config The config object
4056  * 
4057  */
4058 Roo.View = function(config, depreciated_tpl, depreciated_config){
4059     
4060     this.parent = false;
4061     
4062     if (typeof(depreciated_tpl) == 'undefined') {
4063         // new way.. - universal constructor.
4064         Roo.apply(this, config);
4065         this.el  = Roo.get(this.el);
4066     } else {
4067         // old format..
4068         this.el  = Roo.get(config);
4069         this.tpl = depreciated_tpl;
4070         Roo.apply(this, depreciated_config);
4071     }
4072     this.wrapEl  = this.el.wrap().wrap();
4073     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4074     
4075     
4076     if(typeof(this.tpl) == "string"){
4077         this.tpl = new Roo.Template(this.tpl);
4078     } else {
4079         // support xtype ctors..
4080         this.tpl = new Roo.factory(this.tpl, Roo);
4081     }
4082     
4083     
4084     this.tpl.compile();
4085     
4086     /** @private */
4087     this.addEvents({
4088         /**
4089          * @event beforeclick
4090          * Fires before a click is processed. Returns false to cancel the default action.
4091          * @param {Roo.View} this
4092          * @param {Number} index The index of the target node
4093          * @param {HTMLElement} node The target node
4094          * @param {Roo.EventObject} e The raw event object
4095          */
4096             "beforeclick" : true,
4097         /**
4098          * @event click
4099          * Fires when a template node is clicked.
4100          * @param {Roo.View} this
4101          * @param {Number} index The index of the target node
4102          * @param {HTMLElement} node The target node
4103          * @param {Roo.EventObject} e The raw event object
4104          */
4105             "click" : true,
4106         /**
4107          * @event dblclick
4108          * Fires when a template node is double clicked.
4109          * @param {Roo.View} this
4110          * @param {Number} index The index of the target node
4111          * @param {HTMLElement} node The target node
4112          * @param {Roo.EventObject} e The raw event object
4113          */
4114             "dblclick" : true,
4115         /**
4116          * @event contextmenu
4117          * Fires when a template node is right clicked.
4118          * @param {Roo.View} this
4119          * @param {Number} index The index of the target node
4120          * @param {HTMLElement} node The target node
4121          * @param {Roo.EventObject} e The raw event object
4122          */
4123             "contextmenu" : true,
4124         /**
4125          * @event selectionchange
4126          * Fires when the selected nodes change.
4127          * @param {Roo.View} this
4128          * @param {Array} selections Array of the selected nodes
4129          */
4130             "selectionchange" : true,
4131     
4132         /**
4133          * @event beforeselect
4134          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4135          * @param {Roo.View} this
4136          * @param {HTMLElement} node The node to be selected
4137          * @param {Array} selections Array of currently selected nodes
4138          */
4139             "beforeselect" : true,
4140         /**
4141          * @event preparedata
4142          * Fires on every row to render, to allow you to change the data.
4143          * @param {Roo.View} this
4144          * @param {Object} data to be rendered (change this)
4145          */
4146           "preparedata" : true
4147           
4148           
4149         });
4150
4151
4152
4153     this.el.on({
4154         "click": this.onClick,
4155         "dblclick": this.onDblClick,
4156         "contextmenu": this.onContextMenu,
4157         scope:this
4158     });
4159
4160     this.selections = [];
4161     this.nodes = [];
4162     this.cmp = new Roo.CompositeElementLite([]);
4163     if(this.store){
4164         this.store = Roo.factory(this.store, Roo.data);
4165         this.setStore(this.store, true);
4166     }
4167     
4168     if ( this.footer && this.footer.xtype) {
4169            
4170          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4171         
4172         this.footer.dataSource = this.store;
4173         this.footer.container = fctr;
4174         this.footer = Roo.factory(this.footer, Roo);
4175         fctr.insertFirst(this.el);
4176         
4177         // this is a bit insane - as the paging toolbar seems to detach the el..
4178 //        dom.parentNode.parentNode.parentNode
4179          // they get detached?
4180     }
4181     
4182     
4183     Roo.View.superclass.constructor.call(this);
4184     
4185     
4186 };
4187
4188 Roo.extend(Roo.View, Roo.util.Observable, {
4189     
4190      /**
4191      * @cfg {Roo.data.Store} store Data store to load data from.
4192      */
4193     store : false,
4194     
4195     /**
4196      * @cfg {String|Roo.Element} el The container element.
4197      */
4198     el : '',
4199     
4200     /**
4201      * @cfg {String|Roo.Template} tpl The template used by this View 
4202      */
4203     tpl : false,
4204     /**
4205      * @cfg {String} dataName the named area of the template to use as the data area
4206      *                          Works with domtemplates roo-name="name"
4207      */
4208     dataName: false,
4209     /**
4210      * @cfg {String} selectedClass The css class to add to selected nodes
4211      */
4212     selectedClass : "x-view-selected",
4213      /**
4214      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4215      */
4216     emptyText : "",
4217     
4218     /**
4219      * @cfg {String} text to display on mask (default Loading)
4220      */
4221     mask : false,
4222     /**
4223      * @cfg {Boolean} multiSelect Allow multiple selection
4224      */
4225     multiSelect : false,
4226     /**
4227      * @cfg {Boolean} singleSelect Allow single selection
4228      */
4229     singleSelect:  false,
4230     
4231     /**
4232      * @cfg {Boolean} toggleSelect - selecting 
4233      */
4234     toggleSelect : false,
4235     
4236     /**
4237      * @cfg {Boolean} tickable - selecting 
4238      */
4239     tickable : false,
4240     
4241     /**
4242      * Returns the element this view is bound to.
4243      * @return {Roo.Element}
4244      */
4245     getEl : function(){
4246         return this.wrapEl;
4247     },
4248     
4249     
4250
4251     /**
4252      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4253      */
4254     refresh : function(){
4255         //Roo.log('refresh');
4256         var t = this.tpl;
4257         
4258         // if we are using something like 'domtemplate', then
4259         // the what gets used is:
4260         // t.applySubtemplate(NAME, data, wrapping data..)
4261         // the outer template then get' applied with
4262         //     the store 'extra data'
4263         // and the body get's added to the
4264         //      roo-name="data" node?
4265         //      <span class='roo-tpl-{name}'></span> ?????
4266         
4267         
4268         
4269         this.clearSelections();
4270         this.el.update("");
4271         var html = [];
4272         var records = this.store.getRange();
4273         if(records.length < 1) {
4274             
4275             // is this valid??  = should it render a template??
4276             
4277             this.el.update(this.emptyText);
4278             return;
4279         }
4280         var el = this.el;
4281         if (this.dataName) {
4282             this.el.update(t.apply(this.store.meta)); //????
4283             el = this.el.child('.roo-tpl-' + this.dataName);
4284         }
4285         
4286         for(var i = 0, len = records.length; i < len; i++){
4287             var data = this.prepareData(records[i].data, i, records[i]);
4288             this.fireEvent("preparedata", this, data, i, records[i]);
4289             
4290             var d = Roo.apply({}, data);
4291             
4292             if(this.tickable){
4293                 Roo.apply(d, {'roo-id' : Roo.id()});
4294                 
4295                 var _this = this;
4296             
4297                 Roo.each(this.parent.item, function(item){
4298                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4299                         return;
4300                     }
4301                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4302                 });
4303             }
4304             
4305             html[html.length] = Roo.util.Format.trim(
4306                 this.dataName ?
4307                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4308                     t.apply(d)
4309             );
4310         }
4311         
4312         
4313         
4314         el.update(html.join(""));
4315         this.nodes = el.dom.childNodes;
4316         this.updateIndexes(0);
4317     },
4318     
4319
4320     /**
4321      * Function to override to reformat the data that is sent to
4322      * the template for each node.
4323      * DEPRICATED - use the preparedata event handler.
4324      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4325      * a JSON object for an UpdateManager bound view).
4326      */
4327     prepareData : function(data, index, record)
4328     {
4329         this.fireEvent("preparedata", this, data, index, record);
4330         return data;
4331     },
4332
4333     onUpdate : function(ds, record){
4334         // Roo.log('on update');   
4335         this.clearSelections();
4336         var index = this.store.indexOf(record);
4337         var n = this.nodes[index];
4338         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4339         n.parentNode.removeChild(n);
4340         this.updateIndexes(index, index);
4341     },
4342
4343     
4344     
4345 // --------- FIXME     
4346     onAdd : function(ds, records, index)
4347     {
4348         //Roo.log(['on Add', ds, records, index] );        
4349         this.clearSelections();
4350         if(this.nodes.length == 0){
4351             this.refresh();
4352             return;
4353         }
4354         var n = this.nodes[index];
4355         for(var i = 0, len = records.length; i < len; i++){
4356             var d = this.prepareData(records[i].data, i, records[i]);
4357             if(n){
4358                 this.tpl.insertBefore(n, d);
4359             }else{
4360                 
4361                 this.tpl.append(this.el, d);
4362             }
4363         }
4364         this.updateIndexes(index);
4365     },
4366
4367     onRemove : function(ds, record, index){
4368        // Roo.log('onRemove');
4369         this.clearSelections();
4370         var el = this.dataName  ?
4371             this.el.child('.roo-tpl-' + this.dataName) :
4372             this.el; 
4373         
4374         el.dom.removeChild(this.nodes[index]);
4375         this.updateIndexes(index);
4376     },
4377
4378     /**
4379      * Refresh an individual node.
4380      * @param {Number} index
4381      */
4382     refreshNode : function(index){
4383         this.onUpdate(this.store, this.store.getAt(index));
4384     },
4385
4386     updateIndexes : function(startIndex, endIndex){
4387         var ns = this.nodes;
4388         startIndex = startIndex || 0;
4389         endIndex = endIndex || ns.length - 1;
4390         for(var i = startIndex; i <= endIndex; i++){
4391             ns[i].nodeIndex = i;
4392         }
4393     },
4394
4395     /**
4396      * Changes the data store this view uses and refresh the view.
4397      * @param {Store} store
4398      */
4399     setStore : function(store, initial){
4400         if(!initial && this.store){
4401             this.store.un("datachanged", this.refresh);
4402             this.store.un("add", this.onAdd);
4403             this.store.un("remove", this.onRemove);
4404             this.store.un("update", this.onUpdate);
4405             this.store.un("clear", this.refresh);
4406             this.store.un("beforeload", this.onBeforeLoad);
4407             this.store.un("load", this.onLoad);
4408             this.store.un("loadexception", this.onLoad);
4409         }
4410         if(store){
4411           
4412             store.on("datachanged", this.refresh, this);
4413             store.on("add", this.onAdd, this);
4414             store.on("remove", this.onRemove, this);
4415             store.on("update", this.onUpdate, this);
4416             store.on("clear", this.refresh, this);
4417             store.on("beforeload", this.onBeforeLoad, this);
4418             store.on("load", this.onLoad, this);
4419             store.on("loadexception", this.onLoad, this);
4420         }
4421         
4422         if(store){
4423             this.refresh();
4424         }
4425     },
4426     /**
4427      * onbeforeLoad - masks the loading area.
4428      *
4429      */
4430     onBeforeLoad : function(store,opts)
4431     {
4432          //Roo.log('onBeforeLoad');   
4433         if (!opts.add) {
4434             this.el.update("");
4435         }
4436         this.el.mask(this.mask ? this.mask : "Loading" ); 
4437     },
4438     onLoad : function ()
4439     {
4440         this.el.unmask();
4441     },
4442     
4443
4444     /**
4445      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4446      * @param {HTMLElement} node
4447      * @return {HTMLElement} The template node
4448      */
4449     findItemFromChild : function(node){
4450         var el = this.dataName  ?
4451             this.el.child('.roo-tpl-' + this.dataName,true) :
4452             this.el.dom; 
4453         
4454         if(!node || node.parentNode == el){
4455                     return node;
4456             }
4457             var p = node.parentNode;
4458             while(p && p != el){
4459             if(p.parentNode == el){
4460                 return p;
4461             }
4462             p = p.parentNode;
4463         }
4464             return null;
4465     },
4466
4467     /** @ignore */
4468     onClick : function(e){
4469         var item = this.findItemFromChild(e.getTarget());
4470         if(item){
4471             var index = this.indexOf(item);
4472             if(this.onItemClick(item, index, e) !== false){
4473                 this.fireEvent("click", this, index, item, e);
4474             }
4475         }else{
4476             this.clearSelections();
4477         }
4478     },
4479
4480     /** @ignore */
4481     onContextMenu : function(e){
4482         var item = this.findItemFromChild(e.getTarget());
4483         if(item){
4484             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4485         }
4486     },
4487
4488     /** @ignore */
4489     onDblClick : function(e){
4490         var item = this.findItemFromChild(e.getTarget());
4491         if(item){
4492             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4493         }
4494     },
4495
4496     onItemClick : function(item, index, e)
4497     {
4498         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4499             return false;
4500         }
4501         if (this.toggleSelect) {
4502             var m = this.isSelected(item) ? 'unselect' : 'select';
4503             //Roo.log(m);
4504             var _t = this;
4505             _t[m](item, true, false);
4506             return true;
4507         }
4508         if(this.multiSelect || this.singleSelect){
4509             if(this.multiSelect && e.shiftKey && this.lastSelection){
4510                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4511             }else{
4512                 this.select(item, this.multiSelect && e.ctrlKey);
4513                 this.lastSelection = item;
4514             }
4515             
4516             if(!this.tickable){
4517                 e.preventDefault();
4518             }
4519             
4520         }
4521         return true;
4522     },
4523
4524     /**
4525      * Get the number of selected nodes.
4526      * @return {Number}
4527      */
4528     getSelectionCount : function(){
4529         return this.selections.length;
4530     },
4531
4532     /**
4533      * Get the currently selected nodes.
4534      * @return {Array} An array of HTMLElements
4535      */
4536     getSelectedNodes : function(){
4537         return this.selections;
4538     },
4539
4540     /**
4541      * Get the indexes of the selected nodes.
4542      * @return {Array}
4543      */
4544     getSelectedIndexes : function(){
4545         var indexes = [], s = this.selections;
4546         for(var i = 0, len = s.length; i < len; i++){
4547             indexes.push(s[i].nodeIndex);
4548         }
4549         return indexes;
4550     },
4551
4552     /**
4553      * Clear all selections
4554      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4555      */
4556     clearSelections : function(suppressEvent){
4557         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4558             this.cmp.elements = this.selections;
4559             this.cmp.removeClass(this.selectedClass);
4560             this.selections = [];
4561             if(!suppressEvent){
4562                 this.fireEvent("selectionchange", this, this.selections);
4563             }
4564         }
4565     },
4566
4567     /**
4568      * Returns true if the passed node is selected
4569      * @param {HTMLElement/Number} node The node or node index
4570      * @return {Boolean}
4571      */
4572     isSelected : function(node){
4573         var s = this.selections;
4574         if(s.length < 1){
4575             return false;
4576         }
4577         node = this.getNode(node);
4578         return s.indexOf(node) !== -1;
4579     },
4580
4581     /**
4582      * Selects nodes.
4583      * @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
4584      * @param {Boolean} keepExisting (optional) true to keep existing selections
4585      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4586      */
4587     select : function(nodeInfo, keepExisting, suppressEvent){
4588         if(nodeInfo instanceof Array){
4589             if(!keepExisting){
4590                 this.clearSelections(true);
4591             }
4592             for(var i = 0, len = nodeInfo.length; i < len; i++){
4593                 this.select(nodeInfo[i], true, true);
4594             }
4595             return;
4596         } 
4597         var node = this.getNode(nodeInfo);
4598         if(!node || this.isSelected(node)){
4599             return; // already selected.
4600         }
4601         if(!keepExisting){
4602             this.clearSelections(true);
4603         }
4604         
4605         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4606             Roo.fly(node).addClass(this.selectedClass);
4607             this.selections.push(node);
4608             if(!suppressEvent){
4609                 this.fireEvent("selectionchange", this, this.selections);
4610             }
4611         }
4612         
4613         
4614     },
4615       /**
4616      * Unselects nodes.
4617      * @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
4618      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4619      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4620      */
4621     unselect : function(nodeInfo, keepExisting, suppressEvent)
4622     {
4623         if(nodeInfo instanceof Array){
4624             Roo.each(this.selections, function(s) {
4625                 this.unselect(s, nodeInfo);
4626             }, this);
4627             return;
4628         }
4629         var node = this.getNode(nodeInfo);
4630         if(!node || !this.isSelected(node)){
4631             //Roo.log("not selected");
4632             return; // not selected.
4633         }
4634         // fireevent???
4635         var ns = [];
4636         Roo.each(this.selections, function(s) {
4637             if (s == node ) {
4638                 Roo.fly(node).removeClass(this.selectedClass);
4639
4640                 return;
4641             }
4642             ns.push(s);
4643         },this);
4644         
4645         this.selections= ns;
4646         this.fireEvent("selectionchange", this, this.selections);
4647     },
4648
4649     /**
4650      * Gets a template node.
4651      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4652      * @return {HTMLElement} The node or null if it wasn't found
4653      */
4654     getNode : function(nodeInfo){
4655         if(typeof nodeInfo == "string"){
4656             return document.getElementById(nodeInfo);
4657         }else if(typeof nodeInfo == "number"){
4658             return this.nodes[nodeInfo];
4659         }
4660         return nodeInfo;
4661     },
4662
4663     /**
4664      * Gets a range template nodes.
4665      * @param {Number} startIndex
4666      * @param {Number} endIndex
4667      * @return {Array} An array of nodes
4668      */
4669     getNodes : function(start, end){
4670         var ns = this.nodes;
4671         start = start || 0;
4672         end = typeof end == "undefined" ? ns.length - 1 : end;
4673         var nodes = [];
4674         if(start <= end){
4675             for(var i = start; i <= end; i++){
4676                 nodes.push(ns[i]);
4677             }
4678         } else{
4679             for(var i = start; i >= end; i--){
4680                 nodes.push(ns[i]);
4681             }
4682         }
4683         return nodes;
4684     },
4685
4686     /**
4687      * Finds the index of the passed node
4688      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4689      * @return {Number} The index of the node or -1
4690      */
4691     indexOf : function(node){
4692         node = this.getNode(node);
4693         if(typeof node.nodeIndex == "number"){
4694             return node.nodeIndex;
4695         }
4696         var ns = this.nodes;
4697         for(var i = 0, len = ns.length; i < len; i++){
4698             if(ns[i] == node){
4699                 return i;
4700             }
4701         }
4702         return -1;
4703     }
4704 });
4705 /*
4706  * Based on:
4707  * Ext JS Library 1.1.1
4708  * Copyright(c) 2006-2007, Ext JS, LLC.
4709  *
4710  * Originally Released Under LGPL - original licence link has changed is not relivant.
4711  *
4712  * Fork - LGPL
4713  * <script type="text/javascript">
4714  */
4715
4716 /**
4717  * @class Roo.JsonView
4718  * @extends Roo.View
4719  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4720 <pre><code>
4721 var view = new Roo.JsonView({
4722     container: "my-element",
4723     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4724     multiSelect: true, 
4725     jsonRoot: "data" 
4726 });
4727
4728 // listen for node click?
4729 view.on("click", function(vw, index, node, e){
4730     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4731 });
4732
4733 // direct load of JSON data
4734 view.load("foobar.php");
4735
4736 // Example from my blog list
4737 var tpl = new Roo.Template(
4738     '&lt;div class="entry"&gt;' +
4739     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4740     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4741     "&lt;/div&gt;&lt;hr /&gt;"
4742 );
4743
4744 var moreView = new Roo.JsonView({
4745     container :  "entry-list", 
4746     template : tpl,
4747     jsonRoot: "posts"
4748 });
4749 moreView.on("beforerender", this.sortEntries, this);
4750 moreView.load({
4751     url: "/blog/get-posts.php",
4752     params: "allposts=true",
4753     text: "Loading Blog Entries..."
4754 });
4755 </code></pre>
4756
4757 * Note: old code is supported with arguments : (container, template, config)
4758
4759
4760  * @constructor
4761  * Create a new JsonView
4762  * 
4763  * @param {Object} config The config object
4764  * 
4765  */
4766 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4767     
4768     
4769     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4770
4771     var um = this.el.getUpdateManager();
4772     um.setRenderer(this);
4773     um.on("update", this.onLoad, this);
4774     um.on("failure", this.onLoadException, this);
4775
4776     /**
4777      * @event beforerender
4778      * Fires before rendering of the downloaded JSON data.
4779      * @param {Roo.JsonView} this
4780      * @param {Object} data The JSON data loaded
4781      */
4782     /**
4783      * @event load
4784      * Fires when data is loaded.
4785      * @param {Roo.JsonView} this
4786      * @param {Object} data The JSON data loaded
4787      * @param {Object} response The raw Connect response object
4788      */
4789     /**
4790      * @event loadexception
4791      * Fires when loading fails.
4792      * @param {Roo.JsonView} this
4793      * @param {Object} response The raw Connect response object
4794      */
4795     this.addEvents({
4796         'beforerender' : true,
4797         'load' : true,
4798         'loadexception' : true
4799     });
4800 };
4801 Roo.extend(Roo.JsonView, Roo.View, {
4802     /**
4803      * @type {String} The root property in the loaded JSON object that contains the data
4804      */
4805     jsonRoot : "",
4806
4807     /**
4808      * Refreshes the view.
4809      */
4810     refresh : function(){
4811         this.clearSelections();
4812         this.el.update("");
4813         var html = [];
4814         var o = this.jsonData;
4815         if(o && o.length > 0){
4816             for(var i = 0, len = o.length; i < len; i++){
4817                 var data = this.prepareData(o[i], i, o);
4818                 html[html.length] = this.tpl.apply(data);
4819             }
4820         }else{
4821             html.push(this.emptyText);
4822         }
4823         this.el.update(html.join(""));
4824         this.nodes = this.el.dom.childNodes;
4825         this.updateIndexes(0);
4826     },
4827
4828     /**
4829      * 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.
4830      * @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:
4831      <pre><code>
4832      view.load({
4833          url: "your-url.php",
4834          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4835          callback: yourFunction,
4836          scope: yourObject, //(optional scope)
4837          discardUrl: false,
4838          nocache: false,
4839          text: "Loading...",
4840          timeout: 30,
4841          scripts: false
4842      });
4843      </code></pre>
4844      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4845      * 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.
4846      * @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}
4847      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4848      * @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.
4849      */
4850     load : function(){
4851         var um = this.el.getUpdateManager();
4852         um.update.apply(um, arguments);
4853     },
4854
4855     // note - render is a standard framework call...
4856     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4857     render : function(el, response){
4858         
4859         this.clearSelections();
4860         this.el.update("");
4861         var o;
4862         try{
4863             if (response != '') {
4864                 o = Roo.util.JSON.decode(response.responseText);
4865                 if(this.jsonRoot){
4866                     
4867                     o = o[this.jsonRoot];
4868                 }
4869             }
4870         } catch(e){
4871         }
4872         /**
4873          * The current JSON data or null
4874          */
4875         this.jsonData = o;
4876         this.beforeRender();
4877         this.refresh();
4878     },
4879
4880 /**
4881  * Get the number of records in the current JSON dataset
4882  * @return {Number}
4883  */
4884     getCount : function(){
4885         return this.jsonData ? this.jsonData.length : 0;
4886     },
4887
4888 /**
4889  * Returns the JSON object for the specified node(s)
4890  * @param {HTMLElement/Array} node The node or an array of nodes
4891  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4892  * you get the JSON object for the node
4893  */
4894     getNodeData : function(node){
4895         if(node instanceof Array){
4896             var data = [];
4897             for(var i = 0, len = node.length; i < len; i++){
4898                 data.push(this.getNodeData(node[i]));
4899             }
4900             return data;
4901         }
4902         return this.jsonData[this.indexOf(node)] || null;
4903     },
4904
4905     beforeRender : function(){
4906         this.snapshot = this.jsonData;
4907         if(this.sortInfo){
4908             this.sort.apply(this, this.sortInfo);
4909         }
4910         this.fireEvent("beforerender", this, this.jsonData);
4911     },
4912
4913     onLoad : function(el, o){
4914         this.fireEvent("load", this, this.jsonData, o);
4915     },
4916
4917     onLoadException : function(el, o){
4918         this.fireEvent("loadexception", this, o);
4919     },
4920
4921 /**
4922  * Filter the data by a specific property.
4923  * @param {String} property A property on your JSON objects
4924  * @param {String/RegExp} value Either string that the property values
4925  * should start with, or a RegExp to test against the property
4926  */
4927     filter : function(property, value){
4928         if(this.jsonData){
4929             var data = [];
4930             var ss = this.snapshot;
4931             if(typeof value == "string"){
4932                 var vlen = value.length;
4933                 if(vlen == 0){
4934                     this.clearFilter();
4935                     return;
4936                 }
4937                 value = value.toLowerCase();
4938                 for(var i = 0, len = ss.length; i < len; i++){
4939                     var o = ss[i];
4940                     if(o[property].substr(0, vlen).toLowerCase() == value){
4941                         data.push(o);
4942                     }
4943                 }
4944             } else if(value.exec){ // regex?
4945                 for(var i = 0, len = ss.length; i < len; i++){
4946                     var o = ss[i];
4947                     if(value.test(o[property])){
4948                         data.push(o);
4949                     }
4950                 }
4951             } else{
4952                 return;
4953             }
4954             this.jsonData = data;
4955             this.refresh();
4956         }
4957     },
4958
4959 /**
4960  * Filter by a function. The passed function will be called with each
4961  * object in the current dataset. If the function returns true the value is kept,
4962  * otherwise it is filtered.
4963  * @param {Function} fn
4964  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4965  */
4966     filterBy : function(fn, scope){
4967         if(this.jsonData){
4968             var data = [];
4969             var ss = this.snapshot;
4970             for(var i = 0, len = ss.length; i < len; i++){
4971                 var o = ss[i];
4972                 if(fn.call(scope || this, o)){
4973                     data.push(o);
4974                 }
4975             }
4976             this.jsonData = data;
4977             this.refresh();
4978         }
4979     },
4980
4981 /**
4982  * Clears the current filter.
4983  */
4984     clearFilter : function(){
4985         if(this.snapshot && this.jsonData != this.snapshot){
4986             this.jsonData = this.snapshot;
4987             this.refresh();
4988         }
4989     },
4990
4991
4992 /**
4993  * Sorts the data for this view and refreshes it.
4994  * @param {String} property A property on your JSON objects to sort on
4995  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4996  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4997  */
4998     sort : function(property, dir, sortType){
4999         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5000         if(this.jsonData){
5001             var p = property;
5002             var dsc = dir && dir.toLowerCase() == "desc";
5003             var f = function(o1, o2){
5004                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5005                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5006                 ;
5007                 if(v1 < v2){
5008                     return dsc ? +1 : -1;
5009                 } else if(v1 > v2){
5010                     return dsc ? -1 : +1;
5011                 } else{
5012                     return 0;
5013                 }
5014             };
5015             this.jsonData.sort(f);
5016             this.refresh();
5017             if(this.jsonData != this.snapshot){
5018                 this.snapshot.sort(f);
5019             }
5020         }
5021     }
5022 });/*
5023  * Based on:
5024  * Ext JS Library 1.1.1
5025  * Copyright(c) 2006-2007, Ext JS, LLC.
5026  *
5027  * Originally Released Under LGPL - original licence link has changed is not relivant.
5028  *
5029  * Fork - LGPL
5030  * <script type="text/javascript">
5031  */
5032  
5033
5034 /**
5035  * @class Roo.ColorPalette
5036  * @extends Roo.Component
5037  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5038  * Here's an example of typical usage:
5039  * <pre><code>
5040 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5041 cp.render('my-div');
5042
5043 cp.on('select', function(palette, selColor){
5044     // do something with selColor
5045 });
5046 </code></pre>
5047  * @constructor
5048  * Create a new ColorPalette
5049  * @param {Object} config The config object
5050  */
5051 Roo.ColorPalette = function(config){
5052     Roo.ColorPalette.superclass.constructor.call(this, config);
5053     this.addEvents({
5054         /**
5055              * @event select
5056              * Fires when a color is selected
5057              * @param {ColorPalette} this
5058              * @param {String} color The 6-digit color hex code (without the # symbol)
5059              */
5060         select: true
5061     });
5062
5063     if(this.handler){
5064         this.on("select", this.handler, this.scope, true);
5065     }
5066 };
5067 Roo.extend(Roo.ColorPalette, Roo.Component, {
5068     /**
5069      * @cfg {String} itemCls
5070      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5071      */
5072     itemCls : "x-color-palette",
5073     /**
5074      * @cfg {String} value
5075      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5076      * the hex codes are case-sensitive.
5077      */
5078     value : null,
5079     clickEvent:'click',
5080     // private
5081     ctype: "Roo.ColorPalette",
5082
5083     /**
5084      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5085      */
5086     allowReselect : false,
5087
5088     /**
5089      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5090      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5091      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5092      * of colors with the width setting until the box is symmetrical.</p>
5093      * <p>You can override individual colors if needed:</p>
5094      * <pre><code>
5095 var cp = new Roo.ColorPalette();
5096 cp.colors[0] = "FF0000";  // change the first box to red
5097 </code></pre>
5098
5099 Or you can provide a custom array of your own for complete control:
5100 <pre><code>
5101 var cp = new Roo.ColorPalette();
5102 cp.colors = ["000000", "993300", "333300"];
5103 </code></pre>
5104      * @type Array
5105      */
5106     colors : [
5107         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5108         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5109         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5110         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5111         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5112     ],
5113
5114     // private
5115     onRender : function(container, position){
5116         var t = new Roo.MasterTemplate(
5117             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5118         );
5119         var c = this.colors;
5120         for(var i = 0, len = c.length; i < len; i++){
5121             t.add([c[i]]);
5122         }
5123         var el = document.createElement("div");
5124         el.className = this.itemCls;
5125         t.overwrite(el);
5126         container.dom.insertBefore(el, position);
5127         this.el = Roo.get(el);
5128         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5129         if(this.clickEvent != 'click'){
5130             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5131         }
5132     },
5133
5134     // private
5135     afterRender : function(){
5136         Roo.ColorPalette.superclass.afterRender.call(this);
5137         if(this.value){
5138             var s = this.value;
5139             this.value = null;
5140             this.select(s);
5141         }
5142     },
5143
5144     // private
5145     handleClick : function(e, t){
5146         e.preventDefault();
5147         if(!this.disabled){
5148             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5149             this.select(c.toUpperCase());
5150         }
5151     },
5152
5153     /**
5154      * Selects the specified color in the palette (fires the select event)
5155      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5156      */
5157     select : function(color){
5158         color = color.replace("#", "");
5159         if(color != this.value || this.allowReselect){
5160             var el = this.el;
5161             if(this.value){
5162                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5163             }
5164             el.child("a.color-"+color).addClass("x-color-palette-sel");
5165             this.value = color;
5166             this.fireEvent("select", this, color);
5167         }
5168     }
5169 });/*
5170  * Based on:
5171  * Ext JS Library 1.1.1
5172  * Copyright(c) 2006-2007, Ext JS, LLC.
5173  *
5174  * Originally Released Under LGPL - original licence link has changed is not relivant.
5175  *
5176  * Fork - LGPL
5177  * <script type="text/javascript">
5178  */
5179  
5180 /**
5181  * @class Roo.DatePicker
5182  * @extends Roo.Component
5183  * Simple date picker class.
5184  * @constructor
5185  * Create a new DatePicker
5186  * @param {Object} config The config object
5187  */
5188 Roo.DatePicker = function(config){
5189     Roo.DatePicker.superclass.constructor.call(this, config);
5190
5191     this.value = config && config.value ?
5192                  config.value.clearTime() : new Date().clearTime();
5193
5194     this.addEvents({
5195         /**
5196              * @event select
5197              * Fires when a date is selected
5198              * @param {DatePicker} this
5199              * @param {Date} date The selected date
5200              */
5201         'select': true,
5202         /**
5203              * @event monthchange
5204              * Fires when the displayed month changes 
5205              * @param {DatePicker} this
5206              * @param {Date} date The selected month
5207              */
5208         'monthchange': true
5209     });
5210
5211     if(this.handler){
5212         this.on("select", this.handler,  this.scope || this);
5213     }
5214     // build the disabledDatesRE
5215     if(!this.disabledDatesRE && this.disabledDates){
5216         var dd = this.disabledDates;
5217         var re = "(?:";
5218         for(var i = 0; i < dd.length; i++){
5219             re += dd[i];
5220             if(i != dd.length-1) {
5221                 re += "|";
5222             }
5223         }
5224         this.disabledDatesRE = new RegExp(re + ")");
5225     }
5226 };
5227
5228 Roo.extend(Roo.DatePicker, Roo.Component, {
5229     /**
5230      * @cfg {String} todayText
5231      * The text to display on the button that selects the current date (defaults to "Today")
5232      */
5233     todayText : "Today",
5234     /**
5235      * @cfg {String} okText
5236      * The text to display on the ok button
5237      */
5238     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5239     /**
5240      * @cfg {String} cancelText
5241      * The text to display on the cancel button
5242      */
5243     cancelText : "Cancel",
5244     /**
5245      * @cfg {String} todayTip
5246      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5247      */
5248     todayTip : "{0} (Spacebar)",
5249     /**
5250      * @cfg {Date} minDate
5251      * Minimum allowable date (JavaScript date object, defaults to null)
5252      */
5253     minDate : null,
5254     /**
5255      * @cfg {Date} maxDate
5256      * Maximum allowable date (JavaScript date object, defaults to null)
5257      */
5258     maxDate : null,
5259     /**
5260      * @cfg {String} minText
5261      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5262      */
5263     minText : "This date is before the minimum date",
5264     /**
5265      * @cfg {String} maxText
5266      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5267      */
5268     maxText : "This date is after the maximum date",
5269     /**
5270      * @cfg {String} format
5271      * The default date format string which can be overriden for localization support.  The format must be
5272      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5273      */
5274     format : "m/d/y",
5275     /**
5276      * @cfg {Array} disabledDays
5277      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5278      */
5279     disabledDays : null,
5280     /**
5281      * @cfg {String} disabledDaysText
5282      * The tooltip to display when the date falls on a disabled day (defaults to "")
5283      */
5284     disabledDaysText : "",
5285     /**
5286      * @cfg {RegExp} disabledDatesRE
5287      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5288      */
5289     disabledDatesRE : null,
5290     /**
5291      * @cfg {String} disabledDatesText
5292      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5293      */
5294     disabledDatesText : "",
5295     /**
5296      * @cfg {Boolean} constrainToViewport
5297      * True to constrain the date picker to the viewport (defaults to true)
5298      */
5299     constrainToViewport : true,
5300     /**
5301      * @cfg {Array} monthNames
5302      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5303      */
5304     monthNames : Date.monthNames,
5305     /**
5306      * @cfg {Array} dayNames
5307      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5308      */
5309     dayNames : Date.dayNames,
5310     /**
5311      * @cfg {String} nextText
5312      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5313      */
5314     nextText: 'Next Month (Control+Right)',
5315     /**
5316      * @cfg {String} prevText
5317      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5318      */
5319     prevText: 'Previous Month (Control+Left)',
5320     /**
5321      * @cfg {String} monthYearText
5322      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5323      */
5324     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5325     /**
5326      * @cfg {Number} startDay
5327      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5328      */
5329     startDay : 0,
5330     /**
5331      * @cfg {Bool} showClear
5332      * Show a clear button (usefull for date form elements that can be blank.)
5333      */
5334     
5335     showClear: false,
5336     
5337     /**
5338      * Sets the value of the date field
5339      * @param {Date} value The date to set
5340      */
5341     setValue : function(value){
5342         var old = this.value;
5343         
5344         if (typeof(value) == 'string') {
5345          
5346             value = Date.parseDate(value, this.format);
5347         }
5348         if (!value) {
5349             value = new Date();
5350         }
5351         
5352         this.value = value.clearTime(true);
5353         if(this.el){
5354             this.update(this.value);
5355         }
5356     },
5357
5358     /**
5359      * Gets the current selected value of the date field
5360      * @return {Date} The selected date
5361      */
5362     getValue : function(){
5363         return this.value;
5364     },
5365
5366     // private
5367     focus : function(){
5368         if(this.el){
5369             this.update(this.activeDate);
5370         }
5371     },
5372
5373     // privateval
5374     onRender : function(container, position){
5375         
5376         var m = [
5377              '<table cellspacing="0">',
5378                 '<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>',
5379                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5380         var dn = this.dayNames;
5381         for(var i = 0; i < 7; i++){
5382             var d = this.startDay+i;
5383             if(d > 6){
5384                 d = d-7;
5385             }
5386             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5387         }
5388         m[m.length] = "</tr></thead><tbody><tr>";
5389         for(var i = 0; i < 42; i++) {
5390             if(i % 7 == 0 && i != 0){
5391                 m[m.length] = "</tr><tr>";
5392             }
5393             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5394         }
5395         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5396             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5397
5398         var el = document.createElement("div");
5399         el.className = "x-date-picker";
5400         el.innerHTML = m.join("");
5401
5402         container.dom.insertBefore(el, position);
5403
5404         this.el = Roo.get(el);
5405         this.eventEl = Roo.get(el.firstChild);
5406
5407         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5408             handler: this.showPrevMonth,
5409             scope: this,
5410             preventDefault:true,
5411             stopDefault:true
5412         });
5413
5414         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5415             handler: this.showNextMonth,
5416             scope: this,
5417             preventDefault:true,
5418             stopDefault:true
5419         });
5420
5421         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5422
5423         this.monthPicker = this.el.down('div.x-date-mp');
5424         this.monthPicker.enableDisplayMode('block');
5425         
5426         var kn = new Roo.KeyNav(this.eventEl, {
5427             "left" : function(e){
5428                 e.ctrlKey ?
5429                     this.showPrevMonth() :
5430                     this.update(this.activeDate.add("d", -1));
5431             },
5432
5433             "right" : function(e){
5434                 e.ctrlKey ?
5435                     this.showNextMonth() :
5436                     this.update(this.activeDate.add("d", 1));
5437             },
5438
5439             "up" : function(e){
5440                 e.ctrlKey ?
5441                     this.showNextYear() :
5442                     this.update(this.activeDate.add("d", -7));
5443             },
5444
5445             "down" : function(e){
5446                 e.ctrlKey ?
5447                     this.showPrevYear() :
5448                     this.update(this.activeDate.add("d", 7));
5449             },
5450
5451             "pageUp" : function(e){
5452                 this.showNextMonth();
5453             },
5454
5455             "pageDown" : function(e){
5456                 this.showPrevMonth();
5457             },
5458
5459             "enter" : function(e){
5460                 e.stopPropagation();
5461                 return true;
5462             },
5463
5464             scope : this
5465         });
5466
5467         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5468
5469         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5470
5471         this.el.unselectable();
5472         
5473         this.cells = this.el.select("table.x-date-inner tbody td");
5474         this.textNodes = this.el.query("table.x-date-inner tbody span");
5475
5476         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5477             text: "&#160;",
5478             tooltip: this.monthYearText
5479         });
5480
5481         this.mbtn.on('click', this.showMonthPicker, this);
5482         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5483
5484
5485         var today = (new Date()).dateFormat(this.format);
5486         
5487         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5488         if (this.showClear) {
5489             baseTb.add( new Roo.Toolbar.Fill());
5490         }
5491         baseTb.add({
5492             text: String.format(this.todayText, today),
5493             tooltip: String.format(this.todayTip, today),
5494             handler: this.selectToday,
5495             scope: this
5496         });
5497         
5498         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5499             
5500         //});
5501         if (this.showClear) {
5502             
5503             baseTb.add( new Roo.Toolbar.Fill());
5504             baseTb.add({
5505                 text: '&#160;',
5506                 cls: 'x-btn-icon x-btn-clear',
5507                 handler: function() {
5508                     //this.value = '';
5509                     this.fireEvent("select", this, '');
5510                 },
5511                 scope: this
5512             });
5513         }
5514         
5515         
5516         if(Roo.isIE){
5517             this.el.repaint();
5518         }
5519         this.update(this.value);
5520     },
5521
5522     createMonthPicker : function(){
5523         if(!this.monthPicker.dom.firstChild){
5524             var buf = ['<table border="0" cellspacing="0">'];
5525             for(var i = 0; i < 6; i++){
5526                 buf.push(
5527                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5528                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5529                     i == 0 ?
5530                     '<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>' :
5531                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5532                 );
5533             }
5534             buf.push(
5535                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5536                     this.okText,
5537                     '</button><button type="button" class="x-date-mp-cancel">',
5538                     this.cancelText,
5539                     '</button></td></tr>',
5540                 '</table>'
5541             );
5542             this.monthPicker.update(buf.join(''));
5543             this.monthPicker.on('click', this.onMonthClick, this);
5544             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5545
5546             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5547             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5548
5549             this.mpMonths.each(function(m, a, i){
5550                 i += 1;
5551                 if((i%2) == 0){
5552                     m.dom.xmonth = 5 + Math.round(i * .5);
5553                 }else{
5554                     m.dom.xmonth = Math.round((i-1) * .5);
5555                 }
5556             });
5557         }
5558     },
5559
5560     showMonthPicker : function(){
5561         this.createMonthPicker();
5562         var size = this.el.getSize();
5563         this.monthPicker.setSize(size);
5564         this.monthPicker.child('table').setSize(size);
5565
5566         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5567         this.updateMPMonth(this.mpSelMonth);
5568         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5569         this.updateMPYear(this.mpSelYear);
5570
5571         this.monthPicker.slideIn('t', {duration:.2});
5572     },
5573
5574     updateMPYear : function(y){
5575         this.mpyear = y;
5576         var ys = this.mpYears.elements;
5577         for(var i = 1; i <= 10; i++){
5578             var td = ys[i-1], y2;
5579             if((i%2) == 0){
5580                 y2 = y + Math.round(i * .5);
5581                 td.firstChild.innerHTML = y2;
5582                 td.xyear = y2;
5583             }else{
5584                 y2 = y - (5-Math.round(i * .5));
5585                 td.firstChild.innerHTML = y2;
5586                 td.xyear = y2;
5587             }
5588             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5589         }
5590     },
5591
5592     updateMPMonth : function(sm){
5593         this.mpMonths.each(function(m, a, i){
5594             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5595         });
5596     },
5597
5598     selectMPMonth: function(m){
5599         
5600     },
5601
5602     onMonthClick : function(e, t){
5603         e.stopEvent();
5604         var el = new Roo.Element(t), pn;
5605         if(el.is('button.x-date-mp-cancel')){
5606             this.hideMonthPicker();
5607         }
5608         else if(el.is('button.x-date-mp-ok')){
5609             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5610             this.hideMonthPicker();
5611         }
5612         else if(pn = el.up('td.x-date-mp-month', 2)){
5613             this.mpMonths.removeClass('x-date-mp-sel');
5614             pn.addClass('x-date-mp-sel');
5615             this.mpSelMonth = pn.dom.xmonth;
5616         }
5617         else if(pn = el.up('td.x-date-mp-year', 2)){
5618             this.mpYears.removeClass('x-date-mp-sel');
5619             pn.addClass('x-date-mp-sel');
5620             this.mpSelYear = pn.dom.xyear;
5621         }
5622         else if(el.is('a.x-date-mp-prev')){
5623             this.updateMPYear(this.mpyear-10);
5624         }
5625         else if(el.is('a.x-date-mp-next')){
5626             this.updateMPYear(this.mpyear+10);
5627         }
5628     },
5629
5630     onMonthDblClick : function(e, t){
5631         e.stopEvent();
5632         var el = new Roo.Element(t), pn;
5633         if(pn = el.up('td.x-date-mp-month', 2)){
5634             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5635             this.hideMonthPicker();
5636         }
5637         else if(pn = el.up('td.x-date-mp-year', 2)){
5638             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5639             this.hideMonthPicker();
5640         }
5641     },
5642
5643     hideMonthPicker : function(disableAnim){
5644         if(this.monthPicker){
5645             if(disableAnim === true){
5646                 this.monthPicker.hide();
5647             }else{
5648                 this.monthPicker.slideOut('t', {duration:.2});
5649             }
5650         }
5651     },
5652
5653     // private
5654     showPrevMonth : function(e){
5655         this.update(this.activeDate.add("mo", -1));
5656     },
5657
5658     // private
5659     showNextMonth : function(e){
5660         this.update(this.activeDate.add("mo", 1));
5661     },
5662
5663     // private
5664     showPrevYear : function(){
5665         this.update(this.activeDate.add("y", -1));
5666     },
5667
5668     // private
5669     showNextYear : function(){
5670         this.update(this.activeDate.add("y", 1));
5671     },
5672
5673     // private
5674     handleMouseWheel : function(e){
5675         var delta = e.getWheelDelta();
5676         if(delta > 0){
5677             this.showPrevMonth();
5678             e.stopEvent();
5679         } else if(delta < 0){
5680             this.showNextMonth();
5681             e.stopEvent();
5682         }
5683     },
5684
5685     // private
5686     handleDateClick : function(e, t){
5687         e.stopEvent();
5688         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5689             this.setValue(new Date(t.dateValue));
5690             this.fireEvent("select", this, this.value);
5691         }
5692     },
5693
5694     // private
5695     selectToday : function(){
5696         this.setValue(new Date().clearTime());
5697         this.fireEvent("select", this, this.value);
5698     },
5699
5700     // private
5701     update : function(date)
5702     {
5703         var vd = this.activeDate;
5704         this.activeDate = date;
5705         if(vd && this.el){
5706             var t = date.getTime();
5707             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5708                 this.cells.removeClass("x-date-selected");
5709                 this.cells.each(function(c){
5710                    if(c.dom.firstChild.dateValue == t){
5711                        c.addClass("x-date-selected");
5712                        setTimeout(function(){
5713                             try{c.dom.firstChild.focus();}catch(e){}
5714                        }, 50);
5715                        return false;
5716                    }
5717                 });
5718                 return;
5719             }
5720         }
5721         
5722         var days = date.getDaysInMonth();
5723         var firstOfMonth = date.getFirstDateOfMonth();
5724         var startingPos = firstOfMonth.getDay()-this.startDay;
5725
5726         if(startingPos <= this.startDay){
5727             startingPos += 7;
5728         }
5729
5730         var pm = date.add("mo", -1);
5731         var prevStart = pm.getDaysInMonth()-startingPos;
5732
5733         var cells = this.cells.elements;
5734         var textEls = this.textNodes;
5735         days += startingPos;
5736
5737         // convert everything to numbers so it's fast
5738         var day = 86400000;
5739         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5740         var today = new Date().clearTime().getTime();
5741         var sel = date.clearTime().getTime();
5742         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5743         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5744         var ddMatch = this.disabledDatesRE;
5745         var ddText = this.disabledDatesText;
5746         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5747         var ddaysText = this.disabledDaysText;
5748         var format = this.format;
5749
5750         var setCellClass = function(cal, cell){
5751             cell.title = "";
5752             var t = d.getTime();
5753             cell.firstChild.dateValue = t;
5754             if(t == today){
5755                 cell.className += " x-date-today";
5756                 cell.title = cal.todayText;
5757             }
5758             if(t == sel){
5759                 cell.className += " x-date-selected";
5760                 setTimeout(function(){
5761                     try{cell.firstChild.focus();}catch(e){}
5762                 }, 50);
5763             }
5764             // disabling
5765             if(t < min) {
5766                 cell.className = " x-date-disabled";
5767                 cell.title = cal.minText;
5768                 return;
5769             }
5770             if(t > max) {
5771                 cell.className = " x-date-disabled";
5772                 cell.title = cal.maxText;
5773                 return;
5774             }
5775             if(ddays){
5776                 if(ddays.indexOf(d.getDay()) != -1){
5777                     cell.title = ddaysText;
5778                     cell.className = " x-date-disabled";
5779                 }
5780             }
5781             if(ddMatch && format){
5782                 var fvalue = d.dateFormat(format);
5783                 if(ddMatch.test(fvalue)){
5784                     cell.title = ddText.replace("%0", fvalue);
5785                     cell.className = " x-date-disabled";
5786                 }
5787             }
5788         };
5789
5790         var i = 0;
5791         for(; i < startingPos; i++) {
5792             textEls[i].innerHTML = (++prevStart);
5793             d.setDate(d.getDate()+1);
5794             cells[i].className = "x-date-prevday";
5795             setCellClass(this, cells[i]);
5796         }
5797         for(; i < days; i++){
5798             intDay = i - startingPos + 1;
5799             textEls[i].innerHTML = (intDay);
5800             d.setDate(d.getDate()+1);
5801             cells[i].className = "x-date-active";
5802             setCellClass(this, cells[i]);
5803         }
5804         var extraDays = 0;
5805         for(; i < 42; i++) {
5806              textEls[i].innerHTML = (++extraDays);
5807              d.setDate(d.getDate()+1);
5808              cells[i].className = "x-date-nextday";
5809              setCellClass(this, cells[i]);
5810         }
5811
5812         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5813         this.fireEvent('monthchange', this, date);
5814         
5815         if(!this.internalRender){
5816             var main = this.el.dom.firstChild;
5817             var w = main.offsetWidth;
5818             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5819             Roo.fly(main).setWidth(w);
5820             this.internalRender = true;
5821             // opera does not respect the auto grow header center column
5822             // then, after it gets a width opera refuses to recalculate
5823             // without a second pass
5824             if(Roo.isOpera && !this.secondPass){
5825                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5826                 this.secondPass = true;
5827                 this.update.defer(10, this, [date]);
5828             }
5829         }
5830         
5831         
5832     }
5833 });        /*
5834  * Based on:
5835  * Ext JS Library 1.1.1
5836  * Copyright(c) 2006-2007, Ext JS, LLC.
5837  *
5838  * Originally Released Under LGPL - original licence link has changed is not relivant.
5839  *
5840  * Fork - LGPL
5841  * <script type="text/javascript">
5842  */
5843 /**
5844  * @class Roo.TabPanel
5845  * @extends Roo.util.Observable
5846  * A lightweight tab container.
5847  * <br><br>
5848  * Usage:
5849  * <pre><code>
5850 // basic tabs 1, built from existing content
5851 var tabs = new Roo.TabPanel("tabs1");
5852 tabs.addTab("script", "View Script");
5853 tabs.addTab("markup", "View Markup");
5854 tabs.activate("script");
5855
5856 // more advanced tabs, built from javascript
5857 var jtabs = new Roo.TabPanel("jtabs");
5858 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5859
5860 // set up the UpdateManager
5861 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5862 var updater = tab2.getUpdateManager();
5863 updater.setDefaultUrl("ajax1.htm");
5864 tab2.on('activate', updater.refresh, updater, true);
5865
5866 // Use setUrl for Ajax loading
5867 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5868 tab3.setUrl("ajax2.htm", null, true);
5869
5870 // Disabled tab
5871 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5872 tab4.disable();
5873
5874 jtabs.activate("jtabs-1");
5875  * </code></pre>
5876  * @constructor
5877  * Create a new TabPanel.
5878  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5879  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5880  */
5881 Roo.TabPanel = function(container, config){
5882     /**
5883     * The container element for this TabPanel.
5884     * @type Roo.Element
5885     */
5886     this.el = Roo.get(container, true);
5887     if(config){
5888         if(typeof config == "boolean"){
5889             this.tabPosition = config ? "bottom" : "top";
5890         }else{
5891             Roo.apply(this, config);
5892         }
5893     }
5894     if(this.tabPosition == "bottom"){
5895         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5896         this.el.addClass("x-tabs-bottom");
5897     }
5898     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5899     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5900     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5901     if(Roo.isIE){
5902         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5903     }
5904     if(this.tabPosition != "bottom"){
5905         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5906          * @type Roo.Element
5907          */
5908         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5909         this.el.addClass("x-tabs-top");
5910     }
5911     this.items = [];
5912
5913     this.bodyEl.setStyle("position", "relative");
5914
5915     this.active = null;
5916     this.activateDelegate = this.activate.createDelegate(this);
5917
5918     this.addEvents({
5919         /**
5920          * @event tabchange
5921          * Fires when the active tab changes
5922          * @param {Roo.TabPanel} this
5923          * @param {Roo.TabPanelItem} activePanel The new active tab
5924          */
5925         "tabchange": true,
5926         /**
5927          * @event beforetabchange
5928          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5929          * @param {Roo.TabPanel} this
5930          * @param {Object} e Set cancel to true on this object to cancel the tab change
5931          * @param {Roo.TabPanelItem} tab The tab being changed to
5932          */
5933         "beforetabchange" : true
5934     });
5935
5936     Roo.EventManager.onWindowResize(this.onResize, this);
5937     this.cpad = this.el.getPadding("lr");
5938     this.hiddenCount = 0;
5939
5940
5941     // toolbar on the tabbar support...
5942     if (this.toolbar) {
5943         var tcfg = this.toolbar;
5944         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5945         this.toolbar = new Roo.Toolbar(tcfg);
5946         if (Roo.isSafari) {
5947             var tbl = tcfg.container.child('table', true);
5948             tbl.setAttribute('width', '100%');
5949         }
5950         
5951     }
5952    
5953
5954
5955     Roo.TabPanel.superclass.constructor.call(this);
5956 };
5957
5958 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5959     /*
5960      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5961      */
5962     tabPosition : "top",
5963     /*
5964      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5965      */
5966     currentTabWidth : 0,
5967     /*
5968      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5969      */
5970     minTabWidth : 40,
5971     /*
5972      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5973      */
5974     maxTabWidth : 250,
5975     /*
5976      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5977      */
5978     preferredTabWidth : 175,
5979     /*
5980      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5981      */
5982     resizeTabs : false,
5983     /*
5984      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5985      */
5986     monitorResize : true,
5987     /*
5988      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5989      */
5990     toolbar : false,
5991
5992     /**
5993      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5994      * @param {String} id The id of the div to use <b>or create</b>
5995      * @param {String} text The text for the tab
5996      * @param {String} content (optional) Content to put in the TabPanelItem body
5997      * @param {Boolean} closable (optional) True to create a close icon on the tab
5998      * @return {Roo.TabPanelItem} The created TabPanelItem
5999      */
6000     addTab : function(id, text, content, closable){
6001         var item = new Roo.TabPanelItem(this, id, text, closable);
6002         this.addTabItem(item);
6003         if(content){
6004             item.setContent(content);
6005         }
6006         return item;
6007     },
6008
6009     /**
6010      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6011      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6012      * @return {Roo.TabPanelItem}
6013      */
6014     getTab : function(id){
6015         return this.items[id];
6016     },
6017
6018     /**
6019      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6020      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6021      */
6022     hideTab : function(id){
6023         var t = this.items[id];
6024         if(!t.isHidden()){
6025            t.setHidden(true);
6026            this.hiddenCount++;
6027            this.autoSizeTabs();
6028         }
6029     },
6030
6031     /**
6032      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6033      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6034      */
6035     unhideTab : function(id){
6036         var t = this.items[id];
6037         if(t.isHidden()){
6038            t.setHidden(false);
6039            this.hiddenCount--;
6040            this.autoSizeTabs();
6041         }
6042     },
6043
6044     /**
6045      * Adds an existing {@link Roo.TabPanelItem}.
6046      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6047      */
6048     addTabItem : function(item){
6049         this.items[item.id] = item;
6050         this.items.push(item);
6051         if(this.resizeTabs){
6052            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6053            this.autoSizeTabs();
6054         }else{
6055             item.autoSize();
6056         }
6057     },
6058
6059     /**
6060      * Removes a {@link Roo.TabPanelItem}.
6061      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6062      */
6063     removeTab : function(id){
6064         var items = this.items;
6065         var tab = items[id];
6066         if(!tab) { return; }
6067         var index = items.indexOf(tab);
6068         if(this.active == tab && items.length > 1){
6069             var newTab = this.getNextAvailable(index);
6070             if(newTab) {
6071                 newTab.activate();
6072             }
6073         }
6074         this.stripEl.dom.removeChild(tab.pnode.dom);
6075         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6076             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6077         }
6078         items.splice(index, 1);
6079         delete this.items[tab.id];
6080         tab.fireEvent("close", tab);
6081         tab.purgeListeners();
6082         this.autoSizeTabs();
6083     },
6084
6085     getNextAvailable : function(start){
6086         var items = this.items;
6087         var index = start;
6088         // look for a next tab that will slide over to
6089         // replace the one being removed
6090         while(index < items.length){
6091             var item = items[++index];
6092             if(item && !item.isHidden()){
6093                 return item;
6094             }
6095         }
6096         // if one isn't found select the previous tab (on the left)
6097         index = start;
6098         while(index >= 0){
6099             var item = items[--index];
6100             if(item && !item.isHidden()){
6101                 return item;
6102             }
6103         }
6104         return null;
6105     },
6106
6107     /**
6108      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6109      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6110      */
6111     disableTab : function(id){
6112         var tab = this.items[id];
6113         if(tab && this.active != tab){
6114             tab.disable();
6115         }
6116     },
6117
6118     /**
6119      * Enables a {@link Roo.TabPanelItem} that is disabled.
6120      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6121      */
6122     enableTab : function(id){
6123         var tab = this.items[id];
6124         tab.enable();
6125     },
6126
6127     /**
6128      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6129      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6130      * @return {Roo.TabPanelItem} The TabPanelItem.
6131      */
6132     activate : function(id){
6133         var tab = this.items[id];
6134         if(!tab){
6135             return null;
6136         }
6137         if(tab == this.active || tab.disabled){
6138             return tab;
6139         }
6140         var e = {};
6141         this.fireEvent("beforetabchange", this, e, tab);
6142         if(e.cancel !== true && !tab.disabled){
6143             if(this.active){
6144                 this.active.hide();
6145             }
6146             this.active = this.items[id];
6147             this.active.show();
6148             this.fireEvent("tabchange", this, this.active);
6149         }
6150         return tab;
6151     },
6152
6153     /**
6154      * Gets the active {@link Roo.TabPanelItem}.
6155      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6156      */
6157     getActiveTab : function(){
6158         return this.active;
6159     },
6160
6161     /**
6162      * Updates the tab body element to fit the height of the container element
6163      * for overflow scrolling
6164      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6165      */
6166     syncHeight : function(targetHeight){
6167         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6168         var bm = this.bodyEl.getMargins();
6169         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6170         this.bodyEl.setHeight(newHeight);
6171         return newHeight;
6172     },
6173
6174     onResize : function(){
6175         if(this.monitorResize){
6176             this.autoSizeTabs();
6177         }
6178     },
6179
6180     /**
6181      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6182      */
6183     beginUpdate : function(){
6184         this.updating = true;
6185     },
6186
6187     /**
6188      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6189      */
6190     endUpdate : function(){
6191         this.updating = false;
6192         this.autoSizeTabs();
6193     },
6194
6195     /**
6196      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6197      */
6198     autoSizeTabs : function(){
6199         var count = this.items.length;
6200         var vcount = count - this.hiddenCount;
6201         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6202             return;
6203         }
6204         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6205         var availWidth = Math.floor(w / vcount);
6206         var b = this.stripBody;
6207         if(b.getWidth() > w){
6208             var tabs = this.items;
6209             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6210             if(availWidth < this.minTabWidth){
6211                 /*if(!this.sleft){    // incomplete scrolling code
6212                     this.createScrollButtons();
6213                 }
6214                 this.showScroll();
6215                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6216             }
6217         }else{
6218             if(this.currentTabWidth < this.preferredTabWidth){
6219                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6220             }
6221         }
6222     },
6223
6224     /**
6225      * Returns the number of tabs in this TabPanel.
6226      * @return {Number}
6227      */
6228      getCount : function(){
6229          return this.items.length;
6230      },
6231
6232     /**
6233      * Resizes all the tabs to the passed width
6234      * @param {Number} The new width
6235      */
6236     setTabWidth : function(width){
6237         this.currentTabWidth = width;
6238         for(var i = 0, len = this.items.length; i < len; i++) {
6239                 if(!this.items[i].isHidden()) {
6240                 this.items[i].setWidth(width);
6241             }
6242         }
6243     },
6244
6245     /**
6246      * Destroys this TabPanel
6247      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6248      */
6249     destroy : function(removeEl){
6250         Roo.EventManager.removeResizeListener(this.onResize, this);
6251         for(var i = 0, len = this.items.length; i < len; i++){
6252             this.items[i].purgeListeners();
6253         }
6254         if(removeEl === true){
6255             this.el.update("");
6256             this.el.remove();
6257         }
6258     }
6259 });
6260
6261 /**
6262  * @class Roo.TabPanelItem
6263  * @extends Roo.util.Observable
6264  * Represents an individual item (tab plus body) in a TabPanel.
6265  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6266  * @param {String} id The id of this TabPanelItem
6267  * @param {String} text The text for the tab of this TabPanelItem
6268  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6269  */
6270 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6271     /**
6272      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6273      * @type Roo.TabPanel
6274      */
6275     this.tabPanel = tabPanel;
6276     /**
6277      * The id for this TabPanelItem
6278      * @type String
6279      */
6280     this.id = id;
6281     /** @private */
6282     this.disabled = false;
6283     /** @private */
6284     this.text = text;
6285     /** @private */
6286     this.loaded = false;
6287     this.closable = closable;
6288
6289     /**
6290      * The body element for this TabPanelItem.
6291      * @type Roo.Element
6292      */
6293     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6294     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6295     this.bodyEl.setStyle("display", "block");
6296     this.bodyEl.setStyle("zoom", "1");
6297     this.hideAction();
6298
6299     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6300     /** @private */
6301     this.el = Roo.get(els.el, true);
6302     this.inner = Roo.get(els.inner, true);
6303     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6304     this.pnode = Roo.get(els.el.parentNode, true);
6305     this.el.on("mousedown", this.onTabMouseDown, this);
6306     this.el.on("click", this.onTabClick, this);
6307     /** @private */
6308     if(closable){
6309         var c = Roo.get(els.close, true);
6310         c.dom.title = this.closeText;
6311         c.addClassOnOver("close-over");
6312         c.on("click", this.closeClick, this);
6313      }
6314
6315     this.addEvents({
6316          /**
6317          * @event activate
6318          * Fires when this tab becomes the active tab.
6319          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6320          * @param {Roo.TabPanelItem} this
6321          */
6322         "activate": true,
6323         /**
6324          * @event beforeclose
6325          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6326          * @param {Roo.TabPanelItem} this
6327          * @param {Object} e Set cancel to true on this object to cancel the close.
6328          */
6329         "beforeclose": true,
6330         /**
6331          * @event close
6332          * Fires when this tab is closed.
6333          * @param {Roo.TabPanelItem} this
6334          */
6335          "close": true,
6336         /**
6337          * @event deactivate
6338          * Fires when this tab is no longer the active tab.
6339          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6340          * @param {Roo.TabPanelItem} this
6341          */
6342          "deactivate" : true
6343     });
6344     this.hidden = false;
6345
6346     Roo.TabPanelItem.superclass.constructor.call(this);
6347 };
6348
6349 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6350     purgeListeners : function(){
6351        Roo.util.Observable.prototype.purgeListeners.call(this);
6352        this.el.removeAllListeners();
6353     },
6354     /**
6355      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6356      */
6357     show : function(){
6358         this.pnode.addClass("on");
6359         this.showAction();
6360         if(Roo.isOpera){
6361             this.tabPanel.stripWrap.repaint();
6362         }
6363         this.fireEvent("activate", this.tabPanel, this);
6364     },
6365
6366     /**
6367      * Returns true if this tab is the active tab.
6368      * @return {Boolean}
6369      */
6370     isActive : function(){
6371         return this.tabPanel.getActiveTab() == this;
6372     },
6373
6374     /**
6375      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6376      */
6377     hide : function(){
6378         this.pnode.removeClass("on");
6379         this.hideAction();
6380         this.fireEvent("deactivate", this.tabPanel, this);
6381     },
6382
6383     hideAction : function(){
6384         this.bodyEl.hide();
6385         this.bodyEl.setStyle("position", "absolute");
6386         this.bodyEl.setLeft("-20000px");
6387         this.bodyEl.setTop("-20000px");
6388     },
6389
6390     showAction : function(){
6391         this.bodyEl.setStyle("position", "relative");
6392         this.bodyEl.setTop("");
6393         this.bodyEl.setLeft("");
6394         this.bodyEl.show();
6395     },
6396
6397     /**
6398      * Set the tooltip for the tab.
6399      * @param {String} tooltip The tab's tooltip
6400      */
6401     setTooltip : function(text){
6402         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6403             this.textEl.dom.qtip = text;
6404             this.textEl.dom.removeAttribute('title');
6405         }else{
6406             this.textEl.dom.title = text;
6407         }
6408     },
6409
6410     onTabClick : function(e){
6411         e.preventDefault();
6412         this.tabPanel.activate(this.id);
6413     },
6414
6415     onTabMouseDown : function(e){
6416         e.preventDefault();
6417         this.tabPanel.activate(this.id);
6418     },
6419
6420     getWidth : function(){
6421         return this.inner.getWidth();
6422     },
6423
6424     setWidth : function(width){
6425         var iwidth = width - this.pnode.getPadding("lr");
6426         this.inner.setWidth(iwidth);
6427         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6428         this.pnode.setWidth(width);
6429     },
6430
6431     /**
6432      * Show or hide the tab
6433      * @param {Boolean} hidden True to hide or false to show.
6434      */
6435     setHidden : function(hidden){
6436         this.hidden = hidden;
6437         this.pnode.setStyle("display", hidden ? "none" : "");
6438     },
6439
6440     /**
6441      * Returns true if this tab is "hidden"
6442      * @return {Boolean}
6443      */
6444     isHidden : function(){
6445         return this.hidden;
6446     },
6447
6448     /**
6449      * Returns the text for this tab
6450      * @return {String}
6451      */
6452     getText : function(){
6453         return this.text;
6454     },
6455
6456     autoSize : function(){
6457         //this.el.beginMeasure();
6458         this.textEl.setWidth(1);
6459         /*
6460          *  #2804 [new] Tabs in Roojs
6461          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6462          */
6463         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6464         //this.el.endMeasure();
6465     },
6466
6467     /**
6468      * Sets the text for the tab (Note: this also sets the tooltip text)
6469      * @param {String} text The tab's text and tooltip
6470      */
6471     setText : function(text){
6472         this.text = text;
6473         this.textEl.update(text);
6474         this.setTooltip(text);
6475         if(!this.tabPanel.resizeTabs){
6476             this.autoSize();
6477         }
6478     },
6479     /**
6480      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6481      */
6482     activate : function(){
6483         this.tabPanel.activate(this.id);
6484     },
6485
6486     /**
6487      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6488      */
6489     disable : function(){
6490         if(this.tabPanel.active != this){
6491             this.disabled = true;
6492             this.pnode.addClass("disabled");
6493         }
6494     },
6495
6496     /**
6497      * Enables this TabPanelItem if it was previously disabled.
6498      */
6499     enable : function(){
6500         this.disabled = false;
6501         this.pnode.removeClass("disabled");
6502     },
6503
6504     /**
6505      * Sets the content for this TabPanelItem.
6506      * @param {String} content The content
6507      * @param {Boolean} loadScripts true to look for and load scripts
6508      */
6509     setContent : function(content, loadScripts){
6510         this.bodyEl.update(content, loadScripts);
6511     },
6512
6513     /**
6514      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6515      * @return {Roo.UpdateManager} The UpdateManager
6516      */
6517     getUpdateManager : function(){
6518         return this.bodyEl.getUpdateManager();
6519     },
6520
6521     /**
6522      * Set a URL to be used to load the content for this TabPanelItem.
6523      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6524      * @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)
6525      * @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)
6526      * @return {Roo.UpdateManager} The UpdateManager
6527      */
6528     setUrl : function(url, params, loadOnce){
6529         if(this.refreshDelegate){
6530             this.un('activate', this.refreshDelegate);
6531         }
6532         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6533         this.on("activate", this.refreshDelegate);
6534         return this.bodyEl.getUpdateManager();
6535     },
6536
6537     /** @private */
6538     _handleRefresh : function(url, params, loadOnce){
6539         if(!loadOnce || !this.loaded){
6540             var updater = this.bodyEl.getUpdateManager();
6541             updater.update(url, params, this._setLoaded.createDelegate(this));
6542         }
6543     },
6544
6545     /**
6546      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6547      *   Will fail silently if the setUrl method has not been called.
6548      *   This does not activate the panel, just updates its content.
6549      */
6550     refresh : function(){
6551         if(this.refreshDelegate){
6552            this.loaded = false;
6553            this.refreshDelegate();
6554         }
6555     },
6556
6557     /** @private */
6558     _setLoaded : function(){
6559         this.loaded = true;
6560     },
6561
6562     /** @private */
6563     closeClick : function(e){
6564         var o = {};
6565         e.stopEvent();
6566         this.fireEvent("beforeclose", this, o);
6567         if(o.cancel !== true){
6568             this.tabPanel.removeTab(this.id);
6569         }
6570     },
6571     /**
6572      * The text displayed in the tooltip for the close icon.
6573      * @type String
6574      */
6575     closeText : "Close this tab"
6576 });
6577
6578 /** @private */
6579 Roo.TabPanel.prototype.createStrip = function(container){
6580     var strip = document.createElement("div");
6581     strip.className = "x-tabs-wrap";
6582     container.appendChild(strip);
6583     return strip;
6584 };
6585 /** @private */
6586 Roo.TabPanel.prototype.createStripList = function(strip){
6587     // div wrapper for retard IE
6588     // returns the "tr" element.
6589     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6590         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6591         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6592     return strip.firstChild.firstChild.firstChild.firstChild;
6593 };
6594 /** @private */
6595 Roo.TabPanel.prototype.createBody = function(container){
6596     var body = document.createElement("div");
6597     Roo.id(body, "tab-body");
6598     Roo.fly(body).addClass("x-tabs-body");
6599     container.appendChild(body);
6600     return body;
6601 };
6602 /** @private */
6603 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6604     var body = Roo.getDom(id);
6605     if(!body){
6606         body = document.createElement("div");
6607         body.id = id;
6608     }
6609     Roo.fly(body).addClass("x-tabs-item-body");
6610     bodyEl.insertBefore(body, bodyEl.firstChild);
6611     return body;
6612 };
6613 /** @private */
6614 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6615     var td = document.createElement("td");
6616     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6617     //stripEl.appendChild(td);
6618     if(closable){
6619         td.className = "x-tabs-closable";
6620         if(!this.closeTpl){
6621             this.closeTpl = new Roo.Template(
6622                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6623                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6624                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6625             );
6626         }
6627         var el = this.closeTpl.overwrite(td, {"text": text});
6628         var close = el.getElementsByTagName("div")[0];
6629         var inner = el.getElementsByTagName("em")[0];
6630         return {"el": el, "close": close, "inner": inner};
6631     } else {
6632         if(!this.tabTpl){
6633             this.tabTpl = new Roo.Template(
6634                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6635                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6636             );
6637         }
6638         var el = this.tabTpl.overwrite(td, {"text": text});
6639         var inner = el.getElementsByTagName("em")[0];
6640         return {"el": el, "inner": inner};
6641     }
6642 };/*
6643  * Based on:
6644  * Ext JS Library 1.1.1
6645  * Copyright(c) 2006-2007, Ext JS, LLC.
6646  *
6647  * Originally Released Under LGPL - original licence link has changed is not relivant.
6648  *
6649  * Fork - LGPL
6650  * <script type="text/javascript">
6651  */
6652
6653 /**
6654  * @class Roo.Button
6655  * @extends Roo.util.Observable
6656  * Simple Button class
6657  * @cfg {String} text The button text
6658  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6659  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6660  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6661  * @cfg {Object} scope The scope of the handler
6662  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6663  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6664  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6665  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6666  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6667  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6668    applies if enableToggle = true)
6669  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6670  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6671   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6672  * @constructor
6673  * Create a new button
6674  * @param {Object} config The config object
6675  */
6676 Roo.Button = function(renderTo, config)
6677 {
6678     if (!config) {
6679         config = renderTo;
6680         renderTo = config.renderTo || false;
6681     }
6682     
6683     Roo.apply(this, config);
6684     this.addEvents({
6685         /**
6686              * @event click
6687              * Fires when this button is clicked
6688              * @param {Button} this
6689              * @param {EventObject} e The click event
6690              */
6691             "click" : true,
6692         /**
6693              * @event toggle
6694              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6695              * @param {Button} this
6696              * @param {Boolean} pressed
6697              */
6698             "toggle" : true,
6699         /**
6700              * @event mouseover
6701              * Fires when the mouse hovers over the button
6702              * @param {Button} this
6703              * @param {Event} e The event object
6704              */
6705         'mouseover' : true,
6706         /**
6707              * @event mouseout
6708              * Fires when the mouse exits the button
6709              * @param {Button} this
6710              * @param {Event} e The event object
6711              */
6712         'mouseout': true,
6713          /**
6714              * @event render
6715              * Fires when the button is rendered
6716              * @param {Button} this
6717              */
6718         'render': true
6719     });
6720     if(this.menu){
6721         this.menu = Roo.menu.MenuMgr.get(this.menu);
6722     }
6723     // register listeners first!!  - so render can be captured..
6724     Roo.util.Observable.call(this);
6725     if(renderTo){
6726         this.render(renderTo);
6727     }
6728     
6729   
6730 };
6731
6732 Roo.extend(Roo.Button, Roo.util.Observable, {
6733     /**
6734      * 
6735      */
6736     
6737     /**
6738      * Read-only. True if this button is hidden
6739      * @type Boolean
6740      */
6741     hidden : false,
6742     /**
6743      * Read-only. True if this button is disabled
6744      * @type Boolean
6745      */
6746     disabled : false,
6747     /**
6748      * Read-only. True if this button is pressed (only if enableToggle = true)
6749      * @type Boolean
6750      */
6751     pressed : false,
6752
6753     /**
6754      * @cfg {Number} tabIndex 
6755      * The DOM tabIndex for this button (defaults to undefined)
6756      */
6757     tabIndex : undefined,
6758
6759     /**
6760      * @cfg {Boolean} enableToggle
6761      * True to enable pressed/not pressed toggling (defaults to false)
6762      */
6763     enableToggle: false,
6764     /**
6765      * @cfg {Mixed} menu
6766      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6767      */
6768     menu : undefined,
6769     /**
6770      * @cfg {String} menuAlign
6771      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6772      */
6773     menuAlign : "tl-bl?",
6774
6775     /**
6776      * @cfg {String} iconCls
6777      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6778      */
6779     iconCls : undefined,
6780     /**
6781      * @cfg {String} type
6782      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6783      */
6784     type : 'button',
6785
6786     // private
6787     menuClassTarget: 'tr',
6788
6789     /**
6790      * @cfg {String} clickEvent
6791      * The type of event to map to the button's event handler (defaults to 'click')
6792      */
6793     clickEvent : 'click',
6794
6795     /**
6796      * @cfg {Boolean} handleMouseEvents
6797      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6798      */
6799     handleMouseEvents : true,
6800
6801     /**
6802      * @cfg {String} tooltipType
6803      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6804      */
6805     tooltipType : 'qtip',
6806
6807     /**
6808      * @cfg {String} cls
6809      * A CSS class to apply to the button's main element.
6810      */
6811     
6812     /**
6813      * @cfg {Roo.Template} template (Optional)
6814      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6815      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6816      * require code modifications if required elements (e.g. a button) aren't present.
6817      */
6818
6819     // private
6820     render : function(renderTo){
6821         var btn;
6822         if(this.hideParent){
6823             this.parentEl = Roo.get(renderTo);
6824         }
6825         if(!this.dhconfig){
6826             if(!this.template){
6827                 if(!Roo.Button.buttonTemplate){
6828                     // hideous table template
6829                     Roo.Button.buttonTemplate = new Roo.Template(
6830                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6831                         '<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>',
6832                         "</tr></tbody></table>");
6833                 }
6834                 this.template = Roo.Button.buttonTemplate;
6835             }
6836             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6837             var btnEl = btn.child("button:first");
6838             btnEl.on('focus', this.onFocus, this);
6839             btnEl.on('blur', this.onBlur, this);
6840             if(this.cls){
6841                 btn.addClass(this.cls);
6842             }
6843             if(this.icon){
6844                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6845             }
6846             if(this.iconCls){
6847                 btnEl.addClass(this.iconCls);
6848                 if(!this.cls){
6849                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6850                 }
6851             }
6852             if(this.tabIndex !== undefined){
6853                 btnEl.dom.tabIndex = this.tabIndex;
6854             }
6855             if(this.tooltip){
6856                 if(typeof this.tooltip == 'object'){
6857                     Roo.QuickTips.tips(Roo.apply({
6858                           target: btnEl.id
6859                     }, this.tooltip));
6860                 } else {
6861                     btnEl.dom[this.tooltipType] = this.tooltip;
6862                 }
6863             }
6864         }else{
6865             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6866         }
6867         this.el = btn;
6868         if(this.id){
6869             this.el.dom.id = this.el.id = this.id;
6870         }
6871         if(this.menu){
6872             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6873             this.menu.on("show", this.onMenuShow, this);
6874             this.menu.on("hide", this.onMenuHide, this);
6875         }
6876         btn.addClass("x-btn");
6877         if(Roo.isIE && !Roo.isIE7){
6878             this.autoWidth.defer(1, this);
6879         }else{
6880             this.autoWidth();
6881         }
6882         if(this.handleMouseEvents){
6883             btn.on("mouseover", this.onMouseOver, this);
6884             btn.on("mouseout", this.onMouseOut, this);
6885             btn.on("mousedown", this.onMouseDown, this);
6886         }
6887         btn.on(this.clickEvent, this.onClick, this);
6888         //btn.on("mouseup", this.onMouseUp, this);
6889         if(this.hidden){
6890             this.hide();
6891         }
6892         if(this.disabled){
6893             this.disable();
6894         }
6895         Roo.ButtonToggleMgr.register(this);
6896         if(this.pressed){
6897             this.el.addClass("x-btn-pressed");
6898         }
6899         if(this.repeat){
6900             var repeater = new Roo.util.ClickRepeater(btn,
6901                 typeof this.repeat == "object" ? this.repeat : {}
6902             );
6903             repeater.on("click", this.onClick,  this);
6904         }
6905         
6906         this.fireEvent('render', this);
6907         
6908     },
6909     /**
6910      * Returns the button's underlying element
6911      * @return {Roo.Element} The element
6912      */
6913     getEl : function(){
6914         return this.el;  
6915     },
6916     
6917     /**
6918      * Destroys this Button and removes any listeners.
6919      */
6920     destroy : function(){
6921         Roo.ButtonToggleMgr.unregister(this);
6922         this.el.removeAllListeners();
6923         this.purgeListeners();
6924         this.el.remove();
6925     },
6926
6927     // private
6928     autoWidth : function(){
6929         if(this.el){
6930             this.el.setWidth("auto");
6931             if(Roo.isIE7 && Roo.isStrict){
6932                 var ib = this.el.child('button');
6933                 if(ib && ib.getWidth() > 20){
6934                     ib.clip();
6935                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6936                 }
6937             }
6938             if(this.minWidth){
6939                 if(this.hidden){
6940                     this.el.beginMeasure();
6941                 }
6942                 if(this.el.getWidth() < this.minWidth){
6943                     this.el.setWidth(this.minWidth);
6944                 }
6945                 if(this.hidden){
6946                     this.el.endMeasure();
6947                 }
6948             }
6949         }
6950     },
6951
6952     /**
6953      * Assigns this button's click handler
6954      * @param {Function} handler The function to call when the button is clicked
6955      * @param {Object} scope (optional) Scope for the function passed in
6956      */
6957     setHandler : function(handler, scope){
6958         this.handler = handler;
6959         this.scope = scope;  
6960     },
6961     
6962     /**
6963      * Sets this button's text
6964      * @param {String} text The button text
6965      */
6966     setText : function(text){
6967         this.text = text;
6968         if(this.el){
6969             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6970         }
6971         this.autoWidth();
6972     },
6973     
6974     /**
6975      * Gets the text for this button
6976      * @return {String} The button text
6977      */
6978     getText : function(){
6979         return this.text;  
6980     },
6981     
6982     /**
6983      * Show this button
6984      */
6985     show: function(){
6986         this.hidden = false;
6987         if(this.el){
6988             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6989         }
6990     },
6991     
6992     /**
6993      * Hide this button
6994      */
6995     hide: function(){
6996         this.hidden = true;
6997         if(this.el){
6998             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6999         }
7000     },
7001     
7002     /**
7003      * Convenience function for boolean show/hide
7004      * @param {Boolean} visible True to show, false to hide
7005      */
7006     setVisible: function(visible){
7007         if(visible) {
7008             this.show();
7009         }else{
7010             this.hide();
7011         }
7012     },
7013     
7014     /**
7015      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7016      * @param {Boolean} state (optional) Force a particular state
7017      */
7018     toggle : function(state){
7019         state = state === undefined ? !this.pressed : state;
7020         if(state != this.pressed){
7021             if(state){
7022                 this.el.addClass("x-btn-pressed");
7023                 this.pressed = true;
7024                 this.fireEvent("toggle", this, true);
7025             }else{
7026                 this.el.removeClass("x-btn-pressed");
7027                 this.pressed = false;
7028                 this.fireEvent("toggle", this, false);
7029             }
7030             if(this.toggleHandler){
7031                 this.toggleHandler.call(this.scope || this, this, state);
7032             }
7033         }
7034     },
7035     
7036     /**
7037      * Focus the button
7038      */
7039     focus : function(){
7040         this.el.child('button:first').focus();
7041     },
7042     
7043     /**
7044      * Disable this button
7045      */
7046     disable : function(){
7047         if(this.el){
7048             this.el.addClass("x-btn-disabled");
7049         }
7050         this.disabled = true;
7051     },
7052     
7053     /**
7054      * Enable this button
7055      */
7056     enable : function(){
7057         if(this.el){
7058             this.el.removeClass("x-btn-disabled");
7059         }
7060         this.disabled = false;
7061     },
7062
7063     /**
7064      * Convenience function for boolean enable/disable
7065      * @param {Boolean} enabled True to enable, false to disable
7066      */
7067     setDisabled : function(v){
7068         this[v !== true ? "enable" : "disable"]();
7069     },
7070
7071     // private
7072     onClick : function(e)
7073     {
7074         if(e){
7075             e.preventDefault();
7076         }
7077         if(e.button != 0){
7078             return;
7079         }
7080         if(!this.disabled){
7081             if(this.enableToggle){
7082                 this.toggle();
7083             }
7084             if(this.menu && !this.menu.isVisible()){
7085                 this.menu.show(this.el, this.menuAlign);
7086             }
7087             this.fireEvent("click", this, e);
7088             if(this.handler){
7089                 this.el.removeClass("x-btn-over");
7090                 this.handler.call(this.scope || this, this, e);
7091             }
7092         }
7093     },
7094     // private
7095     onMouseOver : function(e){
7096         if(!this.disabled){
7097             this.el.addClass("x-btn-over");
7098             this.fireEvent('mouseover', this, e);
7099         }
7100     },
7101     // private
7102     onMouseOut : function(e){
7103         if(!e.within(this.el,  true)){
7104             this.el.removeClass("x-btn-over");
7105             this.fireEvent('mouseout', this, e);
7106         }
7107     },
7108     // private
7109     onFocus : function(e){
7110         if(!this.disabled){
7111             this.el.addClass("x-btn-focus");
7112         }
7113     },
7114     // private
7115     onBlur : function(e){
7116         this.el.removeClass("x-btn-focus");
7117     },
7118     // private
7119     onMouseDown : function(e){
7120         if(!this.disabled && e.button == 0){
7121             this.el.addClass("x-btn-click");
7122             Roo.get(document).on('mouseup', this.onMouseUp, this);
7123         }
7124     },
7125     // private
7126     onMouseUp : function(e){
7127         if(e.button == 0){
7128             this.el.removeClass("x-btn-click");
7129             Roo.get(document).un('mouseup', this.onMouseUp, this);
7130         }
7131     },
7132     // private
7133     onMenuShow : function(e){
7134         this.el.addClass("x-btn-menu-active");
7135     },
7136     // private
7137     onMenuHide : function(e){
7138         this.el.removeClass("x-btn-menu-active");
7139     }   
7140 });
7141
7142 // Private utility class used by Button
7143 Roo.ButtonToggleMgr = function(){
7144    var groups = {};
7145    
7146    function toggleGroup(btn, state){
7147        if(state){
7148            var g = groups[btn.toggleGroup];
7149            for(var i = 0, l = g.length; i < l; i++){
7150                if(g[i] != btn){
7151                    g[i].toggle(false);
7152                }
7153            }
7154        }
7155    }
7156    
7157    return {
7158        register : function(btn){
7159            if(!btn.toggleGroup){
7160                return;
7161            }
7162            var g = groups[btn.toggleGroup];
7163            if(!g){
7164                g = groups[btn.toggleGroup] = [];
7165            }
7166            g.push(btn);
7167            btn.on("toggle", toggleGroup);
7168        },
7169        
7170        unregister : function(btn){
7171            if(!btn.toggleGroup){
7172                return;
7173            }
7174            var g = groups[btn.toggleGroup];
7175            if(g){
7176                g.remove(btn);
7177                btn.un("toggle", toggleGroup);
7178            }
7179        }
7180    };
7181 }();/*
7182  * Based on:
7183  * Ext JS Library 1.1.1
7184  * Copyright(c) 2006-2007, Ext JS, LLC.
7185  *
7186  * Originally Released Under LGPL - original licence link has changed is not relivant.
7187  *
7188  * Fork - LGPL
7189  * <script type="text/javascript">
7190  */
7191  
7192 /**
7193  * @class Roo.SplitButton
7194  * @extends Roo.Button
7195  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7196  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7197  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7198  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7199  * @cfg {String} arrowTooltip The title attribute of the arrow
7200  * @constructor
7201  * Create a new menu button
7202  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7203  * @param {Object} config The config object
7204  */
7205 Roo.SplitButton = function(renderTo, config){
7206     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7207     /**
7208      * @event arrowclick
7209      * Fires when this button's arrow is clicked
7210      * @param {SplitButton} this
7211      * @param {EventObject} e The click event
7212      */
7213     this.addEvents({"arrowclick":true});
7214 };
7215
7216 Roo.extend(Roo.SplitButton, Roo.Button, {
7217     render : function(renderTo){
7218         // this is one sweet looking template!
7219         var tpl = new Roo.Template(
7220             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7221             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7222             '<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>',
7223             "</tbody></table></td><td>",
7224             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7225             '<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>',
7226             "</tbody></table></td></tr></table>"
7227         );
7228         var btn = tpl.append(renderTo, [this.text, this.type], true);
7229         var btnEl = btn.child("button");
7230         if(this.cls){
7231             btn.addClass(this.cls);
7232         }
7233         if(this.icon){
7234             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7235         }
7236         if(this.iconCls){
7237             btnEl.addClass(this.iconCls);
7238             if(!this.cls){
7239                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7240             }
7241         }
7242         this.el = btn;
7243         if(this.handleMouseEvents){
7244             btn.on("mouseover", this.onMouseOver, this);
7245             btn.on("mouseout", this.onMouseOut, this);
7246             btn.on("mousedown", this.onMouseDown, this);
7247             btn.on("mouseup", this.onMouseUp, this);
7248         }
7249         btn.on(this.clickEvent, this.onClick, this);
7250         if(this.tooltip){
7251             if(typeof this.tooltip == 'object'){
7252                 Roo.QuickTips.tips(Roo.apply({
7253                       target: btnEl.id
7254                 }, this.tooltip));
7255             } else {
7256                 btnEl.dom[this.tooltipType] = this.tooltip;
7257             }
7258         }
7259         if(this.arrowTooltip){
7260             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7261         }
7262         if(this.hidden){
7263             this.hide();
7264         }
7265         if(this.disabled){
7266             this.disable();
7267         }
7268         if(this.pressed){
7269             this.el.addClass("x-btn-pressed");
7270         }
7271         if(Roo.isIE && !Roo.isIE7){
7272             this.autoWidth.defer(1, this);
7273         }else{
7274             this.autoWidth();
7275         }
7276         if(this.menu){
7277             this.menu.on("show", this.onMenuShow, this);
7278             this.menu.on("hide", this.onMenuHide, this);
7279         }
7280         this.fireEvent('render', this);
7281     },
7282
7283     // private
7284     autoWidth : function(){
7285         if(this.el){
7286             var tbl = this.el.child("table:first");
7287             var tbl2 = this.el.child("table:last");
7288             this.el.setWidth("auto");
7289             tbl.setWidth("auto");
7290             if(Roo.isIE7 && Roo.isStrict){
7291                 var ib = this.el.child('button:first');
7292                 if(ib && ib.getWidth() > 20){
7293                     ib.clip();
7294                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7295                 }
7296             }
7297             if(this.minWidth){
7298                 if(this.hidden){
7299                     this.el.beginMeasure();
7300                 }
7301                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7302                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7303                 }
7304                 if(this.hidden){
7305                     this.el.endMeasure();
7306                 }
7307             }
7308             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7309         } 
7310     },
7311     /**
7312      * Sets this button's click handler
7313      * @param {Function} handler The function to call when the button is clicked
7314      * @param {Object} scope (optional) Scope for the function passed above
7315      */
7316     setHandler : function(handler, scope){
7317         this.handler = handler;
7318         this.scope = scope;  
7319     },
7320     
7321     /**
7322      * Sets this button's arrow click handler
7323      * @param {Function} handler The function to call when the arrow is clicked
7324      * @param {Object} scope (optional) Scope for the function passed above
7325      */
7326     setArrowHandler : function(handler, scope){
7327         this.arrowHandler = handler;
7328         this.scope = scope;  
7329     },
7330     
7331     /**
7332      * Focus the button
7333      */
7334     focus : function(){
7335         if(this.el){
7336             this.el.child("button:first").focus();
7337         }
7338     },
7339
7340     // private
7341     onClick : function(e){
7342         e.preventDefault();
7343         if(!this.disabled){
7344             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7345                 if(this.menu && !this.menu.isVisible()){
7346                     this.menu.show(this.el, this.menuAlign);
7347                 }
7348                 this.fireEvent("arrowclick", this, e);
7349                 if(this.arrowHandler){
7350                     this.arrowHandler.call(this.scope || this, this, e);
7351                 }
7352             }else{
7353                 this.fireEvent("click", this, e);
7354                 if(this.handler){
7355                     this.handler.call(this.scope || this, this, e);
7356                 }
7357             }
7358         }
7359     },
7360     // private
7361     onMouseDown : function(e){
7362         if(!this.disabled){
7363             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7364         }
7365     },
7366     // private
7367     onMouseUp : function(e){
7368         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7369     }   
7370 });
7371
7372
7373 // backwards compat
7374 Roo.MenuButton = Roo.SplitButton;/*
7375  * Based on:
7376  * Ext JS Library 1.1.1
7377  * Copyright(c) 2006-2007, Ext JS, LLC.
7378  *
7379  * Originally Released Under LGPL - original licence link has changed is not relivant.
7380  *
7381  * Fork - LGPL
7382  * <script type="text/javascript">
7383  */
7384
7385 /**
7386  * @class Roo.Toolbar
7387  * Basic Toolbar class.
7388  * @constructor
7389  * Creates a new Toolbar
7390  * @param {Object} container The config object
7391  */ 
7392 Roo.Toolbar = function(container, buttons, config)
7393 {
7394     /// old consturctor format still supported..
7395     if(container instanceof Array){ // omit the container for later rendering
7396         buttons = container;
7397         config = buttons;
7398         container = null;
7399     }
7400     if (typeof(container) == 'object' && container.xtype) {
7401         config = container;
7402         container = config.container;
7403         buttons = config.buttons || []; // not really - use items!!
7404     }
7405     var xitems = [];
7406     if (config && config.items) {
7407         xitems = config.items;
7408         delete config.items;
7409     }
7410     Roo.apply(this, config);
7411     this.buttons = buttons;
7412     
7413     if(container){
7414         this.render(container);
7415     }
7416     this.xitems = xitems;
7417     Roo.each(xitems, function(b) {
7418         this.add(b);
7419     }, this);
7420     
7421 };
7422
7423 Roo.Toolbar.prototype = {
7424     /**
7425      * @cfg {Array} items
7426      * array of button configs or elements to add (will be converted to a MixedCollection)
7427      */
7428     
7429     /**
7430      * @cfg {String/HTMLElement/Element} container
7431      * The id or element that will contain the toolbar
7432      */
7433     // private
7434     render : function(ct){
7435         this.el = Roo.get(ct);
7436         if(this.cls){
7437             this.el.addClass(this.cls);
7438         }
7439         // using a table allows for vertical alignment
7440         // 100% width is needed by Safari...
7441         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7442         this.tr = this.el.child("tr", true);
7443         var autoId = 0;
7444         this.items = new Roo.util.MixedCollection(false, function(o){
7445             return o.id || ("item" + (++autoId));
7446         });
7447         if(this.buttons){
7448             this.add.apply(this, this.buttons);
7449             delete this.buttons;
7450         }
7451     },
7452
7453     /**
7454      * Adds element(s) to the toolbar -- this function takes a variable number of 
7455      * arguments of mixed type and adds them to the toolbar.
7456      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7457      * <ul>
7458      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7459      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7460      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7461      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7462      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7463      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7464      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7465      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7466      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7467      * </ul>
7468      * @param {Mixed} arg2
7469      * @param {Mixed} etc.
7470      */
7471     add : function(){
7472         var a = arguments, l = a.length;
7473         for(var i = 0; i < l; i++){
7474             this._add(a[i]);
7475         }
7476     },
7477     // private..
7478     _add : function(el) {
7479         
7480         if (el.xtype) {
7481             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7482         }
7483         
7484         if (el.applyTo){ // some kind of form field
7485             return this.addField(el);
7486         } 
7487         if (el.render){ // some kind of Toolbar.Item
7488             return this.addItem(el);
7489         }
7490         if (typeof el == "string"){ // string
7491             if(el == "separator" || el == "-"){
7492                 return this.addSeparator();
7493             }
7494             if (el == " "){
7495                 return this.addSpacer();
7496             }
7497             if(el == "->"){
7498                 return this.addFill();
7499             }
7500             return this.addText(el);
7501             
7502         }
7503         if(el.tagName){ // element
7504             return this.addElement(el);
7505         }
7506         if(typeof el == "object"){ // must be button config?
7507             return this.addButton(el);
7508         }
7509         // and now what?!?!
7510         return false;
7511         
7512     },
7513     
7514     /**
7515      * Add an Xtype element
7516      * @param {Object} xtype Xtype Object
7517      * @return {Object} created Object
7518      */
7519     addxtype : function(e){
7520         return this.add(e);  
7521     },
7522     
7523     /**
7524      * Returns the Element for this toolbar.
7525      * @return {Roo.Element}
7526      */
7527     getEl : function(){
7528         return this.el;  
7529     },
7530     
7531     /**
7532      * Adds a separator
7533      * @return {Roo.Toolbar.Item} The separator item
7534      */
7535     addSeparator : function(){
7536         return this.addItem(new Roo.Toolbar.Separator());
7537     },
7538
7539     /**
7540      * Adds a spacer element
7541      * @return {Roo.Toolbar.Spacer} The spacer item
7542      */
7543     addSpacer : function(){
7544         return this.addItem(new Roo.Toolbar.Spacer());
7545     },
7546
7547     /**
7548      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7549      * @return {Roo.Toolbar.Fill} The fill item
7550      */
7551     addFill : function(){
7552         return this.addItem(new Roo.Toolbar.Fill());
7553     },
7554
7555     /**
7556      * Adds any standard HTML element to the toolbar
7557      * @param {String/HTMLElement/Element} el The element or id of the element to add
7558      * @return {Roo.Toolbar.Item} The element's item
7559      */
7560     addElement : function(el){
7561         return this.addItem(new Roo.Toolbar.Item(el));
7562     },
7563     /**
7564      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7565      * @type Roo.util.MixedCollection  
7566      */
7567     items : false,
7568      
7569     /**
7570      * Adds any Toolbar.Item or subclass
7571      * @param {Roo.Toolbar.Item} item
7572      * @return {Roo.Toolbar.Item} The item
7573      */
7574     addItem : function(item){
7575         var td = this.nextBlock();
7576         item.render(td);
7577         this.items.add(item);
7578         return item;
7579     },
7580     
7581     /**
7582      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7583      * @param {Object/Array} config A button config or array of configs
7584      * @return {Roo.Toolbar.Button/Array}
7585      */
7586     addButton : function(config){
7587         if(config instanceof Array){
7588             var buttons = [];
7589             for(var i = 0, len = config.length; i < len; i++) {
7590                 buttons.push(this.addButton(config[i]));
7591             }
7592             return buttons;
7593         }
7594         var b = config;
7595         if(!(config instanceof Roo.Toolbar.Button)){
7596             b = config.split ?
7597                 new Roo.Toolbar.SplitButton(config) :
7598                 new Roo.Toolbar.Button(config);
7599         }
7600         var td = this.nextBlock();
7601         b.render(td);
7602         this.items.add(b);
7603         return b;
7604     },
7605     
7606     /**
7607      * Adds text to the toolbar
7608      * @param {String} text The text to add
7609      * @return {Roo.Toolbar.Item} The element's item
7610      */
7611     addText : function(text){
7612         return this.addItem(new Roo.Toolbar.TextItem(text));
7613     },
7614     
7615     /**
7616      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7617      * @param {Number} index The index where the item is to be inserted
7618      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7619      * @return {Roo.Toolbar.Button/Item}
7620      */
7621     insertButton : function(index, item){
7622         if(item instanceof Array){
7623             var buttons = [];
7624             for(var i = 0, len = item.length; i < len; i++) {
7625                buttons.push(this.insertButton(index + i, item[i]));
7626             }
7627             return buttons;
7628         }
7629         if (!(item instanceof Roo.Toolbar.Button)){
7630            item = new Roo.Toolbar.Button(item);
7631         }
7632         var td = document.createElement("td");
7633         this.tr.insertBefore(td, this.tr.childNodes[index]);
7634         item.render(td);
7635         this.items.insert(index, item);
7636         return item;
7637     },
7638     
7639     /**
7640      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7641      * @param {Object} config
7642      * @return {Roo.Toolbar.Item} The element's item
7643      */
7644     addDom : function(config, returnEl){
7645         var td = this.nextBlock();
7646         Roo.DomHelper.overwrite(td, config);
7647         var ti = new Roo.Toolbar.Item(td.firstChild);
7648         ti.render(td);
7649         this.items.add(ti);
7650         return ti;
7651     },
7652
7653     /**
7654      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7655      * @type Roo.util.MixedCollection  
7656      */
7657     fields : false,
7658     
7659     /**
7660      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7661      * Note: the field should not have been rendered yet. For a field that has already been
7662      * rendered, use {@link #addElement}.
7663      * @param {Roo.form.Field} field
7664      * @return {Roo.ToolbarItem}
7665      */
7666      
7667       
7668     addField : function(field) {
7669         if (!this.fields) {
7670             var autoId = 0;
7671             this.fields = new Roo.util.MixedCollection(false, function(o){
7672                 return o.id || ("item" + (++autoId));
7673             });
7674
7675         }
7676         
7677         var td = this.nextBlock();
7678         field.render(td);
7679         var ti = new Roo.Toolbar.Item(td.firstChild);
7680         ti.render(td);
7681         this.items.add(ti);
7682         this.fields.add(field);
7683         return ti;
7684     },
7685     /**
7686      * Hide the toolbar
7687      * @method hide
7688      */
7689      
7690       
7691     hide : function()
7692     {
7693         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7694         this.el.child('div').hide();
7695     },
7696     /**
7697      * Show the toolbar
7698      * @method show
7699      */
7700     show : function()
7701     {
7702         this.el.child('div').show();
7703     },
7704       
7705     // private
7706     nextBlock : function(){
7707         var td = document.createElement("td");
7708         this.tr.appendChild(td);
7709         return td;
7710     },
7711
7712     // private
7713     destroy : function(){
7714         if(this.items){ // rendered?
7715             Roo.destroy.apply(Roo, this.items.items);
7716         }
7717         if(this.fields){ // rendered?
7718             Roo.destroy.apply(Roo, this.fields.items);
7719         }
7720         Roo.Element.uncache(this.el, this.tr);
7721     }
7722 };
7723
7724 /**
7725  * @class Roo.Toolbar.Item
7726  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7727  * @constructor
7728  * Creates a new Item
7729  * @param {HTMLElement} el 
7730  */
7731 Roo.Toolbar.Item = function(el){
7732     var cfg = {};
7733     if (typeof (el.xtype) != 'undefined') {
7734         cfg = el;
7735         el = cfg.el;
7736     }
7737     
7738     this.el = Roo.getDom(el);
7739     this.id = Roo.id(this.el);
7740     this.hidden = false;
7741     
7742     this.addEvents({
7743          /**
7744              * @event render
7745              * Fires when the button is rendered
7746              * @param {Button} this
7747              */
7748         'render': true
7749     });
7750     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7751 };
7752 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7753 //Roo.Toolbar.Item.prototype = {
7754     
7755     /**
7756      * Get this item's HTML Element
7757      * @return {HTMLElement}
7758      */
7759     getEl : function(){
7760        return this.el;  
7761     },
7762
7763     // private
7764     render : function(td){
7765         
7766          this.td = td;
7767         td.appendChild(this.el);
7768         
7769         this.fireEvent('render', this);
7770     },
7771     
7772     /**
7773      * Removes and destroys this item.
7774      */
7775     destroy : function(){
7776         this.td.parentNode.removeChild(this.td);
7777     },
7778     
7779     /**
7780      * Shows this item.
7781      */
7782     show: function(){
7783         this.hidden = false;
7784         this.td.style.display = "";
7785     },
7786     
7787     /**
7788      * Hides this item.
7789      */
7790     hide: function(){
7791         this.hidden = true;
7792         this.td.style.display = "none";
7793     },
7794     
7795     /**
7796      * Convenience function for boolean show/hide.
7797      * @param {Boolean} visible true to show/false to hide
7798      */
7799     setVisible: function(visible){
7800         if(visible) {
7801             this.show();
7802         }else{
7803             this.hide();
7804         }
7805     },
7806     
7807     /**
7808      * Try to focus this item.
7809      */
7810     focus : function(){
7811         Roo.fly(this.el).focus();
7812     },
7813     
7814     /**
7815      * Disables this item.
7816      */
7817     disable : function(){
7818         Roo.fly(this.td).addClass("x-item-disabled");
7819         this.disabled = true;
7820         this.el.disabled = true;
7821     },
7822     
7823     /**
7824      * Enables this item.
7825      */
7826     enable : function(){
7827         Roo.fly(this.td).removeClass("x-item-disabled");
7828         this.disabled = false;
7829         this.el.disabled = false;
7830     }
7831 });
7832
7833
7834 /**
7835  * @class Roo.Toolbar.Separator
7836  * @extends Roo.Toolbar.Item
7837  * A simple toolbar separator class
7838  * @constructor
7839  * Creates a new Separator
7840  */
7841 Roo.Toolbar.Separator = function(cfg){
7842     
7843     var s = document.createElement("span");
7844     s.className = "ytb-sep";
7845     if (cfg) {
7846         cfg.el = s;
7847     }
7848     
7849     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7850 };
7851 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7852     enable:Roo.emptyFn,
7853     disable:Roo.emptyFn,
7854     focus:Roo.emptyFn
7855 });
7856
7857 /**
7858  * @class Roo.Toolbar.Spacer
7859  * @extends Roo.Toolbar.Item
7860  * A simple element that adds extra horizontal space to a toolbar.
7861  * @constructor
7862  * Creates a new Spacer
7863  */
7864 Roo.Toolbar.Spacer = function(cfg){
7865     var s = document.createElement("div");
7866     s.className = "ytb-spacer";
7867     if (cfg) {
7868         cfg.el = s;
7869     }
7870     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7871 };
7872 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7873     enable:Roo.emptyFn,
7874     disable:Roo.emptyFn,
7875     focus:Roo.emptyFn
7876 });
7877
7878 /**
7879  * @class Roo.Toolbar.Fill
7880  * @extends Roo.Toolbar.Spacer
7881  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7882  * @constructor
7883  * Creates a new Spacer
7884  */
7885 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7886     // private
7887     render : function(td){
7888         td.style.width = '100%';
7889         Roo.Toolbar.Fill.superclass.render.call(this, td);
7890     }
7891 });
7892
7893 /**
7894  * @class Roo.Toolbar.TextItem
7895  * @extends Roo.Toolbar.Item
7896  * A simple class that renders text directly into a toolbar.
7897  * @constructor
7898  * Creates a new TextItem
7899  * @param {String} text
7900  */
7901 Roo.Toolbar.TextItem = function(cfg){
7902     var  text = cfg || "";
7903     if (typeof(cfg) == 'object') {
7904         text = cfg.text || "";
7905     }  else {
7906         cfg = null;
7907     }
7908     var s = document.createElement("span");
7909     s.className = "ytb-text";
7910     s.innerHTML = text;
7911     if (cfg) {
7912         cfg.el  = s;
7913     }
7914     
7915     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7916 };
7917 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7918     
7919      
7920     enable:Roo.emptyFn,
7921     disable:Roo.emptyFn,
7922     focus:Roo.emptyFn
7923 });
7924
7925 /**
7926  * @class Roo.Toolbar.Button
7927  * @extends Roo.Button
7928  * A button that renders into a toolbar.
7929  * @constructor
7930  * Creates a new Button
7931  * @param {Object} config A standard {@link Roo.Button} config object
7932  */
7933 Roo.Toolbar.Button = function(config){
7934     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7935 };
7936 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7937     render : function(td){
7938         this.td = td;
7939         Roo.Toolbar.Button.superclass.render.call(this, td);
7940     },
7941     
7942     /**
7943      * Removes and destroys this button
7944      */
7945     destroy : function(){
7946         Roo.Toolbar.Button.superclass.destroy.call(this);
7947         this.td.parentNode.removeChild(this.td);
7948     },
7949     
7950     /**
7951      * Shows this button
7952      */
7953     show: function(){
7954         this.hidden = false;
7955         this.td.style.display = "";
7956     },
7957     
7958     /**
7959      * Hides this button
7960      */
7961     hide: function(){
7962         this.hidden = true;
7963         this.td.style.display = "none";
7964     },
7965
7966     /**
7967      * Disables this item
7968      */
7969     disable : function(){
7970         Roo.fly(this.td).addClass("x-item-disabled");
7971         this.disabled = true;
7972     },
7973
7974     /**
7975      * Enables this item
7976      */
7977     enable : function(){
7978         Roo.fly(this.td).removeClass("x-item-disabled");
7979         this.disabled = false;
7980     }
7981 });
7982 // backwards compat
7983 Roo.ToolbarButton = Roo.Toolbar.Button;
7984
7985 /**
7986  * @class Roo.Toolbar.SplitButton
7987  * @extends Roo.SplitButton
7988  * A menu button that renders into a toolbar.
7989  * @constructor
7990  * Creates a new SplitButton
7991  * @param {Object} config A standard {@link Roo.SplitButton} config object
7992  */
7993 Roo.Toolbar.SplitButton = function(config){
7994     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7995 };
7996 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7997     render : function(td){
7998         this.td = td;
7999         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8000     },
8001     
8002     /**
8003      * Removes and destroys this button
8004      */
8005     destroy : function(){
8006         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8007         this.td.parentNode.removeChild(this.td);
8008     },
8009     
8010     /**
8011      * Shows this button
8012      */
8013     show: function(){
8014         this.hidden = false;
8015         this.td.style.display = "";
8016     },
8017     
8018     /**
8019      * Hides this button
8020      */
8021     hide: function(){
8022         this.hidden = true;
8023         this.td.style.display = "none";
8024     }
8025 });
8026
8027 // backwards compat
8028 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8029  * Based on:
8030  * Ext JS Library 1.1.1
8031  * Copyright(c) 2006-2007, Ext JS, LLC.
8032  *
8033  * Originally Released Under LGPL - original licence link has changed is not relivant.
8034  *
8035  * Fork - LGPL
8036  * <script type="text/javascript">
8037  */
8038  
8039 /**
8040  * @class Roo.PagingToolbar
8041  * @extends Roo.Toolbar
8042  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8043  * @constructor
8044  * Create a new PagingToolbar
8045  * @param {Object} config The config object
8046  */
8047 Roo.PagingToolbar = function(el, ds, config)
8048 {
8049     // old args format still supported... - xtype is prefered..
8050     if (typeof(el) == 'object' && el.xtype) {
8051         // created from xtype...
8052         config = el;
8053         ds = el.dataSource;
8054         el = config.container;
8055     }
8056     var items = [];
8057     if (config.items) {
8058         items = config.items;
8059         config.items = [];
8060     }
8061     
8062     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8063     this.ds = ds;
8064     this.cursor = 0;
8065     this.renderButtons(this.el);
8066     this.bind(ds);
8067     
8068     // supprot items array.
8069    
8070     Roo.each(items, function(e) {
8071         this.add(Roo.factory(e));
8072     },this);
8073     
8074 };
8075
8076 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8077     /**
8078      * @cfg {Roo.data.Store} dataSource
8079      * The underlying data store providing the paged data
8080      */
8081     /**
8082      * @cfg {String/HTMLElement/Element} container
8083      * container The id or element that will contain the toolbar
8084      */
8085     /**
8086      * @cfg {Boolean} displayInfo
8087      * True to display the displayMsg (defaults to false)
8088      */
8089     /**
8090      * @cfg {Number} pageSize
8091      * The number of records to display per page (defaults to 20)
8092      */
8093     pageSize: 20,
8094     /**
8095      * @cfg {String} displayMsg
8096      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8097      */
8098     displayMsg : 'Displaying {0} - {1} of {2}',
8099     /**
8100      * @cfg {String} emptyMsg
8101      * The message to display when no records are found (defaults to "No data to display")
8102      */
8103     emptyMsg : 'No data to display',
8104     /**
8105      * Customizable piece of the default paging text (defaults to "Page")
8106      * @type String
8107      */
8108     beforePageText : "Page",
8109     /**
8110      * Customizable piece of the default paging text (defaults to "of %0")
8111      * @type String
8112      */
8113     afterPageText : "of {0}",
8114     /**
8115      * Customizable piece of the default paging text (defaults to "First Page")
8116      * @type String
8117      */
8118     firstText : "First Page",
8119     /**
8120      * Customizable piece of the default paging text (defaults to "Previous Page")
8121      * @type String
8122      */
8123     prevText : "Previous Page",
8124     /**
8125      * Customizable piece of the default paging text (defaults to "Next Page")
8126      * @type String
8127      */
8128     nextText : "Next Page",
8129     /**
8130      * Customizable piece of the default paging text (defaults to "Last Page")
8131      * @type String
8132      */
8133     lastText : "Last Page",
8134     /**
8135      * Customizable piece of the default paging text (defaults to "Refresh")
8136      * @type String
8137      */
8138     refreshText : "Refresh",
8139
8140     // private
8141     renderButtons : function(el){
8142         Roo.PagingToolbar.superclass.render.call(this, el);
8143         this.first = this.addButton({
8144             tooltip: this.firstText,
8145             cls: "x-btn-icon x-grid-page-first",
8146             disabled: true,
8147             handler: this.onClick.createDelegate(this, ["first"])
8148         });
8149         this.prev = this.addButton({
8150             tooltip: this.prevText,
8151             cls: "x-btn-icon x-grid-page-prev",
8152             disabled: true,
8153             handler: this.onClick.createDelegate(this, ["prev"])
8154         });
8155         //this.addSeparator();
8156         this.add(this.beforePageText);
8157         this.field = Roo.get(this.addDom({
8158            tag: "input",
8159            type: "text",
8160            size: "3",
8161            value: "1",
8162            cls: "x-grid-page-number"
8163         }).el);
8164         this.field.on("keydown", this.onPagingKeydown, this);
8165         this.field.on("focus", function(){this.dom.select();});
8166         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8167         this.field.setHeight(18);
8168         //this.addSeparator();
8169         this.next = this.addButton({
8170             tooltip: this.nextText,
8171             cls: "x-btn-icon x-grid-page-next",
8172             disabled: true,
8173             handler: this.onClick.createDelegate(this, ["next"])
8174         });
8175         this.last = this.addButton({
8176             tooltip: this.lastText,
8177             cls: "x-btn-icon x-grid-page-last",
8178             disabled: true,
8179             handler: this.onClick.createDelegate(this, ["last"])
8180         });
8181         //this.addSeparator();
8182         this.loading = this.addButton({
8183             tooltip: this.refreshText,
8184             cls: "x-btn-icon x-grid-loading",
8185             handler: this.onClick.createDelegate(this, ["refresh"])
8186         });
8187
8188         if(this.displayInfo){
8189             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8190         }
8191     },
8192
8193     // private
8194     updateInfo : function(){
8195         if(this.displayEl){
8196             var count = this.ds.getCount();
8197             var msg = count == 0 ?
8198                 this.emptyMsg :
8199                 String.format(
8200                     this.displayMsg,
8201                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8202                 );
8203             this.displayEl.update(msg);
8204         }
8205     },
8206
8207     // private
8208     onLoad : function(ds, r, o){
8209        this.cursor = o.params ? o.params.start : 0;
8210        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8211
8212        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8213        this.field.dom.value = ap;
8214        this.first.setDisabled(ap == 1);
8215        this.prev.setDisabled(ap == 1);
8216        this.next.setDisabled(ap == ps);
8217        this.last.setDisabled(ap == ps);
8218        this.loading.enable();
8219        this.updateInfo();
8220     },
8221
8222     // private
8223     getPageData : function(){
8224         var total = this.ds.getTotalCount();
8225         return {
8226             total : total,
8227             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8228             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8229         };
8230     },
8231
8232     // private
8233     onLoadError : function(){
8234         this.loading.enable();
8235     },
8236
8237     // private
8238     onPagingKeydown : function(e){
8239         var k = e.getKey();
8240         var d = this.getPageData();
8241         if(k == e.RETURN){
8242             var v = this.field.dom.value, pageNum;
8243             if(!v || isNaN(pageNum = parseInt(v, 10))){
8244                 this.field.dom.value = d.activePage;
8245                 return;
8246             }
8247             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8248             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8249             e.stopEvent();
8250         }
8251         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))
8252         {
8253           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8254           this.field.dom.value = pageNum;
8255           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8256           e.stopEvent();
8257         }
8258         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8259         {
8260           var v = this.field.dom.value, pageNum; 
8261           var increment = (e.shiftKey) ? 10 : 1;
8262           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8263             increment *= -1;
8264           }
8265           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8266             this.field.dom.value = d.activePage;
8267             return;
8268           }
8269           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8270           {
8271             this.field.dom.value = parseInt(v, 10) + increment;
8272             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8273             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8274           }
8275           e.stopEvent();
8276         }
8277     },
8278
8279     // private
8280     beforeLoad : function(){
8281         if(this.loading){
8282             this.loading.disable();
8283         }
8284     },
8285
8286     // private
8287     onClick : function(which){
8288         var ds = this.ds;
8289         switch(which){
8290             case "first":
8291                 ds.load({params:{start: 0, limit: this.pageSize}});
8292             break;
8293             case "prev":
8294                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8295             break;
8296             case "next":
8297                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8298             break;
8299             case "last":
8300                 var total = ds.getTotalCount();
8301                 var extra = total % this.pageSize;
8302                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8303                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8304             break;
8305             case "refresh":
8306                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8307             break;
8308         }
8309     },
8310
8311     /**
8312      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8313      * @param {Roo.data.Store} store The data store to unbind
8314      */
8315     unbind : function(ds){
8316         ds.un("beforeload", this.beforeLoad, this);
8317         ds.un("load", this.onLoad, this);
8318         ds.un("loadexception", this.onLoadError, this);
8319         ds.un("remove", this.updateInfo, this);
8320         ds.un("add", this.updateInfo, this);
8321         this.ds = undefined;
8322     },
8323
8324     /**
8325      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8326      * @param {Roo.data.Store} store The data store to bind
8327      */
8328     bind : function(ds){
8329         ds.on("beforeload", this.beforeLoad, this);
8330         ds.on("load", this.onLoad, this);
8331         ds.on("loadexception", this.onLoadError, this);
8332         ds.on("remove", this.updateInfo, this);
8333         ds.on("add", this.updateInfo, this);
8334         this.ds = ds;
8335     }
8336 });/*
8337  * Based on:
8338  * Ext JS Library 1.1.1
8339  * Copyright(c) 2006-2007, Ext JS, LLC.
8340  *
8341  * Originally Released Under LGPL - original licence link has changed is not relivant.
8342  *
8343  * Fork - LGPL
8344  * <script type="text/javascript">
8345  */
8346
8347 /**
8348  * @class Roo.Resizable
8349  * @extends Roo.util.Observable
8350  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8351  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8352  * 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
8353  * the element will be wrapped for you automatically.</p>
8354  * <p>Here is the list of valid resize handles:</p>
8355  * <pre>
8356 Value   Description
8357 ------  -------------------
8358  'n'     north
8359  's'     south
8360  'e'     east
8361  'w'     west
8362  'nw'    northwest
8363  'sw'    southwest
8364  'se'    southeast
8365  'ne'    northeast
8366  'hd'    horizontal drag
8367  'all'   all
8368 </pre>
8369  * <p>Here's an example showing the creation of a typical Resizable:</p>
8370  * <pre><code>
8371 var resizer = new Roo.Resizable("element-id", {
8372     handles: 'all',
8373     minWidth: 200,
8374     minHeight: 100,
8375     maxWidth: 500,
8376     maxHeight: 400,
8377     pinned: true
8378 });
8379 resizer.on("resize", myHandler);
8380 </code></pre>
8381  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8382  * resizer.east.setDisplayed(false);</p>
8383  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8384  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8385  * resize operation's new size (defaults to [0, 0])
8386  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8387  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8388  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8389  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8390  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8391  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8392  * @cfg {Number} width The width of the element in pixels (defaults to null)
8393  * @cfg {Number} height The height of the element in pixels (defaults to null)
8394  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8395  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8396  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8397  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8398  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8399  * in favor of the handles config option (defaults to false)
8400  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8401  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8402  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8403  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8404  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8405  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8406  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8407  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8408  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8409  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8410  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8411  * @constructor
8412  * Create a new resizable component
8413  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8414  * @param {Object} config configuration options
8415   */
8416 Roo.Resizable = function(el, config)
8417 {
8418     this.el = Roo.get(el);
8419
8420     if(config && config.wrap){
8421         config.resizeChild = this.el;
8422         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8423         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8424         this.el.setStyle("overflow", "hidden");
8425         this.el.setPositioning(config.resizeChild.getPositioning());
8426         config.resizeChild.clearPositioning();
8427         if(!config.width || !config.height){
8428             var csize = config.resizeChild.getSize();
8429             this.el.setSize(csize.width, csize.height);
8430         }
8431         if(config.pinned && !config.adjustments){
8432             config.adjustments = "auto";
8433         }
8434     }
8435
8436     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8437     this.proxy.unselectable();
8438     this.proxy.enableDisplayMode('block');
8439
8440     Roo.apply(this, config);
8441
8442     if(this.pinned){
8443         this.disableTrackOver = true;
8444         this.el.addClass("x-resizable-pinned");
8445     }
8446     // if the element isn't positioned, make it relative
8447     var position = this.el.getStyle("position");
8448     if(position != "absolute" && position != "fixed"){
8449         this.el.setStyle("position", "relative");
8450     }
8451     if(!this.handles){ // no handles passed, must be legacy style
8452         this.handles = 's,e,se';
8453         if(this.multiDirectional){
8454             this.handles += ',n,w';
8455         }
8456     }
8457     if(this.handles == "all"){
8458         this.handles = "n s e w ne nw se sw";
8459     }
8460     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8461     var ps = Roo.Resizable.positions;
8462     for(var i = 0, len = hs.length; i < len; i++){
8463         if(hs[i] && ps[hs[i]]){
8464             var pos = ps[hs[i]];
8465             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8466         }
8467     }
8468     // legacy
8469     this.corner = this.southeast;
8470     
8471     // updateBox = the box can move..
8472     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8473         this.updateBox = true;
8474     }
8475
8476     this.activeHandle = null;
8477
8478     if(this.resizeChild){
8479         if(typeof this.resizeChild == "boolean"){
8480             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8481         }else{
8482             this.resizeChild = Roo.get(this.resizeChild, true);
8483         }
8484     }
8485     
8486     if(this.adjustments == "auto"){
8487         var rc = this.resizeChild;
8488         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8489         if(rc && (hw || hn)){
8490             rc.position("relative");
8491             rc.setLeft(hw ? hw.el.getWidth() : 0);
8492             rc.setTop(hn ? hn.el.getHeight() : 0);
8493         }
8494         this.adjustments = [
8495             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8496             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8497         ];
8498     }
8499
8500     if(this.draggable){
8501         this.dd = this.dynamic ?
8502             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8503         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8504     }
8505
8506     // public events
8507     this.addEvents({
8508         /**
8509          * @event beforeresize
8510          * Fired before resize is allowed. Set enabled to false to cancel resize.
8511          * @param {Roo.Resizable} this
8512          * @param {Roo.EventObject} e The mousedown event
8513          */
8514         "beforeresize" : true,
8515         /**
8516          * @event resizing
8517          * Fired a resizing.
8518          * @param {Roo.Resizable} this
8519          * @param {Number} x The new x position
8520          * @param {Number} y The new y position
8521          * @param {Number} w The new w width
8522          * @param {Number} h The new h hight
8523          * @param {Roo.EventObject} e The mouseup event
8524          */
8525         "resizing" : true,
8526         /**
8527          * @event resize
8528          * Fired after a resize.
8529          * @param {Roo.Resizable} this
8530          * @param {Number} width The new width
8531          * @param {Number} height The new height
8532          * @param {Roo.EventObject} e The mouseup event
8533          */
8534         "resize" : true
8535     });
8536
8537     if(this.width !== null && this.height !== null){
8538         this.resizeTo(this.width, this.height);
8539     }else{
8540         this.updateChildSize();
8541     }
8542     if(Roo.isIE){
8543         this.el.dom.style.zoom = 1;
8544     }
8545     Roo.Resizable.superclass.constructor.call(this);
8546 };
8547
8548 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8549         resizeChild : false,
8550         adjustments : [0, 0],
8551         minWidth : 5,
8552         minHeight : 5,
8553         maxWidth : 10000,
8554         maxHeight : 10000,
8555         enabled : true,
8556         animate : false,
8557         duration : .35,
8558         dynamic : false,
8559         handles : false,
8560         multiDirectional : false,
8561         disableTrackOver : false,
8562         easing : 'easeOutStrong',
8563         widthIncrement : 0,
8564         heightIncrement : 0,
8565         pinned : false,
8566         width : null,
8567         height : null,
8568         preserveRatio : false,
8569         transparent: false,
8570         minX: 0,
8571         minY: 0,
8572         draggable: false,
8573
8574         /**
8575          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8576          */
8577         constrainTo: undefined,
8578         /**
8579          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8580          */
8581         resizeRegion: undefined,
8582
8583
8584     /**
8585      * Perform a manual resize
8586      * @param {Number} width
8587      * @param {Number} height
8588      */
8589     resizeTo : function(width, height){
8590         this.el.setSize(width, height);
8591         this.updateChildSize();
8592         this.fireEvent("resize", this, width, height, null);
8593     },
8594
8595     // private
8596     startSizing : function(e, handle){
8597         this.fireEvent("beforeresize", this, e);
8598         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8599
8600             if(!this.overlay){
8601                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8602                 this.overlay.unselectable();
8603                 this.overlay.enableDisplayMode("block");
8604                 this.overlay.on("mousemove", this.onMouseMove, this);
8605                 this.overlay.on("mouseup", this.onMouseUp, this);
8606             }
8607             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8608
8609             this.resizing = true;
8610             this.startBox = this.el.getBox();
8611             this.startPoint = e.getXY();
8612             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8613                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8614
8615             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8616             this.overlay.show();
8617
8618             if(this.constrainTo) {
8619                 var ct = Roo.get(this.constrainTo);
8620                 this.resizeRegion = ct.getRegion().adjust(
8621                     ct.getFrameWidth('t'),
8622                     ct.getFrameWidth('l'),
8623                     -ct.getFrameWidth('b'),
8624                     -ct.getFrameWidth('r')
8625                 );
8626             }
8627
8628             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8629             this.proxy.show();
8630             this.proxy.setBox(this.startBox);
8631             if(!this.dynamic){
8632                 this.proxy.setStyle('visibility', 'visible');
8633             }
8634         }
8635     },
8636
8637     // private
8638     onMouseDown : function(handle, e){
8639         if(this.enabled){
8640             e.stopEvent();
8641             this.activeHandle = handle;
8642             this.startSizing(e, handle);
8643         }
8644     },
8645
8646     // private
8647     onMouseUp : function(e){
8648         var size = this.resizeElement();
8649         this.resizing = false;
8650         this.handleOut();
8651         this.overlay.hide();
8652         this.proxy.hide();
8653         this.fireEvent("resize", this, size.width, size.height, e);
8654     },
8655
8656     // private
8657     updateChildSize : function(){
8658         
8659         if(this.resizeChild){
8660             var el = this.el;
8661             var child = this.resizeChild;
8662             var adj = this.adjustments;
8663             if(el.dom.offsetWidth){
8664                 var b = el.getSize(true);
8665                 child.setSize(b.width+adj[0], b.height+adj[1]);
8666             }
8667             // Second call here for IE
8668             // The first call enables instant resizing and
8669             // the second call corrects scroll bars if they
8670             // exist
8671             if(Roo.isIE){
8672                 setTimeout(function(){
8673                     if(el.dom.offsetWidth){
8674                         var b = el.getSize(true);
8675                         child.setSize(b.width+adj[0], b.height+adj[1]);
8676                     }
8677                 }, 10);
8678             }
8679         }
8680     },
8681
8682     // private
8683     snap : function(value, inc, min){
8684         if(!inc || !value) {
8685             return value;
8686         }
8687         var newValue = value;
8688         var m = value % inc;
8689         if(m > 0){
8690             if(m > (inc/2)){
8691                 newValue = value + (inc-m);
8692             }else{
8693                 newValue = value - m;
8694             }
8695         }
8696         return Math.max(min, newValue);
8697     },
8698
8699     // private
8700     resizeElement : function(){
8701         var box = this.proxy.getBox();
8702         if(this.updateBox){
8703             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8704         }else{
8705             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8706         }
8707         this.updateChildSize();
8708         if(!this.dynamic){
8709             this.proxy.hide();
8710         }
8711         return box;
8712     },
8713
8714     // private
8715     constrain : function(v, diff, m, mx){
8716         if(v - diff < m){
8717             diff = v - m;
8718         }else if(v - diff > mx){
8719             diff = mx - v;
8720         }
8721         return diff;
8722     },
8723
8724     // private
8725     onMouseMove : function(e){
8726         
8727         if(this.enabled){
8728             try{// try catch so if something goes wrong the user doesn't get hung
8729
8730             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8731                 return;
8732             }
8733
8734             //var curXY = this.startPoint;
8735             var curSize = this.curSize || this.startBox;
8736             var x = this.startBox.x, y = this.startBox.y;
8737             var ox = x, oy = y;
8738             var w = curSize.width, h = curSize.height;
8739             var ow = w, oh = h;
8740             var mw = this.minWidth, mh = this.minHeight;
8741             var mxw = this.maxWidth, mxh = this.maxHeight;
8742             var wi = this.widthIncrement;
8743             var hi = this.heightIncrement;
8744
8745             var eventXY = e.getXY();
8746             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8747             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8748
8749             var pos = this.activeHandle.position;
8750
8751             switch(pos){
8752                 case "east":
8753                     w += diffX;
8754                     w = Math.min(Math.max(mw, w), mxw);
8755                     break;
8756              
8757                 case "south":
8758                     h += diffY;
8759                     h = Math.min(Math.max(mh, h), mxh);
8760                     break;
8761                 case "southeast":
8762                     w += diffX;
8763                     h += diffY;
8764                     w = Math.min(Math.max(mw, w), mxw);
8765                     h = Math.min(Math.max(mh, h), mxh);
8766                     break;
8767                 case "north":
8768                     diffY = this.constrain(h, diffY, mh, mxh);
8769                     y += diffY;
8770                     h -= diffY;
8771                     break;
8772                 case "hdrag":
8773                     
8774                     if (wi) {
8775                         var adiffX = Math.abs(diffX);
8776                         var sub = (adiffX % wi); // how much 
8777                         if (sub > (wi/2)) { // far enough to snap
8778                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8779                         } else {
8780                             // remove difference.. 
8781                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8782                         }
8783                     }
8784                     x += diffX;
8785                     x = Math.max(this.minX, x);
8786                     break;
8787                 case "west":
8788                     diffX = this.constrain(w, diffX, mw, mxw);
8789                     x += diffX;
8790                     w -= diffX;
8791                     break;
8792                 case "northeast":
8793                     w += diffX;
8794                     w = Math.min(Math.max(mw, w), mxw);
8795                     diffY = this.constrain(h, diffY, mh, mxh);
8796                     y += diffY;
8797                     h -= diffY;
8798                     break;
8799                 case "northwest":
8800                     diffX = this.constrain(w, diffX, mw, mxw);
8801                     diffY = this.constrain(h, diffY, mh, mxh);
8802                     y += diffY;
8803                     h -= diffY;
8804                     x += diffX;
8805                     w -= diffX;
8806                     break;
8807                case "southwest":
8808                     diffX = this.constrain(w, diffX, mw, mxw);
8809                     h += diffY;
8810                     h = Math.min(Math.max(mh, h), mxh);
8811                     x += diffX;
8812                     w -= diffX;
8813                     break;
8814             }
8815
8816             var sw = this.snap(w, wi, mw);
8817             var sh = this.snap(h, hi, mh);
8818             if(sw != w || sh != h){
8819                 switch(pos){
8820                     case "northeast":
8821                         y -= sh - h;
8822                     break;
8823                     case "north":
8824                         y -= sh - h;
8825                         break;
8826                     case "southwest":
8827                         x -= sw - w;
8828                     break;
8829                     case "west":
8830                         x -= sw - w;
8831                         break;
8832                     case "northwest":
8833                         x -= sw - w;
8834                         y -= sh - h;
8835                     break;
8836                 }
8837                 w = sw;
8838                 h = sh;
8839             }
8840
8841             if(this.preserveRatio){
8842                 switch(pos){
8843                     case "southeast":
8844                     case "east":
8845                         h = oh * (w/ow);
8846                         h = Math.min(Math.max(mh, h), mxh);
8847                         w = ow * (h/oh);
8848                        break;
8849                     case "south":
8850                         w = ow * (h/oh);
8851                         w = Math.min(Math.max(mw, w), mxw);
8852                         h = oh * (w/ow);
8853                         break;
8854                     case "northeast":
8855                         w = ow * (h/oh);
8856                         w = Math.min(Math.max(mw, w), mxw);
8857                         h = oh * (w/ow);
8858                     break;
8859                     case "north":
8860                         var tw = w;
8861                         w = ow * (h/oh);
8862                         w = Math.min(Math.max(mw, w), mxw);
8863                         h = oh * (w/ow);
8864                         x += (tw - w) / 2;
8865                         break;
8866                     case "southwest":
8867                         h = oh * (w/ow);
8868                         h = Math.min(Math.max(mh, h), mxh);
8869                         var tw = w;
8870                         w = ow * (h/oh);
8871                         x += tw - w;
8872                         break;
8873                     case "west":
8874                         var th = h;
8875                         h = oh * (w/ow);
8876                         h = Math.min(Math.max(mh, h), mxh);
8877                         y += (th - h) / 2;
8878                         var tw = w;
8879                         w = ow * (h/oh);
8880                         x += tw - w;
8881                        break;
8882                     case "northwest":
8883                         var tw = w;
8884                         var th = h;
8885                         h = oh * (w/ow);
8886                         h = Math.min(Math.max(mh, h), mxh);
8887                         w = ow * (h/oh);
8888                         y += th - h;
8889                         x += tw - w;
8890                        break;
8891
8892                 }
8893             }
8894             if (pos == 'hdrag') {
8895                 w = ow;
8896             }
8897             this.proxy.setBounds(x, y, w, h);
8898             if(this.dynamic){
8899                 this.resizeElement();
8900             }
8901             }catch(e){}
8902         }
8903         this.fireEvent("resizing", this, x, y, w, h, e);
8904     },
8905
8906     // private
8907     handleOver : function(){
8908         if(this.enabled){
8909             this.el.addClass("x-resizable-over");
8910         }
8911     },
8912
8913     // private
8914     handleOut : function(){
8915         if(!this.resizing){
8916             this.el.removeClass("x-resizable-over");
8917         }
8918     },
8919
8920     /**
8921      * Returns the element this component is bound to.
8922      * @return {Roo.Element}
8923      */
8924     getEl : function(){
8925         return this.el;
8926     },
8927
8928     /**
8929      * Returns the resizeChild element (or null).
8930      * @return {Roo.Element}
8931      */
8932     getResizeChild : function(){
8933         return this.resizeChild;
8934     },
8935     groupHandler : function()
8936     {
8937         
8938     },
8939     /**
8940      * Destroys this resizable. If the element was wrapped and
8941      * removeEl is not true then the element remains.
8942      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8943      */
8944     destroy : function(removeEl){
8945         this.proxy.remove();
8946         if(this.overlay){
8947             this.overlay.removeAllListeners();
8948             this.overlay.remove();
8949         }
8950         var ps = Roo.Resizable.positions;
8951         for(var k in ps){
8952             if(typeof ps[k] != "function" && this[ps[k]]){
8953                 var h = this[ps[k]];
8954                 h.el.removeAllListeners();
8955                 h.el.remove();
8956             }
8957         }
8958         if(removeEl){
8959             this.el.update("");
8960             this.el.remove();
8961         }
8962     }
8963 });
8964
8965 // private
8966 // hash to map config positions to true positions
8967 Roo.Resizable.positions = {
8968     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8969     hd: "hdrag"
8970 };
8971
8972 // private
8973 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8974     if(!this.tpl){
8975         // only initialize the template if resizable is used
8976         var tpl = Roo.DomHelper.createTemplate(
8977             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8978         );
8979         tpl.compile();
8980         Roo.Resizable.Handle.prototype.tpl = tpl;
8981     }
8982     this.position = pos;
8983     this.rz = rz;
8984     // show north drag fro topdra
8985     var handlepos = pos == 'hdrag' ? 'north' : pos;
8986     
8987     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8988     if (pos == 'hdrag') {
8989         this.el.setStyle('cursor', 'pointer');
8990     }
8991     this.el.unselectable();
8992     if(transparent){
8993         this.el.setOpacity(0);
8994     }
8995     this.el.on("mousedown", this.onMouseDown, this);
8996     if(!disableTrackOver){
8997         this.el.on("mouseover", this.onMouseOver, this);
8998         this.el.on("mouseout", this.onMouseOut, this);
8999     }
9000 };
9001
9002 // private
9003 Roo.Resizable.Handle.prototype = {
9004     afterResize : function(rz){
9005         Roo.log('after?');
9006         // do nothing
9007     },
9008     // private
9009     onMouseDown : function(e){
9010         this.rz.onMouseDown(this, e);
9011     },
9012     // private
9013     onMouseOver : function(e){
9014         this.rz.handleOver(this, e);
9015     },
9016     // private
9017     onMouseOut : function(e){
9018         this.rz.handleOut(this, e);
9019     }
9020 };/*
9021  * Based on:
9022  * Ext JS Library 1.1.1
9023  * Copyright(c) 2006-2007, Ext JS, LLC.
9024  *
9025  * Originally Released Under LGPL - original licence link has changed is not relivant.
9026  *
9027  * Fork - LGPL
9028  * <script type="text/javascript">
9029  */
9030
9031 /**
9032  * @class Roo.Editor
9033  * @extends Roo.Component
9034  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9035  * @constructor
9036  * Create a new Editor
9037  * @param {Roo.form.Field} field The Field object (or descendant)
9038  * @param {Object} config The config object
9039  */
9040 Roo.Editor = function(field, config){
9041     Roo.Editor.superclass.constructor.call(this, config);
9042     this.field = field;
9043     this.addEvents({
9044         /**
9045              * @event beforestartedit
9046              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9047              * false from the handler of this event.
9048              * @param {Editor} this
9049              * @param {Roo.Element} boundEl The underlying element bound to this editor
9050              * @param {Mixed} value The field value being set
9051              */
9052         "beforestartedit" : true,
9053         /**
9054              * @event startedit
9055              * Fires when this editor is displayed
9056              * @param {Roo.Element} boundEl The underlying element bound to this editor
9057              * @param {Mixed} value The starting field value
9058              */
9059         "startedit" : true,
9060         /**
9061              * @event beforecomplete
9062              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9063              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9064              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9065              * event will not fire since no edit actually occurred.
9066              * @param {Editor} this
9067              * @param {Mixed} value The current field value
9068              * @param {Mixed} startValue The original field value
9069              */
9070         "beforecomplete" : true,
9071         /**
9072              * @event complete
9073              * Fires after editing is complete and any changed value has been written to the underlying field.
9074              * @param {Editor} this
9075              * @param {Mixed} value The current field value
9076              * @param {Mixed} startValue The original field value
9077              */
9078         "complete" : true,
9079         /**
9080          * @event specialkey
9081          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9082          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9083          * @param {Roo.form.Field} this
9084          * @param {Roo.EventObject} e The event object
9085          */
9086         "specialkey" : true
9087     });
9088 };
9089
9090 Roo.extend(Roo.Editor, Roo.Component, {
9091     /**
9092      * @cfg {Boolean/String} autosize
9093      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9094      * or "height" to adopt the height only (defaults to false)
9095      */
9096     /**
9097      * @cfg {Boolean} revertInvalid
9098      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9099      * validation fails (defaults to true)
9100      */
9101     /**
9102      * @cfg {Boolean} ignoreNoChange
9103      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9104      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9105      * will never be ignored.
9106      */
9107     /**
9108      * @cfg {Boolean} hideEl
9109      * False to keep the bound element visible while the editor is displayed (defaults to true)
9110      */
9111     /**
9112      * @cfg {Mixed} value
9113      * The data value of the underlying field (defaults to "")
9114      */
9115     value : "",
9116     /**
9117      * @cfg {String} alignment
9118      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9119      */
9120     alignment: "c-c?",
9121     /**
9122      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9123      * for bottom-right shadow (defaults to "frame")
9124      */
9125     shadow : "frame",
9126     /**
9127      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9128      */
9129     constrain : false,
9130     /**
9131      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9132      */
9133     completeOnEnter : false,
9134     /**
9135      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9136      */
9137     cancelOnEsc : false,
9138     /**
9139      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9140      */
9141     updateEl : false,
9142
9143     // private
9144     onRender : function(ct, position){
9145         this.el = new Roo.Layer({
9146             shadow: this.shadow,
9147             cls: "x-editor",
9148             parentEl : ct,
9149             shim : this.shim,
9150             shadowOffset:4,
9151             id: this.id,
9152             constrain: this.constrain
9153         });
9154         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9155         if(this.field.msgTarget != 'title'){
9156             this.field.msgTarget = 'qtip';
9157         }
9158         this.field.render(this.el);
9159         if(Roo.isGecko){
9160             this.field.el.dom.setAttribute('autocomplete', 'off');
9161         }
9162         this.field.on("specialkey", this.onSpecialKey, this);
9163         if(this.swallowKeys){
9164             this.field.el.swallowEvent(['keydown','keypress']);
9165         }
9166         this.field.show();
9167         this.field.on("blur", this.onBlur, this);
9168         if(this.field.grow){
9169             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9170         }
9171     },
9172
9173     onSpecialKey : function(field, e)
9174     {
9175         //Roo.log('editor onSpecialKey');
9176         if(this.completeOnEnter && e.getKey() == e.ENTER){
9177             e.stopEvent();
9178             this.completeEdit();
9179             return;
9180         }
9181         // do not fire special key otherwise it might hide close the editor...
9182         if(e.getKey() == e.ENTER){    
9183             return;
9184         }
9185         if(this.cancelOnEsc && e.getKey() == e.ESC){
9186             this.cancelEdit();
9187             return;
9188         } 
9189         this.fireEvent('specialkey', field, e);
9190     
9191     },
9192
9193     /**
9194      * Starts the editing process and shows the editor.
9195      * @param {String/HTMLElement/Element} el The element to edit
9196      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9197       * to the innerHTML of el.
9198      */
9199     startEdit : function(el, value){
9200         if(this.editing){
9201             this.completeEdit();
9202         }
9203         this.boundEl = Roo.get(el);
9204         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9205         if(!this.rendered){
9206             this.render(this.parentEl || document.body);
9207         }
9208         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9209             return;
9210         }
9211         this.startValue = v;
9212         this.field.setValue(v);
9213         if(this.autoSize){
9214             var sz = this.boundEl.getSize();
9215             switch(this.autoSize){
9216                 case "width":
9217                 this.setSize(sz.width,  "");
9218                 break;
9219                 case "height":
9220                 this.setSize("",  sz.height);
9221                 break;
9222                 default:
9223                 this.setSize(sz.width,  sz.height);
9224             }
9225         }
9226         this.el.alignTo(this.boundEl, this.alignment);
9227         this.editing = true;
9228         if(Roo.QuickTips){
9229             Roo.QuickTips.disable();
9230         }
9231         this.show();
9232     },
9233
9234     /**
9235      * Sets the height and width of this editor.
9236      * @param {Number} width The new width
9237      * @param {Number} height The new height
9238      */
9239     setSize : function(w, h){
9240         this.field.setSize(w, h);
9241         if(this.el){
9242             this.el.sync();
9243         }
9244     },
9245
9246     /**
9247      * Realigns the editor to the bound field based on the current alignment config value.
9248      */
9249     realign : function(){
9250         this.el.alignTo(this.boundEl, this.alignment);
9251     },
9252
9253     /**
9254      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9255      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9256      */
9257     completeEdit : function(remainVisible){
9258         if(!this.editing){
9259             return;
9260         }
9261         var v = this.getValue();
9262         if(this.revertInvalid !== false && !this.field.isValid()){
9263             v = this.startValue;
9264             this.cancelEdit(true);
9265         }
9266         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9267             this.editing = false;
9268             this.hide();
9269             return;
9270         }
9271         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9272             this.editing = false;
9273             if(this.updateEl && this.boundEl){
9274                 this.boundEl.update(v);
9275             }
9276             if(remainVisible !== true){
9277                 this.hide();
9278             }
9279             this.fireEvent("complete", this, v, this.startValue);
9280         }
9281     },
9282
9283     // private
9284     onShow : function(){
9285         this.el.show();
9286         if(this.hideEl !== false){
9287             this.boundEl.hide();
9288         }
9289         this.field.show();
9290         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9291             this.fixIEFocus = true;
9292             this.deferredFocus.defer(50, this);
9293         }else{
9294             this.field.focus();
9295         }
9296         this.fireEvent("startedit", this.boundEl, this.startValue);
9297     },
9298
9299     deferredFocus : function(){
9300         if(this.editing){
9301             this.field.focus();
9302         }
9303     },
9304
9305     /**
9306      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9307      * reverted to the original starting value.
9308      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9309      * cancel (defaults to false)
9310      */
9311     cancelEdit : function(remainVisible){
9312         if(this.editing){
9313             this.setValue(this.startValue);
9314             if(remainVisible !== true){
9315                 this.hide();
9316             }
9317         }
9318     },
9319
9320     // private
9321     onBlur : function(){
9322         if(this.allowBlur !== true && this.editing){
9323             this.completeEdit();
9324         }
9325     },
9326
9327     // private
9328     onHide : function(){
9329         if(this.editing){
9330             this.completeEdit();
9331             return;
9332         }
9333         this.field.blur();
9334         if(this.field.collapse){
9335             this.field.collapse();
9336         }
9337         this.el.hide();
9338         if(this.hideEl !== false){
9339             this.boundEl.show();
9340         }
9341         if(Roo.QuickTips){
9342             Roo.QuickTips.enable();
9343         }
9344     },
9345
9346     /**
9347      * Sets the data value of the editor
9348      * @param {Mixed} value Any valid value supported by the underlying field
9349      */
9350     setValue : function(v){
9351         this.field.setValue(v);
9352     },
9353
9354     /**
9355      * Gets the data value of the editor
9356      * @return {Mixed} The data value
9357      */
9358     getValue : function(){
9359         return this.field.getValue();
9360     }
9361 });/*
9362  * Based on:
9363  * Ext JS Library 1.1.1
9364  * Copyright(c) 2006-2007, Ext JS, LLC.
9365  *
9366  * Originally Released Under LGPL - original licence link has changed is not relivant.
9367  *
9368  * Fork - LGPL
9369  * <script type="text/javascript">
9370  */
9371  
9372 /**
9373  * @class Roo.BasicDialog
9374  * @extends Roo.util.Observable
9375  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9376  * <pre><code>
9377 var dlg = new Roo.BasicDialog("my-dlg", {
9378     height: 200,
9379     width: 300,
9380     minHeight: 100,
9381     minWidth: 150,
9382     modal: true,
9383     proxyDrag: true,
9384     shadow: true
9385 });
9386 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9387 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9388 dlg.addButton('Cancel', dlg.hide, dlg);
9389 dlg.show();
9390 </code></pre>
9391   <b>A Dialog should always be a direct child of the body element.</b>
9392  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9393  * @cfg {String} title Default text to display in the title bar (defaults to null)
9394  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9395  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9396  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9397  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9398  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9399  * (defaults to null with no animation)
9400  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9401  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9402  * property for valid values (defaults to 'all')
9403  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9404  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9405  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9406  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9407  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9408  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9409  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9410  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9411  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9412  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9413  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9414  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9415  * draggable = true (defaults to false)
9416  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9417  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9418  * shadow (defaults to false)
9419  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9420  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9421  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9422  * @cfg {Array} buttons Array of buttons
9423  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9424  * @constructor
9425  * Create a new BasicDialog.
9426  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9427  * @param {Object} config Configuration options
9428  */
9429 Roo.BasicDialog = function(el, config){
9430     this.el = Roo.get(el);
9431     var dh = Roo.DomHelper;
9432     if(!this.el && config && config.autoCreate){
9433         if(typeof config.autoCreate == "object"){
9434             if(!config.autoCreate.id){
9435                 config.autoCreate.id = el;
9436             }
9437             this.el = dh.append(document.body,
9438                         config.autoCreate, true);
9439         }else{
9440             this.el = dh.append(document.body,
9441                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9442         }
9443     }
9444     el = this.el;
9445     el.setDisplayed(true);
9446     el.hide = this.hideAction;
9447     this.id = el.id;
9448     el.addClass("x-dlg");
9449
9450     Roo.apply(this, config);
9451
9452     this.proxy = el.createProxy("x-dlg-proxy");
9453     this.proxy.hide = this.hideAction;
9454     this.proxy.setOpacity(.5);
9455     this.proxy.hide();
9456
9457     if(config.width){
9458         el.setWidth(config.width);
9459     }
9460     if(config.height){
9461         el.setHeight(config.height);
9462     }
9463     this.size = el.getSize();
9464     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9465         this.xy = [config.x,config.y];
9466     }else{
9467         this.xy = el.getCenterXY(true);
9468     }
9469     /** The header element @type Roo.Element */
9470     this.header = el.child("> .x-dlg-hd");
9471     /** The body element @type Roo.Element */
9472     this.body = el.child("> .x-dlg-bd");
9473     /** The footer element @type Roo.Element */
9474     this.footer = el.child("> .x-dlg-ft");
9475
9476     if(!this.header){
9477         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9478     }
9479     if(!this.body){
9480         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9481     }
9482
9483     this.header.unselectable();
9484     if(this.title){
9485         this.header.update(this.title);
9486     }
9487     // this element allows the dialog to be focused for keyboard event
9488     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9489     this.focusEl.swallowEvent("click", true);
9490
9491     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9492
9493     // wrap the body and footer for special rendering
9494     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9495     if(this.footer){
9496         this.bwrap.dom.appendChild(this.footer.dom);
9497     }
9498
9499     this.bg = this.el.createChild({
9500         tag: "div", cls:"x-dlg-bg",
9501         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9502     });
9503     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9504
9505
9506     if(this.autoScroll !== false && !this.autoTabs){
9507         this.body.setStyle("overflow", "auto");
9508     }
9509
9510     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9511
9512     if(this.closable !== false){
9513         this.el.addClass("x-dlg-closable");
9514         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9515         this.close.on("click", this.closeClick, this);
9516         this.close.addClassOnOver("x-dlg-close-over");
9517     }
9518     if(this.collapsible !== false){
9519         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9520         this.collapseBtn.on("click", this.collapseClick, this);
9521         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9522         this.header.on("dblclick", this.collapseClick, this);
9523     }
9524     if(this.resizable !== false){
9525         this.el.addClass("x-dlg-resizable");
9526         this.resizer = new Roo.Resizable(el, {
9527             minWidth: this.minWidth || 80,
9528             minHeight:this.minHeight || 80,
9529             handles: this.resizeHandles || "all",
9530             pinned: true
9531         });
9532         this.resizer.on("beforeresize", this.beforeResize, this);
9533         this.resizer.on("resize", this.onResize, this);
9534     }
9535     if(this.draggable !== false){
9536         el.addClass("x-dlg-draggable");
9537         if (!this.proxyDrag) {
9538             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9539         }
9540         else {
9541             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9542         }
9543         dd.setHandleElId(this.header.id);
9544         dd.endDrag = this.endMove.createDelegate(this);
9545         dd.startDrag = this.startMove.createDelegate(this);
9546         dd.onDrag = this.onDrag.createDelegate(this);
9547         dd.scroll = false;
9548         this.dd = dd;
9549     }
9550     if(this.modal){
9551         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9552         this.mask.enableDisplayMode("block");
9553         this.mask.hide();
9554         this.el.addClass("x-dlg-modal");
9555     }
9556     if(this.shadow){
9557         this.shadow = new Roo.Shadow({
9558             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9559             offset : this.shadowOffset
9560         });
9561     }else{
9562         this.shadowOffset = 0;
9563     }
9564     if(Roo.useShims && this.shim !== false){
9565         this.shim = this.el.createShim();
9566         this.shim.hide = this.hideAction;
9567         this.shim.hide();
9568     }else{
9569         this.shim = false;
9570     }
9571     if(this.autoTabs){
9572         this.initTabs();
9573     }
9574     if (this.buttons) { 
9575         var bts= this.buttons;
9576         this.buttons = [];
9577         Roo.each(bts, function(b) {
9578             this.addButton(b);
9579         }, this);
9580     }
9581     
9582     
9583     this.addEvents({
9584         /**
9585          * @event keydown
9586          * Fires when a key is pressed
9587          * @param {Roo.BasicDialog} this
9588          * @param {Roo.EventObject} e
9589          */
9590         "keydown" : true,
9591         /**
9592          * @event move
9593          * Fires when this dialog is moved by the user.
9594          * @param {Roo.BasicDialog} this
9595          * @param {Number} x The new page X
9596          * @param {Number} y The new page Y
9597          */
9598         "move" : true,
9599         /**
9600          * @event resize
9601          * Fires when this dialog is resized by the user.
9602          * @param {Roo.BasicDialog} this
9603          * @param {Number} width The new width
9604          * @param {Number} height The new height
9605          */
9606         "resize" : true,
9607         /**
9608          * @event beforehide
9609          * Fires before this dialog is hidden.
9610          * @param {Roo.BasicDialog} this
9611          */
9612         "beforehide" : true,
9613         /**
9614          * @event hide
9615          * Fires when this dialog is hidden.
9616          * @param {Roo.BasicDialog} this
9617          */
9618         "hide" : true,
9619         /**
9620          * @event beforeshow
9621          * Fires before this dialog is shown.
9622          * @param {Roo.BasicDialog} this
9623          */
9624         "beforeshow" : true,
9625         /**
9626          * @event show
9627          * Fires when this dialog is shown.
9628          * @param {Roo.BasicDialog} this
9629          */
9630         "show" : true
9631     });
9632     el.on("keydown", this.onKeyDown, this);
9633     el.on("mousedown", this.toFront, this);
9634     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9635     this.el.hide();
9636     Roo.DialogManager.register(this);
9637     Roo.BasicDialog.superclass.constructor.call(this);
9638 };
9639
9640 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9641     shadowOffset: Roo.isIE ? 6 : 5,
9642     minHeight: 80,
9643     minWidth: 200,
9644     minButtonWidth: 75,
9645     defaultButton: null,
9646     buttonAlign: "right",
9647     tabTag: 'div',
9648     firstShow: true,
9649
9650     /**
9651      * Sets the dialog title text
9652      * @param {String} text The title text to display
9653      * @return {Roo.BasicDialog} this
9654      */
9655     setTitle : function(text){
9656         this.header.update(text);
9657         return this;
9658     },
9659
9660     // private
9661     closeClick : function(){
9662         this.hide();
9663     },
9664
9665     // private
9666     collapseClick : function(){
9667         this[this.collapsed ? "expand" : "collapse"]();
9668     },
9669
9670     /**
9671      * Collapses the dialog to its minimized state (only the title bar is visible).
9672      * Equivalent to the user clicking the collapse dialog button.
9673      */
9674     collapse : function(){
9675         if(!this.collapsed){
9676             this.collapsed = true;
9677             this.el.addClass("x-dlg-collapsed");
9678             this.restoreHeight = this.el.getHeight();
9679             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9680         }
9681     },
9682
9683     /**
9684      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9685      * clicking the expand dialog button.
9686      */
9687     expand : function(){
9688         if(this.collapsed){
9689             this.collapsed = false;
9690             this.el.removeClass("x-dlg-collapsed");
9691             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9692         }
9693     },
9694
9695     /**
9696      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9697      * @return {Roo.TabPanel} The tabs component
9698      */
9699     initTabs : function(){
9700         var tabs = this.getTabs();
9701         while(tabs.getTab(0)){
9702             tabs.removeTab(0);
9703         }
9704         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9705             var dom = el.dom;
9706             tabs.addTab(Roo.id(dom), dom.title);
9707             dom.title = "";
9708         });
9709         tabs.activate(0);
9710         return tabs;
9711     },
9712
9713     // private
9714     beforeResize : function(){
9715         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9716     },
9717
9718     // private
9719     onResize : function(){
9720         this.refreshSize();
9721         this.syncBodyHeight();
9722         this.adjustAssets();
9723         this.focus();
9724         this.fireEvent("resize", this, this.size.width, this.size.height);
9725     },
9726
9727     // private
9728     onKeyDown : function(e){
9729         if(this.isVisible()){
9730             this.fireEvent("keydown", this, e);
9731         }
9732     },
9733
9734     /**
9735      * Resizes the dialog.
9736      * @param {Number} width
9737      * @param {Number} height
9738      * @return {Roo.BasicDialog} this
9739      */
9740     resizeTo : function(width, height){
9741         this.el.setSize(width, height);
9742         this.size = {width: width, height: height};
9743         this.syncBodyHeight();
9744         if(this.fixedcenter){
9745             this.center();
9746         }
9747         if(this.isVisible()){
9748             this.constrainXY();
9749             this.adjustAssets();
9750         }
9751         this.fireEvent("resize", this, width, height);
9752         return this;
9753     },
9754
9755
9756     /**
9757      * Resizes the dialog to fit the specified content size.
9758      * @param {Number} width
9759      * @param {Number} height
9760      * @return {Roo.BasicDialog} this
9761      */
9762     setContentSize : function(w, h){
9763         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9764         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9765         //if(!this.el.isBorderBox()){
9766             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9767             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9768         //}
9769         if(this.tabs){
9770             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9771             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9772         }
9773         this.resizeTo(w, h);
9774         return this;
9775     },
9776
9777     /**
9778      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9779      * executed in response to a particular key being pressed while the dialog is active.
9780      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9781      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9782      * @param {Function} fn The function to call
9783      * @param {Object} scope (optional) The scope of the function
9784      * @return {Roo.BasicDialog} this
9785      */
9786     addKeyListener : function(key, fn, scope){
9787         var keyCode, shift, ctrl, alt;
9788         if(typeof key == "object" && !(key instanceof Array)){
9789             keyCode = key["key"];
9790             shift = key["shift"];
9791             ctrl = key["ctrl"];
9792             alt = key["alt"];
9793         }else{
9794             keyCode = key;
9795         }
9796         var handler = function(dlg, e){
9797             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9798                 var k = e.getKey();
9799                 if(keyCode instanceof Array){
9800                     for(var i = 0, len = keyCode.length; i < len; i++){
9801                         if(keyCode[i] == k){
9802                           fn.call(scope || window, dlg, k, e);
9803                           return;
9804                         }
9805                     }
9806                 }else{
9807                     if(k == keyCode){
9808                         fn.call(scope || window, dlg, k, e);
9809                     }
9810                 }
9811             }
9812         };
9813         this.on("keydown", handler);
9814         return this;
9815     },
9816
9817     /**
9818      * Returns the TabPanel component (creates it if it doesn't exist).
9819      * Note: If you wish to simply check for the existence of tabs without creating them,
9820      * check for a null 'tabs' property.
9821      * @return {Roo.TabPanel} The tabs component
9822      */
9823     getTabs : function(){
9824         if(!this.tabs){
9825             this.el.addClass("x-dlg-auto-tabs");
9826             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9827             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9828         }
9829         return this.tabs;
9830     },
9831
9832     /**
9833      * Adds a button to the footer section of the dialog.
9834      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9835      * object or a valid Roo.DomHelper element config
9836      * @param {Function} handler The function called when the button is clicked
9837      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9838      * @return {Roo.Button} The new button
9839      */
9840     addButton : function(config, handler, scope){
9841         var dh = Roo.DomHelper;
9842         if(!this.footer){
9843             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9844         }
9845         if(!this.btnContainer){
9846             var tb = this.footer.createChild({
9847
9848                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9849                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9850             }, null, true);
9851             this.btnContainer = tb.firstChild.firstChild.firstChild;
9852         }
9853         var bconfig = {
9854             handler: handler,
9855             scope: scope,
9856             minWidth: this.minButtonWidth,
9857             hideParent:true
9858         };
9859         if(typeof config == "string"){
9860             bconfig.text = config;
9861         }else{
9862             if(config.tag){
9863                 bconfig.dhconfig = config;
9864             }else{
9865                 Roo.apply(bconfig, config);
9866             }
9867         }
9868         var fc = false;
9869         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9870             bconfig.position = Math.max(0, bconfig.position);
9871             fc = this.btnContainer.childNodes[bconfig.position];
9872         }
9873          
9874         var btn = new Roo.Button(
9875             fc ? 
9876                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9877                 : this.btnContainer.appendChild(document.createElement("td")),
9878             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9879             bconfig
9880         );
9881         this.syncBodyHeight();
9882         if(!this.buttons){
9883             /**
9884              * Array of all the buttons that have been added to this dialog via addButton
9885              * @type Array
9886              */
9887             this.buttons = [];
9888         }
9889         this.buttons.push(btn);
9890         return btn;
9891     },
9892
9893     /**
9894      * Sets the default button to be focused when the dialog is displayed.
9895      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9896      * @return {Roo.BasicDialog} this
9897      */
9898     setDefaultButton : function(btn){
9899         this.defaultButton = btn;
9900         return this;
9901     },
9902
9903     // private
9904     getHeaderFooterHeight : function(safe){
9905         var height = 0;
9906         if(this.header){
9907            height += this.header.getHeight();
9908         }
9909         if(this.footer){
9910            var fm = this.footer.getMargins();
9911             height += (this.footer.getHeight()+fm.top+fm.bottom);
9912         }
9913         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9914         height += this.centerBg.getPadding("tb");
9915         return height;
9916     },
9917
9918     // private
9919     syncBodyHeight : function()
9920     {
9921         var bd = this.body, // the text
9922             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9923             bw = this.bwrap;
9924         var height = this.size.height - this.getHeaderFooterHeight(false);
9925         bd.setHeight(height-bd.getMargins("tb"));
9926         var hh = this.header.getHeight();
9927         var h = this.size.height-hh;
9928         cb.setHeight(h);
9929         
9930         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9931         bw.setHeight(h-cb.getPadding("tb"));
9932         
9933         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9934         bd.setWidth(bw.getWidth(true));
9935         if(this.tabs){
9936             this.tabs.syncHeight();
9937             if(Roo.isIE){
9938                 this.tabs.el.repaint();
9939             }
9940         }
9941     },
9942
9943     /**
9944      * Restores the previous state of the dialog if Roo.state is configured.
9945      * @return {Roo.BasicDialog} this
9946      */
9947     restoreState : function(){
9948         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9949         if(box && box.width){
9950             this.xy = [box.x, box.y];
9951             this.resizeTo(box.width, box.height);
9952         }
9953         return this;
9954     },
9955
9956     // private
9957     beforeShow : function(){
9958         this.expand();
9959         if(this.fixedcenter){
9960             this.xy = this.el.getCenterXY(true);
9961         }
9962         if(this.modal){
9963             Roo.get(document.body).addClass("x-body-masked");
9964             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9965             this.mask.show();
9966         }
9967         this.constrainXY();
9968     },
9969
9970     // private
9971     animShow : function(){
9972         var b = Roo.get(this.animateTarget).getBox();
9973         this.proxy.setSize(b.width, b.height);
9974         this.proxy.setLocation(b.x, b.y);
9975         this.proxy.show();
9976         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9977                     true, .35, this.showEl.createDelegate(this));
9978     },
9979
9980     /**
9981      * Shows the dialog.
9982      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9983      * @return {Roo.BasicDialog} this
9984      */
9985     show : function(animateTarget){
9986         if (this.fireEvent("beforeshow", this) === false){
9987             return;
9988         }
9989         if(this.syncHeightBeforeShow){
9990             this.syncBodyHeight();
9991         }else if(this.firstShow){
9992             this.firstShow = false;
9993             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9994         }
9995         this.animateTarget = animateTarget || this.animateTarget;
9996         if(!this.el.isVisible()){
9997             this.beforeShow();
9998             if(this.animateTarget && Roo.get(this.animateTarget)){
9999                 this.animShow();
10000             }else{
10001                 this.showEl();
10002             }
10003         }
10004         return this;
10005     },
10006
10007     // private
10008     showEl : function(){
10009         this.proxy.hide();
10010         this.el.setXY(this.xy);
10011         this.el.show();
10012         this.adjustAssets(true);
10013         this.toFront();
10014         this.focus();
10015         // IE peekaboo bug - fix found by Dave Fenwick
10016         if(Roo.isIE){
10017             this.el.repaint();
10018         }
10019         this.fireEvent("show", this);
10020     },
10021
10022     /**
10023      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10024      * dialog itself will receive focus.
10025      */
10026     focus : function(){
10027         if(this.defaultButton){
10028             this.defaultButton.focus();
10029         }else{
10030             this.focusEl.focus();
10031         }
10032     },
10033
10034     // private
10035     constrainXY : function(){
10036         if(this.constraintoviewport !== false){
10037             if(!this.viewSize){
10038                 if(this.container){
10039                     var s = this.container.getSize();
10040                     this.viewSize = [s.width, s.height];
10041                 }else{
10042                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10043                 }
10044             }
10045             var s = Roo.get(this.container||document).getScroll();
10046
10047             var x = this.xy[0], y = this.xy[1];
10048             var w = this.size.width, h = this.size.height;
10049             var vw = this.viewSize[0], vh = this.viewSize[1];
10050             // only move it if it needs it
10051             var moved = false;
10052             // first validate right/bottom
10053             if(x + w > vw+s.left){
10054                 x = vw - w;
10055                 moved = true;
10056             }
10057             if(y + h > vh+s.top){
10058                 y = vh - h;
10059                 moved = true;
10060             }
10061             // then make sure top/left isn't negative
10062             if(x < s.left){
10063                 x = s.left;
10064                 moved = true;
10065             }
10066             if(y < s.top){
10067                 y = s.top;
10068                 moved = true;
10069             }
10070             if(moved){
10071                 // cache xy
10072                 this.xy = [x, y];
10073                 if(this.isVisible()){
10074                     this.el.setLocation(x, y);
10075                     this.adjustAssets();
10076                 }
10077             }
10078         }
10079     },
10080
10081     // private
10082     onDrag : function(){
10083         if(!this.proxyDrag){
10084             this.xy = this.el.getXY();
10085             this.adjustAssets();
10086         }
10087     },
10088
10089     // private
10090     adjustAssets : function(doShow){
10091         var x = this.xy[0], y = this.xy[1];
10092         var w = this.size.width, h = this.size.height;
10093         if(doShow === true){
10094             if(this.shadow){
10095                 this.shadow.show(this.el);
10096             }
10097             if(this.shim){
10098                 this.shim.show();
10099             }
10100         }
10101         if(this.shadow && this.shadow.isVisible()){
10102             this.shadow.show(this.el);
10103         }
10104         if(this.shim && this.shim.isVisible()){
10105             this.shim.setBounds(x, y, w, h);
10106         }
10107     },
10108
10109     // private
10110     adjustViewport : function(w, h){
10111         if(!w || !h){
10112             w = Roo.lib.Dom.getViewWidth();
10113             h = Roo.lib.Dom.getViewHeight();
10114         }
10115         // cache the size
10116         this.viewSize = [w, h];
10117         if(this.modal && this.mask.isVisible()){
10118             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10119             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10120         }
10121         if(this.isVisible()){
10122             this.constrainXY();
10123         }
10124     },
10125
10126     /**
10127      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10128      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10129      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10130      */
10131     destroy : function(removeEl){
10132         if(this.isVisible()){
10133             this.animateTarget = null;
10134             this.hide();
10135         }
10136         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10137         if(this.tabs){
10138             this.tabs.destroy(removeEl);
10139         }
10140         Roo.destroy(
10141              this.shim,
10142              this.proxy,
10143              this.resizer,
10144              this.close,
10145              this.mask
10146         );
10147         if(this.dd){
10148             this.dd.unreg();
10149         }
10150         if(this.buttons){
10151            for(var i = 0, len = this.buttons.length; i < len; i++){
10152                this.buttons[i].destroy();
10153            }
10154         }
10155         this.el.removeAllListeners();
10156         if(removeEl === true){
10157             this.el.update("");
10158             this.el.remove();
10159         }
10160         Roo.DialogManager.unregister(this);
10161     },
10162
10163     // private
10164     startMove : function(){
10165         if(this.proxyDrag){
10166             this.proxy.show();
10167         }
10168         if(this.constraintoviewport !== false){
10169             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10170         }
10171     },
10172
10173     // private
10174     endMove : function(){
10175         if(!this.proxyDrag){
10176             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10177         }else{
10178             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10179             this.proxy.hide();
10180         }
10181         this.refreshSize();
10182         this.adjustAssets();
10183         this.focus();
10184         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10185     },
10186
10187     /**
10188      * Brings this dialog to the front of any other visible dialogs
10189      * @return {Roo.BasicDialog} this
10190      */
10191     toFront : function(){
10192         Roo.DialogManager.bringToFront(this);
10193         return this;
10194     },
10195
10196     /**
10197      * Sends this dialog to the back (under) of any other visible dialogs
10198      * @return {Roo.BasicDialog} this
10199      */
10200     toBack : function(){
10201         Roo.DialogManager.sendToBack(this);
10202         return this;
10203     },
10204
10205     /**
10206      * Centers this dialog in the viewport
10207      * @return {Roo.BasicDialog} this
10208      */
10209     center : function(){
10210         var xy = this.el.getCenterXY(true);
10211         this.moveTo(xy[0], xy[1]);
10212         return this;
10213     },
10214
10215     /**
10216      * Moves the dialog's top-left corner to the specified point
10217      * @param {Number} x
10218      * @param {Number} y
10219      * @return {Roo.BasicDialog} this
10220      */
10221     moveTo : function(x, y){
10222         this.xy = [x,y];
10223         if(this.isVisible()){
10224             this.el.setXY(this.xy);
10225             this.adjustAssets();
10226         }
10227         return this;
10228     },
10229
10230     /**
10231      * Aligns the dialog to the specified element
10232      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10233      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10234      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10235      * @return {Roo.BasicDialog} this
10236      */
10237     alignTo : function(element, position, offsets){
10238         this.xy = this.el.getAlignToXY(element, position, offsets);
10239         if(this.isVisible()){
10240             this.el.setXY(this.xy);
10241             this.adjustAssets();
10242         }
10243         return this;
10244     },
10245
10246     /**
10247      * Anchors an element to another element and realigns it when the window is resized.
10248      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10249      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10250      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10251      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10252      * is a number, it is used as the buffer delay (defaults to 50ms).
10253      * @return {Roo.BasicDialog} this
10254      */
10255     anchorTo : function(el, alignment, offsets, monitorScroll){
10256         var action = function(){
10257             this.alignTo(el, alignment, offsets);
10258         };
10259         Roo.EventManager.onWindowResize(action, this);
10260         var tm = typeof monitorScroll;
10261         if(tm != 'undefined'){
10262             Roo.EventManager.on(window, 'scroll', action, this,
10263                 {buffer: tm == 'number' ? monitorScroll : 50});
10264         }
10265         action.call(this);
10266         return this;
10267     },
10268
10269     /**
10270      * Returns true if the dialog is visible
10271      * @return {Boolean}
10272      */
10273     isVisible : function(){
10274         return this.el.isVisible();
10275     },
10276
10277     // private
10278     animHide : function(callback){
10279         var b = Roo.get(this.animateTarget).getBox();
10280         this.proxy.show();
10281         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10282         this.el.hide();
10283         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10284                     this.hideEl.createDelegate(this, [callback]));
10285     },
10286
10287     /**
10288      * Hides the dialog.
10289      * @param {Function} callback (optional) Function to call when the dialog is hidden
10290      * @return {Roo.BasicDialog} this
10291      */
10292     hide : function(callback){
10293         if (this.fireEvent("beforehide", this) === false){
10294             return;
10295         }
10296         if(this.shadow){
10297             this.shadow.hide();
10298         }
10299         if(this.shim) {
10300           this.shim.hide();
10301         }
10302         // sometimes animateTarget seems to get set.. causing problems...
10303         // this just double checks..
10304         if(this.animateTarget && Roo.get(this.animateTarget)) {
10305            this.animHide(callback);
10306         }else{
10307             this.el.hide();
10308             this.hideEl(callback);
10309         }
10310         return this;
10311     },
10312
10313     // private
10314     hideEl : function(callback){
10315         this.proxy.hide();
10316         if(this.modal){
10317             this.mask.hide();
10318             Roo.get(document.body).removeClass("x-body-masked");
10319         }
10320         this.fireEvent("hide", this);
10321         if(typeof callback == "function"){
10322             callback();
10323         }
10324     },
10325
10326     // private
10327     hideAction : function(){
10328         this.setLeft("-10000px");
10329         this.setTop("-10000px");
10330         this.setStyle("visibility", "hidden");
10331     },
10332
10333     // private
10334     refreshSize : function(){
10335         this.size = this.el.getSize();
10336         this.xy = this.el.getXY();
10337         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10338     },
10339
10340     // private
10341     // z-index is managed by the DialogManager and may be overwritten at any time
10342     setZIndex : function(index){
10343         if(this.modal){
10344             this.mask.setStyle("z-index", index);
10345         }
10346         if(this.shim){
10347             this.shim.setStyle("z-index", ++index);
10348         }
10349         if(this.shadow){
10350             this.shadow.setZIndex(++index);
10351         }
10352         this.el.setStyle("z-index", ++index);
10353         if(this.proxy){
10354             this.proxy.setStyle("z-index", ++index);
10355         }
10356         if(this.resizer){
10357             this.resizer.proxy.setStyle("z-index", ++index);
10358         }
10359
10360         this.lastZIndex = index;
10361     },
10362
10363     /**
10364      * Returns the element for this dialog
10365      * @return {Roo.Element} The underlying dialog Element
10366      */
10367     getEl : function(){
10368         return this.el;
10369     }
10370 });
10371
10372 /**
10373  * @class Roo.DialogManager
10374  * Provides global access to BasicDialogs that have been created and
10375  * support for z-indexing (layering) multiple open dialogs.
10376  */
10377 Roo.DialogManager = function(){
10378     var list = {};
10379     var accessList = [];
10380     var front = null;
10381
10382     // private
10383     var sortDialogs = function(d1, d2){
10384         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10385     };
10386
10387     // private
10388     var orderDialogs = function(){
10389         accessList.sort(sortDialogs);
10390         var seed = Roo.DialogManager.zseed;
10391         for(var i = 0, len = accessList.length; i < len; i++){
10392             var dlg = accessList[i];
10393             if(dlg){
10394                 dlg.setZIndex(seed + (i*10));
10395             }
10396         }
10397     };
10398
10399     return {
10400         /**
10401          * The starting z-index for BasicDialogs (defaults to 9000)
10402          * @type Number The z-index value
10403          */
10404         zseed : 9000,
10405
10406         // private
10407         register : function(dlg){
10408             list[dlg.id] = dlg;
10409             accessList.push(dlg);
10410         },
10411
10412         // private
10413         unregister : function(dlg){
10414             delete list[dlg.id];
10415             var i=0;
10416             var len=0;
10417             if(!accessList.indexOf){
10418                 for(  i = 0, len = accessList.length; i < len; i++){
10419                     if(accessList[i] == dlg){
10420                         accessList.splice(i, 1);
10421                         return;
10422                     }
10423                 }
10424             }else{
10425                  i = accessList.indexOf(dlg);
10426                 if(i != -1){
10427                     accessList.splice(i, 1);
10428                 }
10429             }
10430         },
10431
10432         /**
10433          * Gets a registered dialog by id
10434          * @param {String/Object} id The id of the dialog or a dialog
10435          * @return {Roo.BasicDialog} this
10436          */
10437         get : function(id){
10438             return typeof id == "object" ? id : list[id];
10439         },
10440
10441         /**
10442          * Brings the specified dialog to the front
10443          * @param {String/Object} dlg The id of the dialog or a dialog
10444          * @return {Roo.BasicDialog} this
10445          */
10446         bringToFront : function(dlg){
10447             dlg = this.get(dlg);
10448             if(dlg != front){
10449                 front = dlg;
10450                 dlg._lastAccess = new Date().getTime();
10451                 orderDialogs();
10452             }
10453             return dlg;
10454         },
10455
10456         /**
10457          * Sends the specified dialog to the back
10458          * @param {String/Object} dlg The id of the dialog or a dialog
10459          * @return {Roo.BasicDialog} this
10460          */
10461         sendToBack : function(dlg){
10462             dlg = this.get(dlg);
10463             dlg._lastAccess = -(new Date().getTime());
10464             orderDialogs();
10465             return dlg;
10466         },
10467
10468         /**
10469          * Hides all dialogs
10470          */
10471         hideAll : function(){
10472             for(var id in list){
10473                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10474                     list[id].hide();
10475                 }
10476             }
10477         }
10478     };
10479 }();
10480
10481 /**
10482  * @class Roo.LayoutDialog
10483  * @extends Roo.BasicDialog
10484  * Dialog which provides adjustments for working with a layout in a Dialog.
10485  * Add your necessary layout config options to the dialog's config.<br>
10486  * Example usage (including a nested layout):
10487  * <pre><code>
10488 if(!dialog){
10489     dialog = new Roo.LayoutDialog("download-dlg", {
10490         modal: true,
10491         width:600,
10492         height:450,
10493         shadow:true,
10494         minWidth:500,
10495         minHeight:350,
10496         autoTabs:true,
10497         proxyDrag:true,
10498         // layout config merges with the dialog config
10499         center:{
10500             tabPosition: "top",
10501             alwaysShowTabs: true
10502         }
10503     });
10504     dialog.addKeyListener(27, dialog.hide, dialog);
10505     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10506     dialog.addButton("Build It!", this.getDownload, this);
10507
10508     // we can even add nested layouts
10509     var innerLayout = new Roo.BorderLayout("dl-inner", {
10510         east: {
10511             initialSize: 200,
10512             autoScroll:true,
10513             split:true
10514         },
10515         center: {
10516             autoScroll:true
10517         }
10518     });
10519     innerLayout.beginUpdate();
10520     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10521     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10522     innerLayout.endUpdate(true);
10523
10524     var layout = dialog.getLayout();
10525     layout.beginUpdate();
10526     layout.add("center", new Roo.ContentPanel("standard-panel",
10527                         {title: "Download the Source", fitToFrame:true}));
10528     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10529                {title: "Build your own roo.js"}));
10530     layout.getRegion("center").showPanel(sp);
10531     layout.endUpdate();
10532 }
10533 </code></pre>
10534     * @constructor
10535     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10536     * @param {Object} config configuration options
10537   */
10538 Roo.LayoutDialog = function(el, cfg){
10539     
10540     var config=  cfg;
10541     if (typeof(cfg) == 'undefined') {
10542         config = Roo.apply({}, el);
10543         // not sure why we use documentElement here.. - it should always be body.
10544         // IE7 borks horribly if we use documentElement.
10545         // webkit also does not like documentElement - it creates a body element...
10546         el = Roo.get( document.body || document.documentElement ).createChild();
10547         //config.autoCreate = true;
10548     }
10549     
10550     
10551     config.autoTabs = false;
10552     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10553     this.body.setStyle({overflow:"hidden", position:"relative"});
10554     this.layout = new Roo.BorderLayout(this.body.dom, config);
10555     this.layout.monitorWindowResize = false;
10556     this.el.addClass("x-dlg-auto-layout");
10557     // fix case when center region overwrites center function
10558     this.center = Roo.BasicDialog.prototype.center;
10559     this.on("show", this.layout.layout, this.layout, true);
10560     if (config.items) {
10561         var xitems = config.items;
10562         delete config.items;
10563         Roo.each(xitems, this.addxtype, this);
10564     }
10565     
10566     
10567 };
10568 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10569     /**
10570      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10571      * @deprecated
10572      */
10573     endUpdate : function(){
10574         this.layout.endUpdate();
10575     },
10576
10577     /**
10578      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10579      *  @deprecated
10580      */
10581     beginUpdate : function(){
10582         this.layout.beginUpdate();
10583     },
10584
10585     /**
10586      * Get the BorderLayout for this dialog
10587      * @return {Roo.BorderLayout}
10588      */
10589     getLayout : function(){
10590         return this.layout;
10591     },
10592
10593     showEl : function(){
10594         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10595         if(Roo.isIE7){
10596             this.layout.layout();
10597         }
10598     },
10599
10600     // private
10601     // Use the syncHeightBeforeShow config option to control this automatically
10602     syncBodyHeight : function(){
10603         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10604         if(this.layout){this.layout.layout();}
10605     },
10606     
10607       /**
10608      * Add an xtype element (actually adds to the layout.)
10609      * @return {Object} xdata xtype object data.
10610      */
10611     
10612     addxtype : function(c) {
10613         return this.layout.addxtype(c);
10614     }
10615 });/*
10616  * Based on:
10617  * Ext JS Library 1.1.1
10618  * Copyright(c) 2006-2007, Ext JS, LLC.
10619  *
10620  * Originally Released Under LGPL - original licence link has changed is not relivant.
10621  *
10622  * Fork - LGPL
10623  * <script type="text/javascript">
10624  */
10625  
10626 /**
10627  * @class Roo.MessageBox
10628  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10629  * Example usage:
10630  *<pre><code>
10631 // Basic alert:
10632 Roo.Msg.alert('Status', 'Changes saved successfully.');
10633
10634 // Prompt for user data:
10635 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10636     if (btn == 'ok'){
10637         // process text value...
10638     }
10639 });
10640
10641 // Show a dialog using config options:
10642 Roo.Msg.show({
10643    title:'Save Changes?',
10644    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10645    buttons: Roo.Msg.YESNOCANCEL,
10646    fn: processResult,
10647    animEl: 'elId'
10648 });
10649 </code></pre>
10650  * @singleton
10651  */
10652 Roo.MessageBox = function(){
10653     var dlg, opt, mask, waitTimer;
10654     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10655     var buttons, activeTextEl, bwidth;
10656
10657     // private
10658     var handleButton = function(button){
10659         dlg.hide();
10660         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10661     };
10662
10663     // private
10664     var handleHide = function(){
10665         if(opt && opt.cls){
10666             dlg.el.removeClass(opt.cls);
10667         }
10668         if(waitTimer){
10669             Roo.TaskMgr.stop(waitTimer);
10670             waitTimer = null;
10671         }
10672     };
10673
10674     // private
10675     var updateButtons = function(b){
10676         var width = 0;
10677         if(!b){
10678             buttons["ok"].hide();
10679             buttons["cancel"].hide();
10680             buttons["yes"].hide();
10681             buttons["no"].hide();
10682             dlg.footer.dom.style.display = 'none';
10683             return width;
10684         }
10685         dlg.footer.dom.style.display = '';
10686         for(var k in buttons){
10687             if(typeof buttons[k] != "function"){
10688                 if(b[k]){
10689                     buttons[k].show();
10690                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10691                     width += buttons[k].el.getWidth()+15;
10692                 }else{
10693                     buttons[k].hide();
10694                 }
10695             }
10696         }
10697         return width;
10698     };
10699
10700     // private
10701     var handleEsc = function(d, k, e){
10702         if(opt && opt.closable !== false){
10703             dlg.hide();
10704         }
10705         if(e){
10706             e.stopEvent();
10707         }
10708     };
10709
10710     return {
10711         /**
10712          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10713          * @return {Roo.BasicDialog} The BasicDialog element
10714          */
10715         getDialog : function(){
10716            if(!dlg){
10717                 dlg = new Roo.BasicDialog("x-msg-box", {
10718                     autoCreate : true,
10719                     shadow: true,
10720                     draggable: true,
10721                     resizable:false,
10722                     constraintoviewport:false,
10723                     fixedcenter:true,
10724                     collapsible : false,
10725                     shim:true,
10726                     modal: true,
10727                     width:400, height:100,
10728                     buttonAlign:"center",
10729                     closeClick : function(){
10730                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10731                             handleButton("no");
10732                         }else{
10733                             handleButton("cancel");
10734                         }
10735                     }
10736                 });
10737                 dlg.on("hide", handleHide);
10738                 mask = dlg.mask;
10739                 dlg.addKeyListener(27, handleEsc);
10740                 buttons = {};
10741                 var bt = this.buttonText;
10742                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10743                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10744                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10745                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10746                 bodyEl = dlg.body.createChild({
10747
10748                     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>'
10749                 });
10750                 msgEl = bodyEl.dom.firstChild;
10751                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10752                 textboxEl.enableDisplayMode();
10753                 textboxEl.addKeyListener([10,13], function(){
10754                     if(dlg.isVisible() && opt && opt.buttons){
10755                         if(opt.buttons.ok){
10756                             handleButton("ok");
10757                         }else if(opt.buttons.yes){
10758                             handleButton("yes");
10759                         }
10760                     }
10761                 });
10762                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10763                 textareaEl.enableDisplayMode();
10764                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10765                 progressEl.enableDisplayMode();
10766                 var pf = progressEl.dom.firstChild;
10767                 if (pf) {
10768                     pp = Roo.get(pf.firstChild);
10769                     pp.setHeight(pf.offsetHeight);
10770                 }
10771                 
10772             }
10773             return dlg;
10774         },
10775
10776         /**
10777          * Updates the message box body text
10778          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10779          * the XHTML-compliant non-breaking space character '&amp;#160;')
10780          * @return {Roo.MessageBox} This message box
10781          */
10782         updateText : function(text){
10783             if(!dlg.isVisible() && !opt.width){
10784                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10785             }
10786             msgEl.innerHTML = text || '&#160;';
10787       
10788             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10789             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10790             var w = Math.max(
10791                     Math.min(opt.width || cw , this.maxWidth), 
10792                     Math.max(opt.minWidth || this.minWidth, bwidth)
10793             );
10794             if(opt.prompt){
10795                 activeTextEl.setWidth(w);
10796             }
10797             if(dlg.isVisible()){
10798                 dlg.fixedcenter = false;
10799             }
10800             // to big, make it scroll. = But as usual stupid IE does not support
10801             // !important..
10802             
10803             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10804                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10805                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10806             } else {
10807                 bodyEl.dom.style.height = '';
10808                 bodyEl.dom.style.overflowY = '';
10809             }
10810             if (cw > w) {
10811                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10812             } else {
10813                 bodyEl.dom.style.overflowX = '';
10814             }
10815             
10816             dlg.setContentSize(w, bodyEl.getHeight());
10817             if(dlg.isVisible()){
10818                 dlg.fixedcenter = true;
10819             }
10820             return this;
10821         },
10822
10823         /**
10824          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10825          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10826          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10827          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10828          * @return {Roo.MessageBox} This message box
10829          */
10830         updateProgress : function(value, text){
10831             if(text){
10832                 this.updateText(text);
10833             }
10834             if (pp) { // weird bug on my firefox - for some reason this is not defined
10835                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10836             }
10837             return this;
10838         },        
10839
10840         /**
10841          * Returns true if the message box is currently displayed
10842          * @return {Boolean} True if the message box is visible, else false
10843          */
10844         isVisible : function(){
10845             return dlg && dlg.isVisible();  
10846         },
10847
10848         /**
10849          * Hides the message box if it is displayed
10850          */
10851         hide : function(){
10852             if(this.isVisible()){
10853                 dlg.hide();
10854             }  
10855         },
10856
10857         /**
10858          * Displays a new message box, or reinitializes an existing message box, based on the config options
10859          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10860          * The following config object properties are supported:
10861          * <pre>
10862 Property    Type             Description
10863 ----------  ---------------  ------------------------------------------------------------------------------------
10864 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10865                                    closes (defaults to undefined)
10866 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10867                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10868 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10869                                    progress and wait dialogs will ignore this property and always hide the
10870                                    close button as they can only be closed programmatically.
10871 cls               String           A custom CSS class to apply to the message box element
10872 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10873                                    displayed (defaults to 75)
10874 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10875                                    function will be btn (the name of the button that was clicked, if applicable,
10876                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10877                                    Progress and wait dialogs will ignore this option since they do not respond to
10878                                    user actions and can only be closed programmatically, so any required function
10879                                    should be called by the same code after it closes the dialog.
10880 icon              String           A CSS class that provides a background image to be used as an icon for
10881                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10882 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10883 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10884 modal             Boolean          False to allow user interaction with the page while the message box is
10885                                    displayed (defaults to true)
10886 msg               String           A string that will replace the existing message box body text (defaults
10887                                    to the XHTML-compliant non-breaking space character '&#160;')
10888 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10889 progress          Boolean          True to display a progress bar (defaults to false)
10890 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10891 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10892 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10893 title             String           The title text
10894 value             String           The string value to set into the active textbox element if displayed
10895 wait              Boolean          True to display a progress bar (defaults to false)
10896 width             Number           The width of the dialog in pixels
10897 </pre>
10898          *
10899          * Example usage:
10900          * <pre><code>
10901 Roo.Msg.show({
10902    title: 'Address',
10903    msg: 'Please enter your address:',
10904    width: 300,
10905    buttons: Roo.MessageBox.OKCANCEL,
10906    multiline: true,
10907    fn: saveAddress,
10908    animEl: 'addAddressBtn'
10909 });
10910 </code></pre>
10911          * @param {Object} config Configuration options
10912          * @return {Roo.MessageBox} This message box
10913          */
10914         show : function(options)
10915         {
10916             
10917             // this causes nightmares if you show one dialog after another
10918             // especially on callbacks..
10919              
10920             if(this.isVisible()){
10921                 
10922                 this.hide();
10923                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10924                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10925                 Roo.log("New Dialog Message:" +  options.msg )
10926                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10927                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10928                 
10929             }
10930             var d = this.getDialog();
10931             opt = options;
10932             d.setTitle(opt.title || "&#160;");
10933             d.close.setDisplayed(opt.closable !== false);
10934             activeTextEl = textboxEl;
10935             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10936             if(opt.prompt){
10937                 if(opt.multiline){
10938                     textboxEl.hide();
10939                     textareaEl.show();
10940                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10941                         opt.multiline : this.defaultTextHeight);
10942                     activeTextEl = textareaEl;
10943                 }else{
10944                     textboxEl.show();
10945                     textareaEl.hide();
10946                 }
10947             }else{
10948                 textboxEl.hide();
10949                 textareaEl.hide();
10950             }
10951             progressEl.setDisplayed(opt.progress === true);
10952             this.updateProgress(0);
10953             activeTextEl.dom.value = opt.value || "";
10954             if(opt.prompt){
10955                 dlg.setDefaultButton(activeTextEl);
10956             }else{
10957                 var bs = opt.buttons;
10958                 var db = null;
10959                 if(bs && bs.ok){
10960                     db = buttons["ok"];
10961                 }else if(bs && bs.yes){
10962                     db = buttons["yes"];
10963                 }
10964                 dlg.setDefaultButton(db);
10965             }
10966             bwidth = updateButtons(opt.buttons);
10967             this.updateText(opt.msg);
10968             if(opt.cls){
10969                 d.el.addClass(opt.cls);
10970             }
10971             d.proxyDrag = opt.proxyDrag === true;
10972             d.modal = opt.modal !== false;
10973             d.mask = opt.modal !== false ? mask : false;
10974             if(!d.isVisible()){
10975                 // force it to the end of the z-index stack so it gets a cursor in FF
10976                 document.body.appendChild(dlg.el.dom);
10977                 d.animateTarget = null;
10978                 d.show(options.animEl);
10979             }
10980             return this;
10981         },
10982
10983         /**
10984          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10985          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10986          * and closing the message box when the process is complete.
10987          * @param {String} title The title bar text
10988          * @param {String} msg The message box body text
10989          * @return {Roo.MessageBox} This message box
10990          */
10991         progress : function(title, msg){
10992             this.show({
10993                 title : title,
10994                 msg : msg,
10995                 buttons: false,
10996                 progress:true,
10997                 closable:false,
10998                 minWidth: this.minProgressWidth,
10999                 modal : true
11000             });
11001             return this;
11002         },
11003
11004         /**
11005          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11006          * If a callback function is passed it will be called after the user clicks the button, and the
11007          * id of the button that was clicked will be passed as the only parameter to the callback
11008          * (could also be the top-right close button).
11009          * @param {String} title The title bar text
11010          * @param {String} msg The message box body text
11011          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11012          * @param {Object} scope (optional) The scope of the callback function
11013          * @return {Roo.MessageBox} This message box
11014          */
11015         alert : function(title, msg, fn, scope){
11016             this.show({
11017                 title : title,
11018                 msg : msg,
11019                 buttons: this.OK,
11020                 fn: fn,
11021                 scope : scope,
11022                 modal : true
11023             });
11024             return this;
11025         },
11026
11027         /**
11028          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11029          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11030          * You are responsible for closing the message box when the process is complete.
11031          * @param {String} msg The message box body text
11032          * @param {String} title (optional) The title bar text
11033          * @return {Roo.MessageBox} This message box
11034          */
11035         wait : function(msg, title){
11036             this.show({
11037                 title : title,
11038                 msg : msg,
11039                 buttons: false,
11040                 closable:false,
11041                 progress:true,
11042                 modal:true,
11043                 width:300,
11044                 wait:true
11045             });
11046             waitTimer = Roo.TaskMgr.start({
11047                 run: function(i){
11048                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11049                 },
11050                 interval: 1000
11051             });
11052             return this;
11053         },
11054
11055         /**
11056          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11057          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11058          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11059          * @param {String} title The title bar text
11060          * @param {String} msg The message box body text
11061          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11062          * @param {Object} scope (optional) The scope of the callback function
11063          * @return {Roo.MessageBox} This message box
11064          */
11065         confirm : function(title, msg, fn, scope){
11066             this.show({
11067                 title : title,
11068                 msg : msg,
11069                 buttons: this.YESNO,
11070                 fn: fn,
11071                 scope : scope,
11072                 modal : true
11073             });
11074             return this;
11075         },
11076
11077         /**
11078          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11079          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11080          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11081          * (could also be the top-right close button) and the text that was entered will be passed as the two
11082          * parameters to the callback.
11083          * @param {String} title The title bar text
11084          * @param {String} msg The message box body text
11085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11086          * @param {Object} scope (optional) The scope of the callback function
11087          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11088          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11089          * @return {Roo.MessageBox} This message box
11090          */
11091         prompt : function(title, msg, fn, scope, multiline){
11092             this.show({
11093                 title : title,
11094                 msg : msg,
11095                 buttons: this.OKCANCEL,
11096                 fn: fn,
11097                 minWidth:250,
11098                 scope : scope,
11099                 prompt:true,
11100                 multiline: multiline,
11101                 modal : true
11102             });
11103             return this;
11104         },
11105
11106         /**
11107          * Button config that displays a single OK button
11108          * @type Object
11109          */
11110         OK : {ok:true},
11111         /**
11112          * Button config that displays Yes and No buttons
11113          * @type Object
11114          */
11115         YESNO : {yes:true, no:true},
11116         /**
11117          * Button config that displays OK and Cancel buttons
11118          * @type Object
11119          */
11120         OKCANCEL : {ok:true, cancel:true},
11121         /**
11122          * Button config that displays Yes, No and Cancel buttons
11123          * @type Object
11124          */
11125         YESNOCANCEL : {yes:true, no:true, cancel:true},
11126
11127         /**
11128          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11129          * @type Number
11130          */
11131         defaultTextHeight : 75,
11132         /**
11133          * The maximum width in pixels of the message box (defaults to 600)
11134          * @type Number
11135          */
11136         maxWidth : 600,
11137         /**
11138          * The minimum width in pixels of the message box (defaults to 100)
11139          * @type Number
11140          */
11141         minWidth : 100,
11142         /**
11143          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11144          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11145          * @type Number
11146          */
11147         minProgressWidth : 250,
11148         /**
11149          * An object containing the default button text strings that can be overriden for localized language support.
11150          * Supported properties are: ok, cancel, yes and no.
11151          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11152          * @type Object
11153          */
11154         buttonText : {
11155             ok : "OK",
11156             cancel : "Cancel",
11157             yes : "Yes",
11158             no : "No"
11159         }
11160     };
11161 }();
11162
11163 /**
11164  * Shorthand for {@link Roo.MessageBox}
11165  */
11166 Roo.Msg = Roo.MessageBox;/*
11167  * Based on:
11168  * Ext JS Library 1.1.1
11169  * Copyright(c) 2006-2007, Ext JS, LLC.
11170  *
11171  * Originally Released Under LGPL - original licence link has changed is not relivant.
11172  *
11173  * Fork - LGPL
11174  * <script type="text/javascript">
11175  */
11176 /**
11177  * @class Roo.QuickTips
11178  * Provides attractive and customizable tooltips for any element.
11179  * @singleton
11180  */
11181 Roo.QuickTips = function(){
11182     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11183     var ce, bd, xy, dd;
11184     var visible = false, disabled = true, inited = false;
11185     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11186     
11187     var onOver = function(e){
11188         if(disabled){
11189             return;
11190         }
11191         var t = e.getTarget();
11192         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11193             return;
11194         }
11195         if(ce && t == ce.el){
11196             clearTimeout(hideProc);
11197             return;
11198         }
11199         if(t && tagEls[t.id]){
11200             tagEls[t.id].el = t;
11201             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11202             return;
11203         }
11204         var ttp, et = Roo.fly(t);
11205         var ns = cfg.namespace;
11206         if(tm.interceptTitles && t.title){
11207             ttp = t.title;
11208             t.qtip = ttp;
11209             t.removeAttribute("title");
11210             e.preventDefault();
11211         }else{
11212             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11213         }
11214         if(ttp){
11215             showProc = show.defer(tm.showDelay, tm, [{
11216                 el: t, 
11217                 text: ttp, 
11218                 width: et.getAttributeNS(ns, cfg.width),
11219                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11220                 title: et.getAttributeNS(ns, cfg.title),
11221                     cls: et.getAttributeNS(ns, cfg.cls)
11222             }]);
11223         }
11224     };
11225     
11226     var onOut = function(e){
11227         clearTimeout(showProc);
11228         var t = e.getTarget();
11229         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11230             hideProc = setTimeout(hide, tm.hideDelay);
11231         }
11232     };
11233     
11234     var onMove = function(e){
11235         if(disabled){
11236             return;
11237         }
11238         xy = e.getXY();
11239         xy[1] += 18;
11240         if(tm.trackMouse && ce){
11241             el.setXY(xy);
11242         }
11243     };
11244     
11245     var onDown = function(e){
11246         clearTimeout(showProc);
11247         clearTimeout(hideProc);
11248         if(!e.within(el)){
11249             if(tm.hideOnClick){
11250                 hide();
11251                 tm.disable();
11252                 tm.enable.defer(100, tm);
11253             }
11254         }
11255     };
11256     
11257     var getPad = function(){
11258         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11259     };
11260
11261     var show = function(o){
11262         if(disabled){
11263             return;
11264         }
11265         clearTimeout(dismissProc);
11266         ce = o;
11267         if(removeCls){ // in case manually hidden
11268             el.removeClass(removeCls);
11269             removeCls = null;
11270         }
11271         if(ce.cls){
11272             el.addClass(ce.cls);
11273             removeCls = ce.cls;
11274         }
11275         if(ce.title){
11276             tipTitle.update(ce.title);
11277             tipTitle.show();
11278         }else{
11279             tipTitle.update('');
11280             tipTitle.hide();
11281         }
11282         el.dom.style.width  = tm.maxWidth+'px';
11283         //tipBody.dom.style.width = '';
11284         tipBodyText.update(o.text);
11285         var p = getPad(), w = ce.width;
11286         if(!w){
11287             var td = tipBodyText.dom;
11288             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11289             if(aw > tm.maxWidth){
11290                 w = tm.maxWidth;
11291             }else if(aw < tm.minWidth){
11292                 w = tm.minWidth;
11293             }else{
11294                 w = aw;
11295             }
11296         }
11297         //tipBody.setWidth(w);
11298         el.setWidth(parseInt(w, 10) + p);
11299         if(ce.autoHide === false){
11300             close.setDisplayed(true);
11301             if(dd){
11302                 dd.unlock();
11303             }
11304         }else{
11305             close.setDisplayed(false);
11306             if(dd){
11307                 dd.lock();
11308             }
11309         }
11310         if(xy){
11311             el.avoidY = xy[1]-18;
11312             el.setXY(xy);
11313         }
11314         if(tm.animate){
11315             el.setOpacity(.1);
11316             el.setStyle("visibility", "visible");
11317             el.fadeIn({callback: afterShow});
11318         }else{
11319             afterShow();
11320         }
11321     };
11322     
11323     var afterShow = function(){
11324         if(ce){
11325             el.show();
11326             esc.enable();
11327             if(tm.autoDismiss && ce.autoHide !== false){
11328                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11329             }
11330         }
11331     };
11332     
11333     var hide = function(noanim){
11334         clearTimeout(dismissProc);
11335         clearTimeout(hideProc);
11336         ce = null;
11337         if(el.isVisible()){
11338             esc.disable();
11339             if(noanim !== true && tm.animate){
11340                 el.fadeOut({callback: afterHide});
11341             }else{
11342                 afterHide();
11343             } 
11344         }
11345     };
11346     
11347     var afterHide = function(){
11348         el.hide();
11349         if(removeCls){
11350             el.removeClass(removeCls);
11351             removeCls = null;
11352         }
11353     };
11354     
11355     return {
11356         /**
11357         * @cfg {Number} minWidth
11358         * The minimum width of the quick tip (defaults to 40)
11359         */
11360        minWidth : 40,
11361         /**
11362         * @cfg {Number} maxWidth
11363         * The maximum width of the quick tip (defaults to 300)
11364         */
11365        maxWidth : 300,
11366         /**
11367         * @cfg {Boolean} interceptTitles
11368         * True to automatically use the element's DOM title value if available (defaults to false)
11369         */
11370        interceptTitles : false,
11371         /**
11372         * @cfg {Boolean} trackMouse
11373         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11374         */
11375        trackMouse : false,
11376         /**
11377         * @cfg {Boolean} hideOnClick
11378         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11379         */
11380        hideOnClick : true,
11381         /**
11382         * @cfg {Number} showDelay
11383         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11384         */
11385        showDelay : 500,
11386         /**
11387         * @cfg {Number} hideDelay
11388         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11389         */
11390        hideDelay : 200,
11391         /**
11392         * @cfg {Boolean} autoHide
11393         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11394         * Used in conjunction with hideDelay.
11395         */
11396        autoHide : true,
11397         /**
11398         * @cfg {Boolean}
11399         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11400         * (defaults to true).  Used in conjunction with autoDismissDelay.
11401         */
11402        autoDismiss : true,
11403         /**
11404         * @cfg {Number}
11405         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11406         */
11407        autoDismissDelay : 5000,
11408        /**
11409         * @cfg {Boolean} animate
11410         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11411         */
11412        animate : false,
11413
11414        /**
11415         * @cfg {String} title
11416         * Title text to display (defaults to '').  This can be any valid HTML markup.
11417         */
11418         title: '',
11419        /**
11420         * @cfg {String} text
11421         * Body text to display (defaults to '').  This can be any valid HTML markup.
11422         */
11423         text : '',
11424        /**
11425         * @cfg {String} cls
11426         * A CSS class to apply to the base quick tip element (defaults to '').
11427         */
11428         cls : '',
11429        /**
11430         * @cfg {Number} width
11431         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11432         * minWidth or maxWidth.
11433         */
11434         width : null,
11435
11436     /**
11437      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11438      * or display QuickTips in a page.
11439      */
11440        init : function(){
11441           tm = Roo.QuickTips;
11442           cfg = tm.tagConfig;
11443           if(!inited){
11444               if(!Roo.isReady){ // allow calling of init() before onReady
11445                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11446                   return;
11447               }
11448               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11449               el.fxDefaults = {stopFx: true};
11450               // maximum custom styling
11451               //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>');
11452               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>');              
11453               tipTitle = el.child('h3');
11454               tipTitle.enableDisplayMode("block");
11455               tipBody = el.child('div.x-tip-bd');
11456               tipBodyText = el.child('div.x-tip-bd-inner');
11457               //bdLeft = el.child('div.x-tip-bd-left');
11458               //bdRight = el.child('div.x-tip-bd-right');
11459               close = el.child('div.x-tip-close');
11460               close.enableDisplayMode("block");
11461               close.on("click", hide);
11462               var d = Roo.get(document);
11463               d.on("mousedown", onDown);
11464               d.on("mouseover", onOver);
11465               d.on("mouseout", onOut);
11466               d.on("mousemove", onMove);
11467               esc = d.addKeyListener(27, hide);
11468               esc.disable();
11469               if(Roo.dd.DD){
11470                   dd = el.initDD("default", null, {
11471                       onDrag : function(){
11472                           el.sync();  
11473                       }
11474                   });
11475                   dd.setHandleElId(tipTitle.id);
11476                   dd.lock();
11477               }
11478               inited = true;
11479           }
11480           this.enable(); 
11481        },
11482
11483     /**
11484      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11485      * are supported:
11486      * <pre>
11487 Property    Type                   Description
11488 ----------  ---------------------  ------------------------------------------------------------------------
11489 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11490      * </ul>
11491      * @param {Object} config The config object
11492      */
11493        register : function(config){
11494            var cs = config instanceof Array ? config : arguments;
11495            for(var i = 0, len = cs.length; i < len; i++) {
11496                var c = cs[i];
11497                var target = c.target;
11498                if(target){
11499                    if(target instanceof Array){
11500                        for(var j = 0, jlen = target.length; j < jlen; j++){
11501                            tagEls[target[j]] = c;
11502                        }
11503                    }else{
11504                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11505                    }
11506                }
11507            }
11508        },
11509
11510     /**
11511      * Removes this quick tip from its element and destroys it.
11512      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11513      */
11514        unregister : function(el){
11515            delete tagEls[Roo.id(el)];
11516        },
11517
11518     /**
11519      * Enable this quick tip.
11520      */
11521        enable : function(){
11522            if(inited && disabled){
11523                locks.pop();
11524                if(locks.length < 1){
11525                    disabled = false;
11526                }
11527            }
11528        },
11529
11530     /**
11531      * Disable this quick tip.
11532      */
11533        disable : function(){
11534           disabled = true;
11535           clearTimeout(showProc);
11536           clearTimeout(hideProc);
11537           clearTimeout(dismissProc);
11538           if(ce){
11539               hide(true);
11540           }
11541           locks.push(1);
11542        },
11543
11544     /**
11545      * Returns true if the quick tip is enabled, else false.
11546      */
11547        isEnabled : function(){
11548             return !disabled;
11549        },
11550
11551         // private
11552        tagConfig : {
11553            namespace : "roo", // was ext?? this may break..
11554            alt_namespace : "ext",
11555            attribute : "qtip",
11556            width : "width",
11557            target : "target",
11558            title : "qtitle",
11559            hide : "hide",
11560            cls : "qclass"
11561        }
11562    };
11563 }();
11564
11565 // backwards compat
11566 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576  
11577
11578 /**
11579  * @class Roo.tree.TreePanel
11580  * @extends Roo.data.Tree
11581
11582  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11583  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11584  * @cfg {Boolean} enableDD true to enable drag and drop
11585  * @cfg {Boolean} enableDrag true to enable just drag
11586  * @cfg {Boolean} enableDrop true to enable just drop
11587  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11588  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11589  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11590  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11591  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11592  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11593  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11594  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11595  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11596  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11597  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11598  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11599  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11600  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11601  * @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>
11602  * @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>
11603  * 
11604  * @constructor
11605  * @param {String/HTMLElement/Element} el The container element
11606  * @param {Object} config
11607  */
11608 Roo.tree.TreePanel = function(el, config){
11609     var root = false;
11610     var loader = false;
11611     if (config.root) {
11612         root = config.root;
11613         delete config.root;
11614     }
11615     if (config.loader) {
11616         loader = config.loader;
11617         delete config.loader;
11618     }
11619     
11620     Roo.apply(this, config);
11621     Roo.tree.TreePanel.superclass.constructor.call(this);
11622     this.el = Roo.get(el);
11623     this.el.addClass('x-tree');
11624     //console.log(root);
11625     if (root) {
11626         this.setRootNode( Roo.factory(root, Roo.tree));
11627     }
11628     if (loader) {
11629         this.loader = Roo.factory(loader, Roo.tree);
11630     }
11631    /**
11632     * Read-only. The id of the container element becomes this TreePanel's id.
11633     */
11634     this.id = this.el.id;
11635     this.addEvents({
11636         /**
11637         * @event beforeload
11638         * Fires before a node is loaded, return false to cancel
11639         * @param {Node} node The node being loaded
11640         */
11641         "beforeload" : true,
11642         /**
11643         * @event load
11644         * Fires when a node is loaded
11645         * @param {Node} node The node that was loaded
11646         */
11647         "load" : true,
11648         /**
11649         * @event textchange
11650         * Fires when the text for a node is changed
11651         * @param {Node} node The node
11652         * @param {String} text The new text
11653         * @param {String} oldText The old text
11654         */
11655         "textchange" : true,
11656         /**
11657         * @event beforeexpand
11658         * Fires before a node is expanded, return false to cancel.
11659         * @param {Node} node The node
11660         * @param {Boolean} deep
11661         * @param {Boolean} anim
11662         */
11663         "beforeexpand" : true,
11664         /**
11665         * @event beforecollapse
11666         * Fires before a node is collapsed, return false to cancel.
11667         * @param {Node} node The node
11668         * @param {Boolean} deep
11669         * @param {Boolean} anim
11670         */
11671         "beforecollapse" : true,
11672         /**
11673         * @event expand
11674         * Fires when a node is expanded
11675         * @param {Node} node The node
11676         */
11677         "expand" : true,
11678         /**
11679         * @event disabledchange
11680         * Fires when the disabled status of a node changes
11681         * @param {Node} node The node
11682         * @param {Boolean} disabled
11683         */
11684         "disabledchange" : true,
11685         /**
11686         * @event collapse
11687         * Fires when a node is collapsed
11688         * @param {Node} node The node
11689         */
11690         "collapse" : true,
11691         /**
11692         * @event beforeclick
11693         * Fires before click processing on a node. Return false to cancel the default action.
11694         * @param {Node} node The node
11695         * @param {Roo.EventObject} e The event object
11696         */
11697         "beforeclick":true,
11698         /**
11699         * @event checkchange
11700         * Fires when a node with a checkbox's checked property changes
11701         * @param {Node} this This node
11702         * @param {Boolean} checked
11703         */
11704         "checkchange":true,
11705         /**
11706         * @event click
11707         * Fires when a node is clicked
11708         * @param {Node} node The node
11709         * @param {Roo.EventObject} e The event object
11710         */
11711         "click":true,
11712         /**
11713         * @event dblclick
11714         * Fires when a node is double clicked
11715         * @param {Node} node The node
11716         * @param {Roo.EventObject} e The event object
11717         */
11718         "dblclick":true,
11719         /**
11720         * @event contextmenu
11721         * Fires when a node is right clicked
11722         * @param {Node} node The node
11723         * @param {Roo.EventObject} e The event object
11724         */
11725         "contextmenu":true,
11726         /**
11727         * @event beforechildrenrendered
11728         * Fires right before the child nodes for a node are rendered
11729         * @param {Node} node The node
11730         */
11731         "beforechildrenrendered":true,
11732         /**
11733         * @event startdrag
11734         * Fires when a node starts being dragged
11735         * @param {Roo.tree.TreePanel} this
11736         * @param {Roo.tree.TreeNode} node
11737         * @param {event} e The raw browser event
11738         */ 
11739        "startdrag" : true,
11740        /**
11741         * @event enddrag
11742         * Fires when a drag operation is complete
11743         * @param {Roo.tree.TreePanel} this
11744         * @param {Roo.tree.TreeNode} node
11745         * @param {event} e The raw browser event
11746         */
11747        "enddrag" : true,
11748        /**
11749         * @event dragdrop
11750         * Fires when a dragged node is dropped on a valid DD target
11751         * @param {Roo.tree.TreePanel} this
11752         * @param {Roo.tree.TreeNode} node
11753         * @param {DD} dd The dd it was dropped on
11754         * @param {event} e The raw browser event
11755         */
11756        "dragdrop" : true,
11757        /**
11758         * @event beforenodedrop
11759         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11760         * passed to handlers has the following properties:<br />
11761         * <ul style="padding:5px;padding-left:16px;">
11762         * <li>tree - The TreePanel</li>
11763         * <li>target - The node being targeted for the drop</li>
11764         * <li>data - The drag data from the drag source</li>
11765         * <li>point - The point of the drop - append, above or below</li>
11766         * <li>source - The drag source</li>
11767         * <li>rawEvent - Raw mouse event</li>
11768         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11769         * to be inserted by setting them on this object.</li>
11770         * <li>cancel - Set this to true to cancel the drop.</li>
11771         * </ul>
11772         * @param {Object} dropEvent
11773         */
11774        "beforenodedrop" : true,
11775        /**
11776         * @event nodedrop
11777         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11778         * passed to handlers has the following properties:<br />
11779         * <ul style="padding:5px;padding-left:16px;">
11780         * <li>tree - The TreePanel</li>
11781         * <li>target - The node being targeted for the drop</li>
11782         * <li>data - The drag data from the drag source</li>
11783         * <li>point - The point of the drop - append, above or below</li>
11784         * <li>source - The drag source</li>
11785         * <li>rawEvent - Raw mouse event</li>
11786         * <li>dropNode - Dropped node(s).</li>
11787         * </ul>
11788         * @param {Object} dropEvent
11789         */
11790        "nodedrop" : true,
11791         /**
11792         * @event nodedragover
11793         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11794         * passed to handlers has the following properties:<br />
11795         * <ul style="padding:5px;padding-left:16px;">
11796         * <li>tree - The TreePanel</li>
11797         * <li>target - The node being targeted for the drop</li>
11798         * <li>data - The drag data from the drag source</li>
11799         * <li>point - The point of the drop - append, above or below</li>
11800         * <li>source - The drag source</li>
11801         * <li>rawEvent - Raw mouse event</li>
11802         * <li>dropNode - Drop node(s) provided by the source.</li>
11803         * <li>cancel - Set this to true to signal drop not allowed.</li>
11804         * </ul>
11805         * @param {Object} dragOverEvent
11806         */
11807        "nodedragover" : true
11808         
11809     });
11810     if(this.singleExpand){
11811        this.on("beforeexpand", this.restrictExpand, this);
11812     }
11813     if (this.editor) {
11814         this.editor.tree = this;
11815         this.editor = Roo.factory(this.editor, Roo.tree);
11816     }
11817     
11818     if (this.selModel) {
11819         this.selModel = Roo.factory(this.selModel, Roo.tree);
11820     }
11821    
11822 };
11823 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11824     rootVisible : true,
11825     animate: Roo.enableFx,
11826     lines : true,
11827     enableDD : false,
11828     hlDrop : Roo.enableFx,
11829   
11830     renderer: false,
11831     
11832     rendererTip: false,
11833     // private
11834     restrictExpand : function(node){
11835         var p = node.parentNode;
11836         if(p){
11837             if(p.expandedChild && p.expandedChild.parentNode == p){
11838                 p.expandedChild.collapse();
11839             }
11840             p.expandedChild = node;
11841         }
11842     },
11843
11844     // private override
11845     setRootNode : function(node){
11846         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11847         if(!this.rootVisible){
11848             node.ui = new Roo.tree.RootTreeNodeUI(node);
11849         }
11850         return node;
11851     },
11852
11853     /**
11854      * Returns the container element for this TreePanel
11855      */
11856     getEl : function(){
11857         return this.el;
11858     },
11859
11860     /**
11861      * Returns the default TreeLoader for this TreePanel
11862      */
11863     getLoader : function(){
11864         return this.loader;
11865     },
11866
11867     /**
11868      * Expand all nodes
11869      */
11870     expandAll : function(){
11871         this.root.expand(true);
11872     },
11873
11874     /**
11875      * Collapse all nodes
11876      */
11877     collapseAll : function(){
11878         this.root.collapse(true);
11879     },
11880
11881     /**
11882      * Returns the selection model used by this TreePanel
11883      */
11884     getSelectionModel : function(){
11885         if(!this.selModel){
11886             this.selModel = new Roo.tree.DefaultSelectionModel();
11887         }
11888         return this.selModel;
11889     },
11890
11891     /**
11892      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11893      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11894      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11895      * @return {Array}
11896      */
11897     getChecked : function(a, startNode){
11898         startNode = startNode || this.root;
11899         var r = [];
11900         var f = function(){
11901             if(this.attributes.checked){
11902                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11903             }
11904         }
11905         startNode.cascade(f);
11906         return r;
11907     },
11908
11909     /**
11910      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11911      * @param {String} path
11912      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11913      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11914      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11915      */
11916     expandPath : function(path, attr, callback){
11917         attr = attr || "id";
11918         var keys = path.split(this.pathSeparator);
11919         var curNode = this.root;
11920         if(curNode.attributes[attr] != keys[1]){ // invalid root
11921             if(callback){
11922                 callback(false, null);
11923             }
11924             return;
11925         }
11926         var index = 1;
11927         var f = function(){
11928             if(++index == keys.length){
11929                 if(callback){
11930                     callback(true, curNode);
11931                 }
11932                 return;
11933             }
11934             var c = curNode.findChild(attr, keys[index]);
11935             if(!c){
11936                 if(callback){
11937                     callback(false, curNode);
11938                 }
11939                 return;
11940             }
11941             curNode = c;
11942             c.expand(false, false, f);
11943         };
11944         curNode.expand(false, false, f);
11945     },
11946
11947     /**
11948      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11949      * @param {String} path
11950      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11951      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11952      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11953      */
11954     selectPath : function(path, attr, callback){
11955         attr = attr || "id";
11956         var keys = path.split(this.pathSeparator);
11957         var v = keys.pop();
11958         if(keys.length > 0){
11959             var f = function(success, node){
11960                 if(success && node){
11961                     var n = node.findChild(attr, v);
11962                     if(n){
11963                         n.select();
11964                         if(callback){
11965                             callback(true, n);
11966                         }
11967                     }else if(callback){
11968                         callback(false, n);
11969                     }
11970                 }else{
11971                     if(callback){
11972                         callback(false, n);
11973                     }
11974                 }
11975             };
11976             this.expandPath(keys.join(this.pathSeparator), attr, f);
11977         }else{
11978             this.root.select();
11979             if(callback){
11980                 callback(true, this.root);
11981             }
11982         }
11983     },
11984
11985     getTreeEl : function(){
11986         return this.el;
11987     },
11988
11989     /**
11990      * Trigger rendering of this TreePanel
11991      */
11992     render : function(){
11993         if (this.innerCt) {
11994             return this; // stop it rendering more than once!!
11995         }
11996         
11997         this.innerCt = this.el.createChild({tag:"ul",
11998                cls:"x-tree-root-ct " +
11999                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12000
12001         if(this.containerScroll){
12002             Roo.dd.ScrollManager.register(this.el);
12003         }
12004         if((this.enableDD || this.enableDrop) && !this.dropZone){
12005            /**
12006             * The dropZone used by this tree if drop is enabled
12007             * @type Roo.tree.TreeDropZone
12008             */
12009              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12010                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12011            });
12012         }
12013         if((this.enableDD || this.enableDrag) && !this.dragZone){
12014            /**
12015             * The dragZone used by this tree if drag is enabled
12016             * @type Roo.tree.TreeDragZone
12017             */
12018             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12019                ddGroup: this.ddGroup || "TreeDD",
12020                scroll: this.ddScroll
12021            });
12022         }
12023         this.getSelectionModel().init(this);
12024         if (!this.root) {
12025             Roo.log("ROOT not set in tree");
12026             return this;
12027         }
12028         this.root.render();
12029         if(!this.rootVisible){
12030             this.root.renderChildren();
12031         }
12032         return this;
12033     }
12034 });/*
12035  * Based on:
12036  * Ext JS Library 1.1.1
12037  * Copyright(c) 2006-2007, Ext JS, LLC.
12038  *
12039  * Originally Released Under LGPL - original licence link has changed is not relivant.
12040  *
12041  * Fork - LGPL
12042  * <script type="text/javascript">
12043  */
12044  
12045
12046 /**
12047  * @class Roo.tree.DefaultSelectionModel
12048  * @extends Roo.util.Observable
12049  * The default single selection for a TreePanel.
12050  * @param {Object} cfg Configuration
12051  */
12052 Roo.tree.DefaultSelectionModel = function(cfg){
12053    this.selNode = null;
12054    
12055    
12056    
12057    this.addEvents({
12058        /**
12059         * @event selectionchange
12060         * Fires when the selected node changes
12061         * @param {DefaultSelectionModel} this
12062         * @param {TreeNode} node the new selection
12063         */
12064        "selectionchange" : true,
12065
12066        /**
12067         * @event beforeselect
12068         * Fires before the selected node changes, return false to cancel the change
12069         * @param {DefaultSelectionModel} this
12070         * @param {TreeNode} node the new selection
12071         * @param {TreeNode} node the old selection
12072         */
12073        "beforeselect" : true
12074    });
12075    
12076     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12077 };
12078
12079 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12080     init : function(tree){
12081         this.tree = tree;
12082         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12083         tree.on("click", this.onNodeClick, this);
12084     },
12085     
12086     onNodeClick : function(node, e){
12087         if (e.ctrlKey && this.selNode == node)  {
12088             this.unselect(node);
12089             return;
12090         }
12091         this.select(node);
12092     },
12093     
12094     /**
12095      * Select a node.
12096      * @param {TreeNode} node The node to select
12097      * @return {TreeNode} The selected node
12098      */
12099     select : function(node){
12100         var last = this.selNode;
12101         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12102             if(last){
12103                 last.ui.onSelectedChange(false);
12104             }
12105             this.selNode = node;
12106             node.ui.onSelectedChange(true);
12107             this.fireEvent("selectionchange", this, node, last);
12108         }
12109         return node;
12110     },
12111     
12112     /**
12113      * Deselect a node.
12114      * @param {TreeNode} node The node to unselect
12115      */
12116     unselect : function(node){
12117         if(this.selNode == node){
12118             this.clearSelections();
12119         }    
12120     },
12121     
12122     /**
12123      * Clear all selections
12124      */
12125     clearSelections : function(){
12126         var n = this.selNode;
12127         if(n){
12128             n.ui.onSelectedChange(false);
12129             this.selNode = null;
12130             this.fireEvent("selectionchange", this, null);
12131         }
12132         return n;
12133     },
12134     
12135     /**
12136      * Get the selected node
12137      * @return {TreeNode} The selected node
12138      */
12139     getSelectedNode : function(){
12140         return this.selNode;    
12141     },
12142     
12143     /**
12144      * Returns true if the node is selected
12145      * @param {TreeNode} node The node to check
12146      * @return {Boolean}
12147      */
12148     isSelected : function(node){
12149         return this.selNode == node;  
12150     },
12151
12152     /**
12153      * Selects the node above the selected node in the tree, intelligently walking the nodes
12154      * @return TreeNode The new selection
12155      */
12156     selectPrevious : function(){
12157         var s = this.selNode || this.lastSelNode;
12158         if(!s){
12159             return null;
12160         }
12161         var ps = s.previousSibling;
12162         if(ps){
12163             if(!ps.isExpanded() || ps.childNodes.length < 1){
12164                 return this.select(ps);
12165             } else{
12166                 var lc = ps.lastChild;
12167                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12168                     lc = lc.lastChild;
12169                 }
12170                 return this.select(lc);
12171             }
12172         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12173             return this.select(s.parentNode);
12174         }
12175         return null;
12176     },
12177
12178     /**
12179      * Selects the node above the selected node in the tree, intelligently walking the nodes
12180      * @return TreeNode The new selection
12181      */
12182     selectNext : function(){
12183         var s = this.selNode || this.lastSelNode;
12184         if(!s){
12185             return null;
12186         }
12187         if(s.firstChild && s.isExpanded()){
12188              return this.select(s.firstChild);
12189          }else if(s.nextSibling){
12190              return this.select(s.nextSibling);
12191          }else if(s.parentNode){
12192             var newS = null;
12193             s.parentNode.bubble(function(){
12194                 if(this.nextSibling){
12195                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12196                     return false;
12197                 }
12198             });
12199             return newS;
12200          }
12201         return null;
12202     },
12203
12204     onKeyDown : function(e){
12205         var s = this.selNode || this.lastSelNode;
12206         // undesirable, but required
12207         var sm = this;
12208         if(!s){
12209             return;
12210         }
12211         var k = e.getKey();
12212         switch(k){
12213              case e.DOWN:
12214                  e.stopEvent();
12215                  this.selectNext();
12216              break;
12217              case e.UP:
12218                  e.stopEvent();
12219                  this.selectPrevious();
12220              break;
12221              case e.RIGHT:
12222                  e.preventDefault();
12223                  if(s.hasChildNodes()){
12224                      if(!s.isExpanded()){
12225                          s.expand();
12226                      }else if(s.firstChild){
12227                          this.select(s.firstChild, e);
12228                      }
12229                  }
12230              break;
12231              case e.LEFT:
12232                  e.preventDefault();
12233                  if(s.hasChildNodes() && s.isExpanded()){
12234                      s.collapse();
12235                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12236                      this.select(s.parentNode, e);
12237                  }
12238              break;
12239         };
12240     }
12241 });
12242
12243 /**
12244  * @class Roo.tree.MultiSelectionModel
12245  * @extends Roo.util.Observable
12246  * Multi selection for a TreePanel.
12247  * @param {Object} cfg Configuration
12248  */
12249 Roo.tree.MultiSelectionModel = function(){
12250    this.selNodes = [];
12251    this.selMap = {};
12252    this.addEvents({
12253        /**
12254         * @event selectionchange
12255         * Fires when the selected nodes change
12256         * @param {MultiSelectionModel} this
12257         * @param {Array} nodes Array of the selected nodes
12258         */
12259        "selectionchange" : true
12260    });
12261    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12262    
12263 };
12264
12265 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12266     init : function(tree){
12267         this.tree = tree;
12268         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12269         tree.on("click", this.onNodeClick, this);
12270     },
12271     
12272     onNodeClick : function(node, e){
12273         this.select(node, e, e.ctrlKey);
12274     },
12275     
12276     /**
12277      * Select a node.
12278      * @param {TreeNode} node The node to select
12279      * @param {EventObject} e (optional) An event associated with the selection
12280      * @param {Boolean} keepExisting True to retain existing selections
12281      * @return {TreeNode} The selected node
12282      */
12283     select : function(node, e, keepExisting){
12284         if(keepExisting !== true){
12285             this.clearSelections(true);
12286         }
12287         if(this.isSelected(node)){
12288             this.lastSelNode = node;
12289             return node;
12290         }
12291         this.selNodes.push(node);
12292         this.selMap[node.id] = node;
12293         this.lastSelNode = node;
12294         node.ui.onSelectedChange(true);
12295         this.fireEvent("selectionchange", this, this.selNodes);
12296         return node;
12297     },
12298     
12299     /**
12300      * Deselect a node.
12301      * @param {TreeNode} node The node to unselect
12302      */
12303     unselect : function(node){
12304         if(this.selMap[node.id]){
12305             node.ui.onSelectedChange(false);
12306             var sn = this.selNodes;
12307             var index = -1;
12308             if(sn.indexOf){
12309                 index = sn.indexOf(node);
12310             }else{
12311                 for(var i = 0, len = sn.length; i < len; i++){
12312                     if(sn[i] == node){
12313                         index = i;
12314                         break;
12315                     }
12316                 }
12317             }
12318             if(index != -1){
12319                 this.selNodes.splice(index, 1);
12320             }
12321             delete this.selMap[node.id];
12322             this.fireEvent("selectionchange", this, this.selNodes);
12323         }
12324     },
12325     
12326     /**
12327      * Clear all selections
12328      */
12329     clearSelections : function(suppressEvent){
12330         var sn = this.selNodes;
12331         if(sn.length > 0){
12332             for(var i = 0, len = sn.length; i < len; i++){
12333                 sn[i].ui.onSelectedChange(false);
12334             }
12335             this.selNodes = [];
12336             this.selMap = {};
12337             if(suppressEvent !== true){
12338                 this.fireEvent("selectionchange", this, this.selNodes);
12339             }
12340         }
12341     },
12342     
12343     /**
12344      * Returns true if the node is selected
12345      * @param {TreeNode} node The node to check
12346      * @return {Boolean}
12347      */
12348     isSelected : function(node){
12349         return this.selMap[node.id] ? true : false;  
12350     },
12351     
12352     /**
12353      * Returns an array of the selected nodes
12354      * @return {Array}
12355      */
12356     getSelectedNodes : function(){
12357         return this.selNodes;    
12358     },
12359
12360     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12361
12362     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12363
12364     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12365 });/*
12366  * Based on:
12367  * Ext JS Library 1.1.1
12368  * Copyright(c) 2006-2007, Ext JS, LLC.
12369  *
12370  * Originally Released Under LGPL - original licence link has changed is not relivant.
12371  *
12372  * Fork - LGPL
12373  * <script type="text/javascript">
12374  */
12375  
12376 /**
12377  * @class Roo.tree.TreeNode
12378  * @extends Roo.data.Node
12379  * @cfg {String} text The text for this node
12380  * @cfg {Boolean} expanded true to start the node expanded
12381  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12382  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12383  * @cfg {Boolean} disabled true to start the node disabled
12384  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12385  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12386  * @cfg {String} cls A css class to be added to the node
12387  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12388  * @cfg {String} href URL of the link used for the node (defaults to #)
12389  * @cfg {String} hrefTarget target frame for the link
12390  * @cfg {String} qtip An Ext QuickTip for the node
12391  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12392  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12393  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12394  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12395  * (defaults to undefined with no checkbox rendered)
12396  * @constructor
12397  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12398  */
12399 Roo.tree.TreeNode = function(attributes){
12400     attributes = attributes || {};
12401     if(typeof attributes == "string"){
12402         attributes = {text: attributes};
12403     }
12404     this.childrenRendered = false;
12405     this.rendered = false;
12406     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12407     this.expanded = attributes.expanded === true;
12408     this.isTarget = attributes.isTarget !== false;
12409     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12410     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12411
12412     /**
12413      * Read-only. The text for this node. To change it use setText().
12414      * @type String
12415      */
12416     this.text = attributes.text;
12417     /**
12418      * True if this node is disabled.
12419      * @type Boolean
12420      */
12421     this.disabled = attributes.disabled === true;
12422
12423     this.addEvents({
12424         /**
12425         * @event textchange
12426         * Fires when the text for this node is changed
12427         * @param {Node} this This node
12428         * @param {String} text The new text
12429         * @param {String} oldText The old text
12430         */
12431         "textchange" : true,
12432         /**
12433         * @event beforeexpand
12434         * Fires before this node is expanded, return false to cancel.
12435         * @param {Node} this This node
12436         * @param {Boolean} deep
12437         * @param {Boolean} anim
12438         */
12439         "beforeexpand" : true,
12440         /**
12441         * @event beforecollapse
12442         * Fires before this node is collapsed, return false to cancel.
12443         * @param {Node} this This node
12444         * @param {Boolean} deep
12445         * @param {Boolean} anim
12446         */
12447         "beforecollapse" : true,
12448         /**
12449         * @event expand
12450         * Fires when this node is expanded
12451         * @param {Node} this This node
12452         */
12453         "expand" : true,
12454         /**
12455         * @event disabledchange
12456         * Fires when the disabled status of this node changes
12457         * @param {Node} this This node
12458         * @param {Boolean} disabled
12459         */
12460         "disabledchange" : true,
12461         /**
12462         * @event collapse
12463         * Fires when this node is collapsed
12464         * @param {Node} this This node
12465         */
12466         "collapse" : true,
12467         /**
12468         * @event beforeclick
12469         * Fires before click processing. Return false to cancel the default action.
12470         * @param {Node} this This node
12471         * @param {Roo.EventObject} e The event object
12472         */
12473         "beforeclick":true,
12474         /**
12475         * @event checkchange
12476         * Fires when a node with a checkbox's checked property changes
12477         * @param {Node} this This node
12478         * @param {Boolean} checked
12479         */
12480         "checkchange":true,
12481         /**
12482         * @event click
12483         * Fires when this node is clicked
12484         * @param {Node} this This node
12485         * @param {Roo.EventObject} e The event object
12486         */
12487         "click":true,
12488         /**
12489         * @event dblclick
12490         * Fires when this node is double clicked
12491         * @param {Node} this This node
12492         * @param {Roo.EventObject} e The event object
12493         */
12494         "dblclick":true,
12495         /**
12496         * @event contextmenu
12497         * Fires when this node is right clicked
12498         * @param {Node} this This node
12499         * @param {Roo.EventObject} e The event object
12500         */
12501         "contextmenu":true,
12502         /**
12503         * @event beforechildrenrendered
12504         * Fires right before the child nodes for this node are rendered
12505         * @param {Node} this This node
12506         */
12507         "beforechildrenrendered":true
12508     });
12509
12510     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12511
12512     /**
12513      * Read-only. The UI for this node
12514      * @type TreeNodeUI
12515      */
12516     this.ui = new uiClass(this);
12517     
12518     // finally support items[]
12519     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12520         return;
12521     }
12522     
12523     
12524     Roo.each(this.attributes.items, function(c) {
12525         this.appendChild(Roo.factory(c,Roo.Tree));
12526     }, this);
12527     delete this.attributes.items;
12528     
12529     
12530     
12531 };
12532 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12533     preventHScroll: true,
12534     /**
12535      * Returns true if this node is expanded
12536      * @return {Boolean}
12537      */
12538     isExpanded : function(){
12539         return this.expanded;
12540     },
12541
12542     /**
12543      * Returns the UI object for this node
12544      * @return {TreeNodeUI}
12545      */
12546     getUI : function(){
12547         return this.ui;
12548     },
12549
12550     // private override
12551     setFirstChild : function(node){
12552         var of = this.firstChild;
12553         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12554         if(this.childrenRendered && of && node != of){
12555             of.renderIndent(true, true);
12556         }
12557         if(this.rendered){
12558             this.renderIndent(true, true);
12559         }
12560     },
12561
12562     // private override
12563     setLastChild : function(node){
12564         var ol = this.lastChild;
12565         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12566         if(this.childrenRendered && ol && node != ol){
12567             ol.renderIndent(true, true);
12568         }
12569         if(this.rendered){
12570             this.renderIndent(true, true);
12571         }
12572     },
12573
12574     // these methods are overridden to provide lazy rendering support
12575     // private override
12576     appendChild : function()
12577     {
12578         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12579         if(node && this.childrenRendered){
12580             node.render();
12581         }
12582         this.ui.updateExpandIcon();
12583         return node;
12584     },
12585
12586     // private override
12587     removeChild : function(node){
12588         this.ownerTree.getSelectionModel().unselect(node);
12589         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12590         // if it's been rendered remove dom node
12591         if(this.childrenRendered){
12592             node.ui.remove();
12593         }
12594         if(this.childNodes.length < 1){
12595             this.collapse(false, false);
12596         }else{
12597             this.ui.updateExpandIcon();
12598         }
12599         if(!this.firstChild) {
12600             this.childrenRendered = false;
12601         }
12602         return node;
12603     },
12604
12605     // private override
12606     insertBefore : function(node, refNode){
12607         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12608         if(newNode && refNode && this.childrenRendered){
12609             node.render();
12610         }
12611         this.ui.updateExpandIcon();
12612         return newNode;
12613     },
12614
12615     /**
12616      * Sets the text for this node
12617      * @param {String} text
12618      */
12619     setText : function(text){
12620         var oldText = this.text;
12621         this.text = text;
12622         this.attributes.text = text;
12623         if(this.rendered){ // event without subscribing
12624             this.ui.onTextChange(this, text, oldText);
12625         }
12626         this.fireEvent("textchange", this, text, oldText);
12627     },
12628
12629     /**
12630      * Triggers selection of this node
12631      */
12632     select : function(){
12633         this.getOwnerTree().getSelectionModel().select(this);
12634     },
12635
12636     /**
12637      * Triggers deselection of this node
12638      */
12639     unselect : function(){
12640         this.getOwnerTree().getSelectionModel().unselect(this);
12641     },
12642
12643     /**
12644      * Returns true if this node is selected
12645      * @return {Boolean}
12646      */
12647     isSelected : function(){
12648         return this.getOwnerTree().getSelectionModel().isSelected(this);
12649     },
12650
12651     /**
12652      * Expand this node.
12653      * @param {Boolean} deep (optional) True to expand all children as well
12654      * @param {Boolean} anim (optional) false to cancel the default animation
12655      * @param {Function} callback (optional) A callback to be called when
12656      * expanding this node completes (does not wait for deep expand to complete).
12657      * Called with 1 parameter, this node.
12658      */
12659     expand : function(deep, anim, callback){
12660         if(!this.expanded){
12661             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12662                 return;
12663             }
12664             if(!this.childrenRendered){
12665                 this.renderChildren();
12666             }
12667             this.expanded = true;
12668             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12669                 this.ui.animExpand(function(){
12670                     this.fireEvent("expand", this);
12671                     if(typeof callback == "function"){
12672                         callback(this);
12673                     }
12674                     if(deep === true){
12675                         this.expandChildNodes(true);
12676                     }
12677                 }.createDelegate(this));
12678                 return;
12679             }else{
12680                 this.ui.expand();
12681                 this.fireEvent("expand", this);
12682                 if(typeof callback == "function"){
12683                     callback(this);
12684                 }
12685             }
12686         }else{
12687            if(typeof callback == "function"){
12688                callback(this);
12689            }
12690         }
12691         if(deep === true){
12692             this.expandChildNodes(true);
12693         }
12694     },
12695
12696     isHiddenRoot : function(){
12697         return this.isRoot && !this.getOwnerTree().rootVisible;
12698     },
12699
12700     /**
12701      * Collapse this node.
12702      * @param {Boolean} deep (optional) True to collapse all children as well
12703      * @param {Boolean} anim (optional) false to cancel the default animation
12704      */
12705     collapse : function(deep, anim){
12706         if(this.expanded && !this.isHiddenRoot()){
12707             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12708                 return;
12709             }
12710             this.expanded = false;
12711             if((this.getOwnerTree().animate && anim !== false) || anim){
12712                 this.ui.animCollapse(function(){
12713                     this.fireEvent("collapse", this);
12714                     if(deep === true){
12715                         this.collapseChildNodes(true);
12716                     }
12717                 }.createDelegate(this));
12718                 return;
12719             }else{
12720                 this.ui.collapse();
12721                 this.fireEvent("collapse", this);
12722             }
12723         }
12724         if(deep === true){
12725             var cs = this.childNodes;
12726             for(var i = 0, len = cs.length; i < len; i++) {
12727                 cs[i].collapse(true, false);
12728             }
12729         }
12730     },
12731
12732     // private
12733     delayedExpand : function(delay){
12734         if(!this.expandProcId){
12735             this.expandProcId = this.expand.defer(delay, this);
12736         }
12737     },
12738
12739     // private
12740     cancelExpand : function(){
12741         if(this.expandProcId){
12742             clearTimeout(this.expandProcId);
12743         }
12744         this.expandProcId = false;
12745     },
12746
12747     /**
12748      * Toggles expanded/collapsed state of the node
12749      */
12750     toggle : function(){
12751         if(this.expanded){
12752             this.collapse();
12753         }else{
12754             this.expand();
12755         }
12756     },
12757
12758     /**
12759      * Ensures all parent nodes are expanded
12760      */
12761     ensureVisible : function(callback){
12762         var tree = this.getOwnerTree();
12763         tree.expandPath(this.parentNode.getPath(), false, function(){
12764             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12765             Roo.callback(callback);
12766         }.createDelegate(this));
12767     },
12768
12769     /**
12770      * Expand all child nodes
12771      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12772      */
12773     expandChildNodes : function(deep){
12774         var cs = this.childNodes;
12775         for(var i = 0, len = cs.length; i < len; i++) {
12776                 cs[i].expand(deep);
12777         }
12778     },
12779
12780     /**
12781      * Collapse all child nodes
12782      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12783      */
12784     collapseChildNodes : function(deep){
12785         var cs = this.childNodes;
12786         for(var i = 0, len = cs.length; i < len; i++) {
12787                 cs[i].collapse(deep);
12788         }
12789     },
12790
12791     /**
12792      * Disables this node
12793      */
12794     disable : function(){
12795         this.disabled = true;
12796         this.unselect();
12797         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12798             this.ui.onDisableChange(this, true);
12799         }
12800         this.fireEvent("disabledchange", this, true);
12801     },
12802
12803     /**
12804      * Enables this node
12805      */
12806     enable : function(){
12807         this.disabled = false;
12808         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12809             this.ui.onDisableChange(this, false);
12810         }
12811         this.fireEvent("disabledchange", this, false);
12812     },
12813
12814     // private
12815     renderChildren : function(suppressEvent){
12816         if(suppressEvent !== false){
12817             this.fireEvent("beforechildrenrendered", this);
12818         }
12819         var cs = this.childNodes;
12820         for(var i = 0, len = cs.length; i < len; i++){
12821             cs[i].render(true);
12822         }
12823         this.childrenRendered = true;
12824     },
12825
12826     // private
12827     sort : function(fn, scope){
12828         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12829         if(this.childrenRendered){
12830             var cs = this.childNodes;
12831             for(var i = 0, len = cs.length; i < len; i++){
12832                 cs[i].render(true);
12833             }
12834         }
12835     },
12836
12837     // private
12838     render : function(bulkRender){
12839         this.ui.render(bulkRender);
12840         if(!this.rendered){
12841             this.rendered = true;
12842             if(this.expanded){
12843                 this.expanded = false;
12844                 this.expand(false, false);
12845             }
12846         }
12847     },
12848
12849     // private
12850     renderIndent : function(deep, refresh){
12851         if(refresh){
12852             this.ui.childIndent = null;
12853         }
12854         this.ui.renderIndent();
12855         if(deep === true && this.childrenRendered){
12856             var cs = this.childNodes;
12857             for(var i = 0, len = cs.length; i < len; i++){
12858                 cs[i].renderIndent(true, refresh);
12859             }
12860         }
12861     }
12862 });/*
12863  * Based on:
12864  * Ext JS Library 1.1.1
12865  * Copyright(c) 2006-2007, Ext JS, LLC.
12866  *
12867  * Originally Released Under LGPL - original licence link has changed is not relivant.
12868  *
12869  * Fork - LGPL
12870  * <script type="text/javascript">
12871  */
12872  
12873 /**
12874  * @class Roo.tree.AsyncTreeNode
12875  * @extends Roo.tree.TreeNode
12876  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12877  * @constructor
12878  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12879  */
12880  Roo.tree.AsyncTreeNode = function(config){
12881     this.loaded = false;
12882     this.loading = false;
12883     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12884     /**
12885     * @event beforeload
12886     * Fires before this node is loaded, return false to cancel
12887     * @param {Node} this This node
12888     */
12889     this.addEvents({'beforeload':true, 'load': true});
12890     /**
12891     * @event load
12892     * Fires when this node is loaded
12893     * @param {Node} this This node
12894     */
12895     /**
12896      * The loader used by this node (defaults to using the tree's defined loader)
12897      * @type TreeLoader
12898      * @property loader
12899      */
12900 };
12901 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12902     expand : function(deep, anim, callback){
12903         if(this.loading){ // if an async load is already running, waiting til it's done
12904             var timer;
12905             var f = function(){
12906                 if(!this.loading){ // done loading
12907                     clearInterval(timer);
12908                     this.expand(deep, anim, callback);
12909                 }
12910             }.createDelegate(this);
12911             timer = setInterval(f, 200);
12912             return;
12913         }
12914         if(!this.loaded){
12915             if(this.fireEvent("beforeload", this) === false){
12916                 return;
12917             }
12918             this.loading = true;
12919             this.ui.beforeLoad(this);
12920             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12921             if(loader){
12922                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12923                 return;
12924             }
12925         }
12926         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12927     },
12928     
12929     /**
12930      * Returns true if this node is currently loading
12931      * @return {Boolean}
12932      */
12933     isLoading : function(){
12934         return this.loading;  
12935     },
12936     
12937     loadComplete : function(deep, anim, callback){
12938         this.loading = false;
12939         this.loaded = true;
12940         this.ui.afterLoad(this);
12941         this.fireEvent("load", this);
12942         this.expand(deep, anim, callback);
12943     },
12944     
12945     /**
12946      * Returns true if this node has been loaded
12947      * @return {Boolean}
12948      */
12949     isLoaded : function(){
12950         return this.loaded;
12951     },
12952     
12953     hasChildNodes : function(){
12954         if(!this.isLeaf() && !this.loaded){
12955             return true;
12956         }else{
12957             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12958         }
12959     },
12960
12961     /**
12962      * Trigger a reload for this node
12963      * @param {Function} callback
12964      */
12965     reload : function(callback){
12966         this.collapse(false, false);
12967         while(this.firstChild){
12968             this.removeChild(this.firstChild);
12969         }
12970         this.childrenRendered = false;
12971         this.loaded = false;
12972         if(this.isHiddenRoot()){
12973             this.expanded = false;
12974         }
12975         this.expand(false, false, callback);
12976     }
12977 });/*
12978  * Based on:
12979  * Ext JS Library 1.1.1
12980  * Copyright(c) 2006-2007, Ext JS, LLC.
12981  *
12982  * Originally Released Under LGPL - original licence link has changed is not relivant.
12983  *
12984  * Fork - LGPL
12985  * <script type="text/javascript">
12986  */
12987  
12988 /**
12989  * @class Roo.tree.TreeNodeUI
12990  * @constructor
12991  * @param {Object} node The node to render
12992  * The TreeNode UI implementation is separate from the
12993  * tree implementation. Unless you are customizing the tree UI,
12994  * you should never have to use this directly.
12995  */
12996 Roo.tree.TreeNodeUI = function(node){
12997     this.node = node;
12998     this.rendered = false;
12999     this.animating = false;
13000     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13001 };
13002
13003 Roo.tree.TreeNodeUI.prototype = {
13004     removeChild : function(node){
13005         if(this.rendered){
13006             this.ctNode.removeChild(node.ui.getEl());
13007         }
13008     },
13009
13010     beforeLoad : function(){
13011          this.addClass("x-tree-node-loading");
13012     },
13013
13014     afterLoad : function(){
13015          this.removeClass("x-tree-node-loading");
13016     },
13017
13018     onTextChange : function(node, text, oldText){
13019         if(this.rendered){
13020             this.textNode.innerHTML = text;
13021         }
13022     },
13023
13024     onDisableChange : function(node, state){
13025         this.disabled = state;
13026         if(state){
13027             this.addClass("x-tree-node-disabled");
13028         }else{
13029             this.removeClass("x-tree-node-disabled");
13030         }
13031     },
13032
13033     onSelectedChange : function(state){
13034         if(state){
13035             this.focus();
13036             this.addClass("x-tree-selected");
13037         }else{
13038             //this.blur();
13039             this.removeClass("x-tree-selected");
13040         }
13041     },
13042
13043     onMove : function(tree, node, oldParent, newParent, index, refNode){
13044         this.childIndent = null;
13045         if(this.rendered){
13046             var targetNode = newParent.ui.getContainer();
13047             if(!targetNode){//target not rendered
13048                 this.holder = document.createElement("div");
13049                 this.holder.appendChild(this.wrap);
13050                 return;
13051             }
13052             var insertBefore = refNode ? refNode.ui.getEl() : null;
13053             if(insertBefore){
13054                 targetNode.insertBefore(this.wrap, insertBefore);
13055             }else{
13056                 targetNode.appendChild(this.wrap);
13057             }
13058             this.node.renderIndent(true);
13059         }
13060     },
13061
13062     addClass : function(cls){
13063         if(this.elNode){
13064             Roo.fly(this.elNode).addClass(cls);
13065         }
13066     },
13067
13068     removeClass : function(cls){
13069         if(this.elNode){
13070             Roo.fly(this.elNode).removeClass(cls);
13071         }
13072     },
13073
13074     remove : function(){
13075         if(this.rendered){
13076             this.holder = document.createElement("div");
13077             this.holder.appendChild(this.wrap);
13078         }
13079     },
13080
13081     fireEvent : function(){
13082         return this.node.fireEvent.apply(this.node, arguments);
13083     },
13084
13085     initEvents : function(){
13086         this.node.on("move", this.onMove, this);
13087         var E = Roo.EventManager;
13088         var a = this.anchor;
13089
13090         var el = Roo.fly(a, '_treeui');
13091
13092         if(Roo.isOpera){ // opera render bug ignores the CSS
13093             el.setStyle("text-decoration", "none");
13094         }
13095
13096         el.on("click", this.onClick, this);
13097         el.on("dblclick", this.onDblClick, this);
13098
13099         if(this.checkbox){
13100             Roo.EventManager.on(this.checkbox,
13101                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13102         }
13103
13104         el.on("contextmenu", this.onContextMenu, this);
13105
13106         var icon = Roo.fly(this.iconNode);
13107         icon.on("click", this.onClick, this);
13108         icon.on("dblclick", this.onDblClick, this);
13109         icon.on("contextmenu", this.onContextMenu, this);
13110         E.on(this.ecNode, "click", this.ecClick, this, true);
13111
13112         if(this.node.disabled){
13113             this.addClass("x-tree-node-disabled");
13114         }
13115         if(this.node.hidden){
13116             this.addClass("x-tree-node-disabled");
13117         }
13118         var ot = this.node.getOwnerTree();
13119         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13120         if(dd && (!this.node.isRoot || ot.rootVisible)){
13121             Roo.dd.Registry.register(this.elNode, {
13122                 node: this.node,
13123                 handles: this.getDDHandles(),
13124                 isHandle: false
13125             });
13126         }
13127     },
13128
13129     getDDHandles : function(){
13130         return [this.iconNode, this.textNode];
13131     },
13132
13133     hide : function(){
13134         if(this.rendered){
13135             this.wrap.style.display = "none";
13136         }
13137     },
13138
13139     show : function(){
13140         if(this.rendered){
13141             this.wrap.style.display = "";
13142         }
13143     },
13144
13145     onContextMenu : function(e){
13146         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13147             e.preventDefault();
13148             this.focus();
13149             this.fireEvent("contextmenu", this.node, e);
13150         }
13151     },
13152
13153     onClick : function(e){
13154         if(this.dropping){
13155             e.stopEvent();
13156             return;
13157         }
13158         if(this.fireEvent("beforeclick", this.node, e) !== false){
13159             if(!this.disabled && this.node.attributes.href){
13160                 this.fireEvent("click", this.node, e);
13161                 return;
13162             }
13163             e.preventDefault();
13164             if(this.disabled){
13165                 return;
13166             }
13167
13168             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13169                 this.node.toggle();
13170             }
13171
13172             this.fireEvent("click", this.node, e);
13173         }else{
13174             e.stopEvent();
13175         }
13176     },
13177
13178     onDblClick : function(e){
13179         e.preventDefault();
13180         if(this.disabled){
13181             return;
13182         }
13183         if(this.checkbox){
13184             this.toggleCheck();
13185         }
13186         if(!this.animating && this.node.hasChildNodes()){
13187             this.node.toggle();
13188         }
13189         this.fireEvent("dblclick", this.node, e);
13190     },
13191
13192     onCheckChange : function(){
13193         var checked = this.checkbox.checked;
13194         this.node.attributes.checked = checked;
13195         this.fireEvent('checkchange', this.node, checked);
13196     },
13197
13198     ecClick : function(e){
13199         if(!this.animating && this.node.hasChildNodes()){
13200             this.node.toggle();
13201         }
13202     },
13203
13204     startDrop : function(){
13205         this.dropping = true;
13206     },
13207
13208     // delayed drop so the click event doesn't get fired on a drop
13209     endDrop : function(){
13210        setTimeout(function(){
13211            this.dropping = false;
13212        }.createDelegate(this), 50);
13213     },
13214
13215     expand : function(){
13216         this.updateExpandIcon();
13217         this.ctNode.style.display = "";
13218     },
13219
13220     focus : function(){
13221         if(!this.node.preventHScroll){
13222             try{this.anchor.focus();
13223             }catch(e){}
13224         }else if(!Roo.isIE){
13225             try{
13226                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13227                 var l = noscroll.scrollLeft;
13228                 this.anchor.focus();
13229                 noscroll.scrollLeft = l;
13230             }catch(e){}
13231         }
13232     },
13233
13234     toggleCheck : function(value){
13235         var cb = this.checkbox;
13236         if(cb){
13237             cb.checked = (value === undefined ? !cb.checked : value);
13238         }
13239     },
13240
13241     blur : function(){
13242         try{
13243             this.anchor.blur();
13244         }catch(e){}
13245     },
13246
13247     animExpand : function(callback){
13248         var ct = Roo.get(this.ctNode);
13249         ct.stopFx();
13250         if(!this.node.hasChildNodes()){
13251             this.updateExpandIcon();
13252             this.ctNode.style.display = "";
13253             Roo.callback(callback);
13254             return;
13255         }
13256         this.animating = true;
13257         this.updateExpandIcon();
13258
13259         ct.slideIn('t', {
13260            callback : function(){
13261                this.animating = false;
13262                Roo.callback(callback);
13263             },
13264             scope: this,
13265             duration: this.node.ownerTree.duration || .25
13266         });
13267     },
13268
13269     highlight : function(){
13270         var tree = this.node.getOwnerTree();
13271         Roo.fly(this.wrap).highlight(
13272             tree.hlColor || "C3DAF9",
13273             {endColor: tree.hlBaseColor}
13274         );
13275     },
13276
13277     collapse : function(){
13278         this.updateExpandIcon();
13279         this.ctNode.style.display = "none";
13280     },
13281
13282     animCollapse : function(callback){
13283         var ct = Roo.get(this.ctNode);
13284         ct.enableDisplayMode('block');
13285         ct.stopFx();
13286
13287         this.animating = true;
13288         this.updateExpandIcon();
13289
13290         ct.slideOut('t', {
13291             callback : function(){
13292                this.animating = false;
13293                Roo.callback(callback);
13294             },
13295             scope: this,
13296             duration: this.node.ownerTree.duration || .25
13297         });
13298     },
13299
13300     getContainer : function(){
13301         return this.ctNode;
13302     },
13303
13304     getEl : function(){
13305         return this.wrap;
13306     },
13307
13308     appendDDGhost : function(ghostNode){
13309         ghostNode.appendChild(this.elNode.cloneNode(true));
13310     },
13311
13312     getDDRepairXY : function(){
13313         return Roo.lib.Dom.getXY(this.iconNode);
13314     },
13315
13316     onRender : function(){
13317         this.render();
13318     },
13319
13320     render : function(bulkRender){
13321         var n = this.node, a = n.attributes;
13322         var targetNode = n.parentNode ?
13323               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13324
13325         if(!this.rendered){
13326             this.rendered = true;
13327
13328             this.renderElements(n, a, targetNode, bulkRender);
13329
13330             if(a.qtip){
13331                if(this.textNode.setAttributeNS){
13332                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13333                    if(a.qtipTitle){
13334                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13335                    }
13336                }else{
13337                    this.textNode.setAttribute("ext:qtip", a.qtip);
13338                    if(a.qtipTitle){
13339                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13340                    }
13341                }
13342             }else if(a.qtipCfg){
13343                 a.qtipCfg.target = Roo.id(this.textNode);
13344                 Roo.QuickTips.register(a.qtipCfg);
13345             }
13346             this.initEvents();
13347             if(!this.node.expanded){
13348                 this.updateExpandIcon();
13349             }
13350         }else{
13351             if(bulkRender === true) {
13352                 targetNode.appendChild(this.wrap);
13353             }
13354         }
13355     },
13356
13357     renderElements : function(n, a, targetNode, bulkRender)
13358     {
13359         // add some indent caching, this helps performance when rendering a large tree
13360         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13361         var t = n.getOwnerTree();
13362         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13363         if (typeof(n.attributes.html) != 'undefined') {
13364             txt = n.attributes.html;
13365         }
13366         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13367         var cb = typeof a.checked == 'boolean';
13368         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13369         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13370             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13371             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13372             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13373             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13374             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13375              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13376                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13377             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13378             "</li>"];
13379
13380         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13381             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13382                                 n.nextSibling.ui.getEl(), buf.join(""));
13383         }else{
13384             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13385         }
13386
13387         this.elNode = this.wrap.childNodes[0];
13388         this.ctNode = this.wrap.childNodes[1];
13389         var cs = this.elNode.childNodes;
13390         this.indentNode = cs[0];
13391         this.ecNode = cs[1];
13392         this.iconNode = cs[2];
13393         var index = 3;
13394         if(cb){
13395             this.checkbox = cs[3];
13396             index++;
13397         }
13398         this.anchor = cs[index];
13399         this.textNode = cs[index].firstChild;
13400     },
13401
13402     getAnchor : function(){
13403         return this.anchor;
13404     },
13405
13406     getTextEl : function(){
13407         return this.textNode;
13408     },
13409
13410     getIconEl : function(){
13411         return this.iconNode;
13412     },
13413
13414     isChecked : function(){
13415         return this.checkbox ? this.checkbox.checked : false;
13416     },
13417
13418     updateExpandIcon : function(){
13419         if(this.rendered){
13420             var n = this.node, c1, c2;
13421             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13422             var hasChild = n.hasChildNodes();
13423             if(hasChild){
13424                 if(n.expanded){
13425                     cls += "-minus";
13426                     c1 = "x-tree-node-collapsed";
13427                     c2 = "x-tree-node-expanded";
13428                 }else{
13429                     cls += "-plus";
13430                     c1 = "x-tree-node-expanded";
13431                     c2 = "x-tree-node-collapsed";
13432                 }
13433                 if(this.wasLeaf){
13434                     this.removeClass("x-tree-node-leaf");
13435                     this.wasLeaf = false;
13436                 }
13437                 if(this.c1 != c1 || this.c2 != c2){
13438                     Roo.fly(this.elNode).replaceClass(c1, c2);
13439                     this.c1 = c1; this.c2 = c2;
13440                 }
13441             }else{
13442                 // this changes non-leafs into leafs if they have no children.
13443                 // it's not very rational behaviour..
13444                 
13445                 if(!this.wasLeaf && this.node.leaf){
13446                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13447                     delete this.c1;
13448                     delete this.c2;
13449                     this.wasLeaf = true;
13450                 }
13451             }
13452             var ecc = "x-tree-ec-icon "+cls;
13453             if(this.ecc != ecc){
13454                 this.ecNode.className = ecc;
13455                 this.ecc = ecc;
13456             }
13457         }
13458     },
13459
13460     getChildIndent : function(){
13461         if(!this.childIndent){
13462             var buf = [];
13463             var p = this.node;
13464             while(p){
13465                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13466                     if(!p.isLast()) {
13467                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13468                     } else {
13469                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13470                     }
13471                 }
13472                 p = p.parentNode;
13473             }
13474             this.childIndent = buf.join("");
13475         }
13476         return this.childIndent;
13477     },
13478
13479     renderIndent : function(){
13480         if(this.rendered){
13481             var indent = "";
13482             var p = this.node.parentNode;
13483             if(p){
13484                 indent = p.ui.getChildIndent();
13485             }
13486             if(this.indentMarkup != indent){ // don't rerender if not required
13487                 this.indentNode.innerHTML = indent;
13488                 this.indentMarkup = indent;
13489             }
13490             this.updateExpandIcon();
13491         }
13492     }
13493 };
13494
13495 Roo.tree.RootTreeNodeUI = function(){
13496     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13497 };
13498 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13499     render : function(){
13500         if(!this.rendered){
13501             var targetNode = this.node.ownerTree.innerCt.dom;
13502             this.node.expanded = true;
13503             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13504             this.wrap = this.ctNode = targetNode.firstChild;
13505         }
13506     },
13507     collapse : function(){
13508     },
13509     expand : function(){
13510     }
13511 });/*
13512  * Based on:
13513  * Ext JS Library 1.1.1
13514  * Copyright(c) 2006-2007, Ext JS, LLC.
13515  *
13516  * Originally Released Under LGPL - original licence link has changed is not relivant.
13517  *
13518  * Fork - LGPL
13519  * <script type="text/javascript">
13520  */
13521 /**
13522  * @class Roo.tree.TreeLoader
13523  * @extends Roo.util.Observable
13524  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13525  * nodes from a specified URL. The response must be a javascript Array definition
13526  * who's elements are node definition objects. eg:
13527  * <pre><code>
13528 {  success : true,
13529    data :      [
13530    
13531     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13532     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13533     ]
13534 }
13535
13536
13537 </code></pre>
13538  * <br><br>
13539  * The old style respose with just an array is still supported, but not recommended.
13540  * <br><br>
13541  *
13542  * A server request is sent, and child nodes are loaded only when a node is expanded.
13543  * The loading node's id is passed to the server under the parameter name "node" to
13544  * enable the server to produce the correct child nodes.
13545  * <br><br>
13546  * To pass extra parameters, an event handler may be attached to the "beforeload"
13547  * event, and the parameters specified in the TreeLoader's baseParams property:
13548  * <pre><code>
13549     myTreeLoader.on("beforeload", function(treeLoader, node) {
13550         this.baseParams.category = node.attributes.category;
13551     }, this);
13552 </code></pre><
13553  * This would pass an HTTP parameter called "category" to the server containing
13554  * the value of the Node's "category" attribute.
13555  * @constructor
13556  * Creates a new Treeloader.
13557  * @param {Object} config A config object containing config properties.
13558  */
13559 Roo.tree.TreeLoader = function(config){
13560     this.baseParams = {};
13561     this.requestMethod = "POST";
13562     Roo.apply(this, config);
13563
13564     this.addEvents({
13565     
13566         /**
13567          * @event beforeload
13568          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13569          * @param {Object} This TreeLoader object.
13570          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13571          * @param {Object} callback The callback function specified in the {@link #load} call.
13572          */
13573         beforeload : true,
13574         /**
13575          * @event load
13576          * Fires when the node has been successfuly loaded.
13577          * @param {Object} This TreeLoader object.
13578          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13579          * @param {Object} response The response object containing the data from the server.
13580          */
13581         load : true,
13582         /**
13583          * @event loadexception
13584          * Fires if the network request failed.
13585          * @param {Object} This TreeLoader object.
13586          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13587          * @param {Object} response The response object containing the data from the server.
13588          */
13589         loadexception : true,
13590         /**
13591          * @event create
13592          * Fires before a node is created, enabling you to return custom Node types 
13593          * @param {Object} This TreeLoader object.
13594          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13595          */
13596         create : true
13597     });
13598
13599     Roo.tree.TreeLoader.superclass.constructor.call(this);
13600 };
13601
13602 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13603     /**
13604     * @cfg {String} dataUrl The URL from which to request a Json string which
13605     * specifies an array of node definition object representing the child nodes
13606     * to be loaded.
13607     */
13608     /**
13609     * @cfg {String} requestMethod either GET or POST
13610     * defaults to POST (due to BC)
13611     * to be loaded.
13612     */
13613     /**
13614     * @cfg {Object} baseParams (optional) An object containing properties which
13615     * specify HTTP parameters to be passed to each request for child nodes.
13616     */
13617     /**
13618     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13619     * created by this loader. If the attributes sent by the server have an attribute in this object,
13620     * they take priority.
13621     */
13622     /**
13623     * @cfg {Object} uiProviders (optional) An object containing properties which
13624     * 
13625     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13626     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13627     * <i>uiProvider</i> attribute of a returned child node is a string rather
13628     * than a reference to a TreeNodeUI implementation, this that string value
13629     * is used as a property name in the uiProviders object. You can define the provider named
13630     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13631     */
13632     uiProviders : {},
13633
13634     /**
13635     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13636     * child nodes before loading.
13637     */
13638     clearOnLoad : true,
13639
13640     /**
13641     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13642     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13643     * Grid query { data : [ .....] }
13644     */
13645     
13646     root : false,
13647      /**
13648     * @cfg {String} queryParam (optional) 
13649     * Name of the query as it will be passed on the querystring (defaults to 'node')
13650     * eg. the request will be ?node=[id]
13651     */
13652     
13653     
13654     queryParam: false,
13655     
13656     /**
13657      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13658      * This is called automatically when a node is expanded, but may be used to reload
13659      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13660      * @param {Roo.tree.TreeNode} node
13661      * @param {Function} callback
13662      */
13663     load : function(node, callback){
13664         if(this.clearOnLoad){
13665             while(node.firstChild){
13666                 node.removeChild(node.firstChild);
13667             }
13668         }
13669         if(node.attributes.children){ // preloaded json children
13670             var cs = node.attributes.children;
13671             for(var i = 0, len = cs.length; i < len; i++){
13672                 node.appendChild(this.createNode(cs[i]));
13673             }
13674             if(typeof callback == "function"){
13675                 callback();
13676             }
13677         }else if(this.dataUrl){
13678             this.requestData(node, callback);
13679         }
13680     },
13681
13682     getParams: function(node){
13683         var buf = [], bp = this.baseParams;
13684         for(var key in bp){
13685             if(typeof bp[key] != "function"){
13686                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13687             }
13688         }
13689         var n = this.queryParam === false ? 'node' : this.queryParam;
13690         buf.push(n + "=", encodeURIComponent(node.id));
13691         return buf.join("");
13692     },
13693
13694     requestData : function(node, callback){
13695         if(this.fireEvent("beforeload", this, node, callback) !== false){
13696             this.transId = Roo.Ajax.request({
13697                 method:this.requestMethod,
13698                 url: this.dataUrl||this.url,
13699                 success: this.handleResponse,
13700                 failure: this.handleFailure,
13701                 scope: this,
13702                 argument: {callback: callback, node: node},
13703                 params: this.getParams(node)
13704             });
13705         }else{
13706             // if the load is cancelled, make sure we notify
13707             // the node that we are done
13708             if(typeof callback == "function"){
13709                 callback();
13710             }
13711         }
13712     },
13713
13714     isLoading : function(){
13715         return this.transId ? true : false;
13716     },
13717
13718     abort : function(){
13719         if(this.isLoading()){
13720             Roo.Ajax.abort(this.transId);
13721         }
13722     },
13723
13724     // private
13725     createNode : function(attr)
13726     {
13727         // apply baseAttrs, nice idea Corey!
13728         if(this.baseAttrs){
13729             Roo.applyIf(attr, this.baseAttrs);
13730         }
13731         if(this.applyLoader !== false){
13732             attr.loader = this;
13733         }
13734         // uiProvider = depreciated..
13735         
13736         if(typeof(attr.uiProvider) == 'string'){
13737            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13738                 /**  eval:var:attr */ eval(attr.uiProvider);
13739         }
13740         if(typeof(this.uiProviders['default']) != 'undefined') {
13741             attr.uiProvider = this.uiProviders['default'];
13742         }
13743         
13744         this.fireEvent('create', this, attr);
13745         
13746         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13747         return(attr.leaf ?
13748                         new Roo.tree.TreeNode(attr) :
13749                         new Roo.tree.AsyncTreeNode(attr));
13750     },
13751
13752     processResponse : function(response, node, callback)
13753     {
13754         var json = response.responseText;
13755         try {
13756             
13757             var o = Roo.decode(json);
13758             
13759             if (this.root === false && typeof(o.success) != undefined) {
13760                 this.root = 'data'; // the default behaviour for list like data..
13761                 }
13762                 
13763             if (this.root !== false &&  !o.success) {
13764                 // it's a failure condition.
13765                 var a = response.argument;
13766                 this.fireEvent("loadexception", this, a.node, response);
13767                 Roo.log("Load failed - should have a handler really");
13768                 return;
13769             }
13770             
13771             
13772             
13773             if (this.root !== false) {
13774                  o = o[this.root];
13775             }
13776             
13777             for(var i = 0, len = o.length; i < len; i++){
13778                 var n = this.createNode(o[i]);
13779                 if(n){
13780                     node.appendChild(n);
13781                 }
13782             }
13783             if(typeof callback == "function"){
13784                 callback(this, node);
13785             }
13786         }catch(e){
13787             this.handleFailure(response);
13788         }
13789     },
13790
13791     handleResponse : function(response){
13792         this.transId = false;
13793         var a = response.argument;
13794         this.processResponse(response, a.node, a.callback);
13795         this.fireEvent("load", this, a.node, response);
13796     },
13797
13798     handleFailure : function(response)
13799     {
13800         // should handle failure better..
13801         this.transId = false;
13802         var a = response.argument;
13803         this.fireEvent("loadexception", this, a.node, response);
13804         if(typeof a.callback == "function"){
13805             a.callback(this, a.node);
13806         }
13807     }
13808 });/*
13809  * Based on:
13810  * Ext JS Library 1.1.1
13811  * Copyright(c) 2006-2007, Ext JS, LLC.
13812  *
13813  * Originally Released Under LGPL - original licence link has changed is not relivant.
13814  *
13815  * Fork - LGPL
13816  * <script type="text/javascript">
13817  */
13818
13819 /**
13820 * @class Roo.tree.TreeFilter
13821 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13822 * @param {TreePanel} tree
13823 * @param {Object} config (optional)
13824  */
13825 Roo.tree.TreeFilter = function(tree, config){
13826     this.tree = tree;
13827     this.filtered = {};
13828     Roo.apply(this, config);
13829 };
13830
13831 Roo.tree.TreeFilter.prototype = {
13832     clearBlank:false,
13833     reverse:false,
13834     autoClear:false,
13835     remove:false,
13836
13837      /**
13838      * Filter the data by a specific attribute.
13839      * @param {String/RegExp} value Either string that the attribute value
13840      * should start with or a RegExp to test against the attribute
13841      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13842      * @param {TreeNode} startNode (optional) The node to start the filter at.
13843      */
13844     filter : function(value, attr, startNode){
13845         attr = attr || "text";
13846         var f;
13847         if(typeof value == "string"){
13848             var vlen = value.length;
13849             // auto clear empty filter
13850             if(vlen == 0 && this.clearBlank){
13851                 this.clear();
13852                 return;
13853             }
13854             value = value.toLowerCase();
13855             f = function(n){
13856                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13857             };
13858         }else if(value.exec){ // regex?
13859             f = function(n){
13860                 return value.test(n.attributes[attr]);
13861             };
13862         }else{
13863             throw 'Illegal filter type, must be string or regex';
13864         }
13865         this.filterBy(f, null, startNode);
13866         },
13867
13868     /**
13869      * Filter by a function. The passed function will be called with each
13870      * node in the tree (or from the startNode). If the function returns true, the node is kept
13871      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13872      * @param {Function} fn The filter function
13873      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13874      */
13875     filterBy : function(fn, scope, startNode){
13876         startNode = startNode || this.tree.root;
13877         if(this.autoClear){
13878             this.clear();
13879         }
13880         var af = this.filtered, rv = this.reverse;
13881         var f = function(n){
13882             if(n == startNode){
13883                 return true;
13884             }
13885             if(af[n.id]){
13886                 return false;
13887             }
13888             var m = fn.call(scope || n, n);
13889             if(!m || rv){
13890                 af[n.id] = n;
13891                 n.ui.hide();
13892                 return false;
13893             }
13894             return true;
13895         };
13896         startNode.cascade(f);
13897         if(this.remove){
13898            for(var id in af){
13899                if(typeof id != "function"){
13900                    var n = af[id];
13901                    if(n && n.parentNode){
13902                        n.parentNode.removeChild(n);
13903                    }
13904                }
13905            }
13906         }
13907     },
13908
13909     /**
13910      * Clears the current filter. Note: with the "remove" option
13911      * set a filter cannot be cleared.
13912      */
13913     clear : function(){
13914         var t = this.tree;
13915         var af = this.filtered;
13916         for(var id in af){
13917             if(typeof id != "function"){
13918                 var n = af[id];
13919                 if(n){
13920                     n.ui.show();
13921                 }
13922             }
13923         }
13924         this.filtered = {};
13925     }
13926 };
13927 /*
13928  * Based on:
13929  * Ext JS Library 1.1.1
13930  * Copyright(c) 2006-2007, Ext JS, LLC.
13931  *
13932  * Originally Released Under LGPL - original licence link has changed is not relivant.
13933  *
13934  * Fork - LGPL
13935  * <script type="text/javascript">
13936  */
13937  
13938
13939 /**
13940  * @class Roo.tree.TreeSorter
13941  * Provides sorting of nodes in a TreePanel
13942  * 
13943  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13944  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13945  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13946  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13947  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13948  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13949  * @constructor
13950  * @param {TreePanel} tree
13951  * @param {Object} config
13952  */
13953 Roo.tree.TreeSorter = function(tree, config){
13954     Roo.apply(this, config);
13955     tree.on("beforechildrenrendered", this.doSort, this);
13956     tree.on("append", this.updateSort, this);
13957     tree.on("insert", this.updateSort, this);
13958     
13959     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13960     var p = this.property || "text";
13961     var sortType = this.sortType;
13962     var fs = this.folderSort;
13963     var cs = this.caseSensitive === true;
13964     var leafAttr = this.leafAttr || 'leaf';
13965
13966     this.sortFn = function(n1, n2){
13967         if(fs){
13968             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13969                 return 1;
13970             }
13971             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13972                 return -1;
13973             }
13974         }
13975         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13976         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13977         if(v1 < v2){
13978                         return dsc ? +1 : -1;
13979                 }else if(v1 > v2){
13980                         return dsc ? -1 : +1;
13981         }else{
13982                 return 0;
13983         }
13984     };
13985 };
13986
13987 Roo.tree.TreeSorter.prototype = {
13988     doSort : function(node){
13989         node.sort(this.sortFn);
13990     },
13991     
13992     compareNodes : function(n1, n2){
13993         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13994     },
13995     
13996     updateSort : function(tree, node){
13997         if(node.childrenRendered){
13998             this.doSort.defer(1, this, [node]);
13999         }
14000     }
14001 };/*
14002  * Based on:
14003  * Ext JS Library 1.1.1
14004  * Copyright(c) 2006-2007, Ext JS, LLC.
14005  *
14006  * Originally Released Under LGPL - original licence link has changed is not relivant.
14007  *
14008  * Fork - LGPL
14009  * <script type="text/javascript">
14010  */
14011
14012 if(Roo.dd.DropZone){
14013     
14014 Roo.tree.TreeDropZone = function(tree, config){
14015     this.allowParentInsert = false;
14016     this.allowContainerDrop = false;
14017     this.appendOnly = false;
14018     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14019     this.tree = tree;
14020     this.lastInsertClass = "x-tree-no-status";
14021     this.dragOverData = {};
14022 };
14023
14024 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14025     ddGroup : "TreeDD",
14026     scroll:  true,
14027     
14028     expandDelay : 1000,
14029     
14030     expandNode : function(node){
14031         if(node.hasChildNodes() && !node.isExpanded()){
14032             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14033         }
14034     },
14035     
14036     queueExpand : function(node){
14037         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14038     },
14039     
14040     cancelExpand : function(){
14041         if(this.expandProcId){
14042             clearTimeout(this.expandProcId);
14043             this.expandProcId = false;
14044         }
14045     },
14046     
14047     isValidDropPoint : function(n, pt, dd, e, data){
14048         if(!n || !data){ return false; }
14049         var targetNode = n.node;
14050         var dropNode = data.node;
14051         // default drop rules
14052         if(!(targetNode && targetNode.isTarget && pt)){
14053             return false;
14054         }
14055         if(pt == "append" && targetNode.allowChildren === false){
14056             return false;
14057         }
14058         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14059             return false;
14060         }
14061         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14062             return false;
14063         }
14064         // reuse the object
14065         var overEvent = this.dragOverData;
14066         overEvent.tree = this.tree;
14067         overEvent.target = targetNode;
14068         overEvent.data = data;
14069         overEvent.point = pt;
14070         overEvent.source = dd;
14071         overEvent.rawEvent = e;
14072         overEvent.dropNode = dropNode;
14073         overEvent.cancel = false;  
14074         var result = this.tree.fireEvent("nodedragover", overEvent);
14075         return overEvent.cancel === false && result !== false;
14076     },
14077     
14078     getDropPoint : function(e, n, dd)
14079     {
14080         var tn = n.node;
14081         if(tn.isRoot){
14082             return tn.allowChildren !== false ? "append" : false; // always append for root
14083         }
14084         var dragEl = n.ddel;
14085         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14086         var y = Roo.lib.Event.getPageY(e);
14087         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14088         
14089         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14090         var noAppend = tn.allowChildren === false;
14091         if(this.appendOnly || tn.parentNode.allowChildren === false){
14092             return noAppend ? false : "append";
14093         }
14094         var noBelow = false;
14095         if(!this.allowParentInsert){
14096             noBelow = tn.hasChildNodes() && tn.isExpanded();
14097         }
14098         var q = (b - t) / (noAppend ? 2 : 3);
14099         if(y >= t && y < (t + q)){
14100             return "above";
14101         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14102             return "below";
14103         }else{
14104             return "append";
14105         }
14106     },
14107     
14108     onNodeEnter : function(n, dd, e, data)
14109     {
14110         this.cancelExpand();
14111     },
14112     
14113     onNodeOver : function(n, dd, e, data)
14114     {
14115        
14116         var pt = this.getDropPoint(e, n, dd);
14117         var node = n.node;
14118         
14119         // auto node expand check
14120         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14121             this.queueExpand(node);
14122         }else if(pt != "append"){
14123             this.cancelExpand();
14124         }
14125         
14126         // set the insert point style on the target node
14127         var returnCls = this.dropNotAllowed;
14128         if(this.isValidDropPoint(n, pt, dd, e, data)){
14129            if(pt){
14130                var el = n.ddel;
14131                var cls;
14132                if(pt == "above"){
14133                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14134                    cls = "x-tree-drag-insert-above";
14135                }else if(pt == "below"){
14136                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14137                    cls = "x-tree-drag-insert-below";
14138                }else{
14139                    returnCls = "x-tree-drop-ok-append";
14140                    cls = "x-tree-drag-append";
14141                }
14142                if(this.lastInsertClass != cls){
14143                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14144                    this.lastInsertClass = cls;
14145                }
14146            }
14147        }
14148        return returnCls;
14149     },
14150     
14151     onNodeOut : function(n, dd, e, data){
14152         
14153         this.cancelExpand();
14154         this.removeDropIndicators(n);
14155     },
14156     
14157     onNodeDrop : function(n, dd, e, data){
14158         var point = this.getDropPoint(e, n, dd);
14159         var targetNode = n.node;
14160         targetNode.ui.startDrop();
14161         if(!this.isValidDropPoint(n, point, dd, e, data)){
14162             targetNode.ui.endDrop();
14163             return false;
14164         }
14165         // first try to find the drop node
14166         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14167         var dropEvent = {
14168             tree : this.tree,
14169             target: targetNode,
14170             data: data,
14171             point: point,
14172             source: dd,
14173             rawEvent: e,
14174             dropNode: dropNode,
14175             cancel: !dropNode   
14176         };
14177         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14178         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14179             targetNode.ui.endDrop();
14180             return false;
14181         }
14182         // allow target changing
14183         targetNode = dropEvent.target;
14184         if(point == "append" && !targetNode.isExpanded()){
14185             targetNode.expand(false, null, function(){
14186                 this.completeDrop(dropEvent);
14187             }.createDelegate(this));
14188         }else{
14189             this.completeDrop(dropEvent);
14190         }
14191         return true;
14192     },
14193     
14194     completeDrop : function(de){
14195         var ns = de.dropNode, p = de.point, t = de.target;
14196         if(!(ns instanceof Array)){
14197             ns = [ns];
14198         }
14199         var n;
14200         for(var i = 0, len = ns.length; i < len; i++){
14201             n = ns[i];
14202             if(p == "above"){
14203                 t.parentNode.insertBefore(n, t);
14204             }else if(p == "below"){
14205                 t.parentNode.insertBefore(n, t.nextSibling);
14206             }else{
14207                 t.appendChild(n);
14208             }
14209         }
14210         n.ui.focus();
14211         if(this.tree.hlDrop){
14212             n.ui.highlight();
14213         }
14214         t.ui.endDrop();
14215         this.tree.fireEvent("nodedrop", de);
14216     },
14217     
14218     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14219         if(this.tree.hlDrop){
14220             dropNode.ui.focus();
14221             dropNode.ui.highlight();
14222         }
14223         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14224     },
14225     
14226     getTree : function(){
14227         return this.tree;
14228     },
14229     
14230     removeDropIndicators : function(n){
14231         if(n && n.ddel){
14232             var el = n.ddel;
14233             Roo.fly(el).removeClass([
14234                     "x-tree-drag-insert-above",
14235                     "x-tree-drag-insert-below",
14236                     "x-tree-drag-append"]);
14237             this.lastInsertClass = "_noclass";
14238         }
14239     },
14240     
14241     beforeDragDrop : function(target, e, id){
14242         this.cancelExpand();
14243         return true;
14244     },
14245     
14246     afterRepair : function(data){
14247         if(data && Roo.enableFx){
14248             data.node.ui.highlight();
14249         }
14250         this.hideProxy();
14251     } 
14252     
14253 });
14254
14255 }
14256 /*
14257  * Based on:
14258  * Ext JS Library 1.1.1
14259  * Copyright(c) 2006-2007, Ext JS, LLC.
14260  *
14261  * Originally Released Under LGPL - original licence link has changed is not relivant.
14262  *
14263  * Fork - LGPL
14264  * <script type="text/javascript">
14265  */
14266  
14267
14268 if(Roo.dd.DragZone){
14269 Roo.tree.TreeDragZone = function(tree, config){
14270     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14271     this.tree = tree;
14272 };
14273
14274 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14275     ddGroup : "TreeDD",
14276    
14277     onBeforeDrag : function(data, e){
14278         var n = data.node;
14279         return n && n.draggable && !n.disabled;
14280     },
14281      
14282     
14283     onInitDrag : function(e){
14284         var data = this.dragData;
14285         this.tree.getSelectionModel().select(data.node);
14286         this.proxy.update("");
14287         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14288         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14289     },
14290     
14291     getRepairXY : function(e, data){
14292         return data.node.ui.getDDRepairXY();
14293     },
14294     
14295     onEndDrag : function(data, e){
14296         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14297         
14298         
14299     },
14300     
14301     onValidDrop : function(dd, e, id){
14302         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14303         this.hideProxy();
14304     },
14305     
14306     beforeInvalidDrop : function(e, id){
14307         // this scrolls the original position back into view
14308         var sm = this.tree.getSelectionModel();
14309         sm.clearSelections();
14310         sm.select(this.dragData.node);
14311     }
14312 });
14313 }/*
14314  * Based on:
14315  * Ext JS Library 1.1.1
14316  * Copyright(c) 2006-2007, Ext JS, LLC.
14317  *
14318  * Originally Released Under LGPL - original licence link has changed is not relivant.
14319  *
14320  * Fork - LGPL
14321  * <script type="text/javascript">
14322  */
14323 /**
14324  * @class Roo.tree.TreeEditor
14325  * @extends Roo.Editor
14326  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14327  * as the editor field.
14328  * @constructor
14329  * @param {Object} config (used to be the tree panel.)
14330  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14331  * 
14332  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14333  * @cfg {Roo.form.TextField|Object} field The field configuration
14334  *
14335  * 
14336  */
14337 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14338     var tree = config;
14339     var field;
14340     if (oldconfig) { // old style..
14341         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14342     } else {
14343         // new style..
14344         tree = config.tree;
14345         config.field = config.field  || {};
14346         config.field.xtype = 'TextField';
14347         field = Roo.factory(config.field, Roo.form);
14348     }
14349     config = config || {};
14350     
14351     
14352     this.addEvents({
14353         /**
14354          * @event beforenodeedit
14355          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14356          * false from the handler of this event.
14357          * @param {Editor} this
14358          * @param {Roo.tree.Node} node 
14359          */
14360         "beforenodeedit" : true
14361     });
14362     
14363     //Roo.log(config);
14364     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14365
14366     this.tree = tree;
14367
14368     tree.on('beforeclick', this.beforeNodeClick, this);
14369     tree.getTreeEl().on('mousedown', this.hide, this);
14370     this.on('complete', this.updateNode, this);
14371     this.on('beforestartedit', this.fitToTree, this);
14372     this.on('startedit', this.bindScroll, this, {delay:10});
14373     this.on('specialkey', this.onSpecialKey, this);
14374 };
14375
14376 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14377     /**
14378      * @cfg {String} alignment
14379      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14380      */
14381     alignment: "l-l",
14382     // inherit
14383     autoSize: false,
14384     /**
14385      * @cfg {Boolean} hideEl
14386      * True to hide the bound element while the editor is displayed (defaults to false)
14387      */
14388     hideEl : false,
14389     /**
14390      * @cfg {String} cls
14391      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14392      */
14393     cls: "x-small-editor x-tree-editor",
14394     /**
14395      * @cfg {Boolean} shim
14396      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14397      */
14398     shim:false,
14399     // inherit
14400     shadow:"frame",
14401     /**
14402      * @cfg {Number} maxWidth
14403      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14404      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14405      * scroll and client offsets into account prior to each edit.
14406      */
14407     maxWidth: 250,
14408
14409     editDelay : 350,
14410
14411     // private
14412     fitToTree : function(ed, el){
14413         var td = this.tree.getTreeEl().dom, nd = el.dom;
14414         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14415             td.scrollLeft = nd.offsetLeft;
14416         }
14417         var w = Math.min(
14418                 this.maxWidth,
14419                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14420         this.setSize(w, '');
14421         
14422         return this.fireEvent('beforenodeedit', this, this.editNode);
14423         
14424     },
14425
14426     // private
14427     triggerEdit : function(node){
14428         this.completeEdit();
14429         this.editNode = node;
14430         this.startEdit(node.ui.textNode, node.text);
14431     },
14432
14433     // private
14434     bindScroll : function(){
14435         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14436     },
14437
14438     // private
14439     beforeNodeClick : function(node, e){
14440         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14441         this.lastClick = new Date();
14442         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14443             e.stopEvent();
14444             this.triggerEdit(node);
14445             return false;
14446         }
14447         return true;
14448     },
14449
14450     // private
14451     updateNode : function(ed, value){
14452         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14453         this.editNode.setText(value);
14454     },
14455
14456     // private
14457     onHide : function(){
14458         Roo.tree.TreeEditor.superclass.onHide.call(this);
14459         if(this.editNode){
14460             this.editNode.ui.focus();
14461         }
14462     },
14463
14464     // private
14465     onSpecialKey : function(field, e){
14466         var k = e.getKey();
14467         if(k == e.ESC){
14468             e.stopEvent();
14469             this.cancelEdit();
14470         }else if(k == e.ENTER && !e.hasModifier()){
14471             e.stopEvent();
14472             this.completeEdit();
14473         }
14474     }
14475 });//<Script type="text/javascript">
14476 /*
14477  * Based on:
14478  * Ext JS Library 1.1.1
14479  * Copyright(c) 2006-2007, Ext JS, LLC.
14480  *
14481  * Originally Released Under LGPL - original licence link has changed is not relivant.
14482  *
14483  * Fork - LGPL
14484  * <script type="text/javascript">
14485  */
14486  
14487 /**
14488  * Not documented??? - probably should be...
14489  */
14490
14491 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14492     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14493     
14494     renderElements : function(n, a, targetNode, bulkRender){
14495         //consel.log("renderElements?");
14496         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14497
14498         var t = n.getOwnerTree();
14499         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14500         
14501         var cols = t.columns;
14502         var bw = t.borderWidth;
14503         var c = cols[0];
14504         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14505          var cb = typeof a.checked == "boolean";
14506         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14507         var colcls = 'x-t-' + tid + '-c0';
14508         var buf = [
14509             '<li class="x-tree-node">',
14510             
14511                 
14512                 '<div class="x-tree-node-el ', a.cls,'">',
14513                     // extran...
14514                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14515                 
14516                 
14517                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14518                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14519                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14520                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14521                            (a.iconCls ? ' '+a.iconCls : ''),
14522                            '" unselectable="on" />',
14523                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14524                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14525                              
14526                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14527                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14528                             '<span unselectable="on" qtip="' + tx + '">',
14529                              tx,
14530                              '</span></a>' ,
14531                     '</div>',
14532                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14533                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14534                  ];
14535         for(var i = 1, len = cols.length; i < len; i++){
14536             c = cols[i];
14537             colcls = 'x-t-' + tid + '-c' +i;
14538             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14539             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14540                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14541                       "</div>");
14542          }
14543          
14544          buf.push(
14545             '</a>',
14546             '<div class="x-clear"></div></div>',
14547             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14548             "</li>");
14549         
14550         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14551             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14552                                 n.nextSibling.ui.getEl(), buf.join(""));
14553         }else{
14554             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14555         }
14556         var el = this.wrap.firstChild;
14557         this.elRow = el;
14558         this.elNode = el.firstChild;
14559         this.ranchor = el.childNodes[1];
14560         this.ctNode = this.wrap.childNodes[1];
14561         var cs = el.firstChild.childNodes;
14562         this.indentNode = cs[0];
14563         this.ecNode = cs[1];
14564         this.iconNode = cs[2];
14565         var index = 3;
14566         if(cb){
14567             this.checkbox = cs[3];
14568             index++;
14569         }
14570         this.anchor = cs[index];
14571         
14572         this.textNode = cs[index].firstChild;
14573         
14574         //el.on("click", this.onClick, this);
14575         //el.on("dblclick", this.onDblClick, this);
14576         
14577         
14578        // console.log(this);
14579     },
14580     initEvents : function(){
14581         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14582         
14583             
14584         var a = this.ranchor;
14585
14586         var el = Roo.get(a);
14587
14588         if(Roo.isOpera){ // opera render bug ignores the CSS
14589             el.setStyle("text-decoration", "none");
14590         }
14591
14592         el.on("click", this.onClick, this);
14593         el.on("dblclick", this.onDblClick, this);
14594         el.on("contextmenu", this.onContextMenu, this);
14595         
14596     },
14597     
14598     /*onSelectedChange : function(state){
14599         if(state){
14600             this.focus();
14601             this.addClass("x-tree-selected");
14602         }else{
14603             //this.blur();
14604             this.removeClass("x-tree-selected");
14605         }
14606     },*/
14607     addClass : function(cls){
14608         if(this.elRow){
14609             Roo.fly(this.elRow).addClass(cls);
14610         }
14611         
14612     },
14613     
14614     
14615     removeClass : function(cls){
14616         if(this.elRow){
14617             Roo.fly(this.elRow).removeClass(cls);
14618         }
14619     }
14620
14621     
14622     
14623 });//<Script type="text/javascript">
14624
14625 /*
14626  * Based on:
14627  * Ext JS Library 1.1.1
14628  * Copyright(c) 2006-2007, Ext JS, LLC.
14629  *
14630  * Originally Released Under LGPL - original licence link has changed is not relivant.
14631  *
14632  * Fork - LGPL
14633  * <script type="text/javascript">
14634  */
14635  
14636
14637 /**
14638  * @class Roo.tree.ColumnTree
14639  * @extends Roo.data.TreePanel
14640  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14641  * @cfg {int} borderWidth  compined right/left border allowance
14642  * @constructor
14643  * @param {String/HTMLElement/Element} el The container element
14644  * @param {Object} config
14645  */
14646 Roo.tree.ColumnTree =  function(el, config)
14647 {
14648    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14649    this.addEvents({
14650         /**
14651         * @event resize
14652         * Fire this event on a container when it resizes
14653         * @param {int} w Width
14654         * @param {int} h Height
14655         */
14656        "resize" : true
14657     });
14658     this.on('resize', this.onResize, this);
14659 };
14660
14661 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14662     //lines:false,
14663     
14664     
14665     borderWidth: Roo.isBorderBox ? 0 : 2, 
14666     headEls : false,
14667     
14668     render : function(){
14669         // add the header.....
14670        
14671         Roo.tree.ColumnTree.superclass.render.apply(this);
14672         
14673         this.el.addClass('x-column-tree');
14674         
14675         this.headers = this.el.createChild(
14676             {cls:'x-tree-headers'},this.innerCt.dom);
14677    
14678         var cols = this.columns, c;
14679         var totalWidth = 0;
14680         this.headEls = [];
14681         var  len = cols.length;
14682         for(var i = 0; i < len; i++){
14683              c = cols[i];
14684              totalWidth += c.width;
14685             this.headEls.push(this.headers.createChild({
14686                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14687                  cn: {
14688                      cls:'x-tree-hd-text',
14689                      html: c.header
14690                  },
14691                  style:'width:'+(c.width-this.borderWidth)+'px;'
14692              }));
14693         }
14694         this.headers.createChild({cls:'x-clear'});
14695         // prevent floats from wrapping when clipped
14696         this.headers.setWidth(totalWidth);
14697         //this.innerCt.setWidth(totalWidth);
14698         this.innerCt.setStyle({ overflow: 'auto' });
14699         this.onResize(this.width, this.height);
14700              
14701         
14702     },
14703     onResize : function(w,h)
14704     {
14705         this.height = h;
14706         this.width = w;
14707         // resize cols..
14708         this.innerCt.setWidth(this.width);
14709         this.innerCt.setHeight(this.height-20);
14710         
14711         // headers...
14712         var cols = this.columns, c;
14713         var totalWidth = 0;
14714         var expEl = false;
14715         var len = cols.length;
14716         for(var i = 0; i < len; i++){
14717             c = cols[i];
14718             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14719                 // it's the expander..
14720                 expEl  = this.headEls[i];
14721                 continue;
14722             }
14723             totalWidth += c.width;
14724             
14725         }
14726         if (expEl) {
14727             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14728         }
14729         this.headers.setWidth(w-20);
14730
14731         
14732         
14733         
14734     }
14735 });
14736 /*
14737  * Based on:
14738  * Ext JS Library 1.1.1
14739  * Copyright(c) 2006-2007, Ext JS, LLC.
14740  *
14741  * Originally Released Under LGPL - original licence link has changed is not relivant.
14742  *
14743  * Fork - LGPL
14744  * <script type="text/javascript">
14745  */
14746  
14747 /**
14748  * @class Roo.menu.Menu
14749  * @extends Roo.util.Observable
14750  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14751  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14752  * @constructor
14753  * Creates a new Menu
14754  * @param {Object} config Configuration options
14755  */
14756 Roo.menu.Menu = function(config){
14757     Roo.apply(this, config);
14758     this.id = this.id || Roo.id();
14759     this.addEvents({
14760         /**
14761          * @event beforeshow
14762          * Fires before this menu is displayed
14763          * @param {Roo.menu.Menu} this
14764          */
14765         beforeshow : true,
14766         /**
14767          * @event beforehide
14768          * Fires before this menu is hidden
14769          * @param {Roo.menu.Menu} this
14770          */
14771         beforehide : true,
14772         /**
14773          * @event show
14774          * Fires after this menu is displayed
14775          * @param {Roo.menu.Menu} this
14776          */
14777         show : true,
14778         /**
14779          * @event hide
14780          * Fires after this menu is hidden
14781          * @param {Roo.menu.Menu} this
14782          */
14783         hide : true,
14784         /**
14785          * @event click
14786          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14787          * @param {Roo.menu.Menu} this
14788          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14789          * @param {Roo.EventObject} e
14790          */
14791         click : true,
14792         /**
14793          * @event mouseover
14794          * Fires when the mouse is hovering over this menu
14795          * @param {Roo.menu.Menu} this
14796          * @param {Roo.EventObject} e
14797          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14798          */
14799         mouseover : true,
14800         /**
14801          * @event mouseout
14802          * Fires when the mouse exits this menu
14803          * @param {Roo.menu.Menu} this
14804          * @param {Roo.EventObject} e
14805          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14806          */
14807         mouseout : true,
14808         /**
14809          * @event itemclick
14810          * Fires when a menu item contained in this menu is clicked
14811          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14812          * @param {Roo.EventObject} e
14813          */
14814         itemclick: true
14815     });
14816     if (this.registerMenu) {
14817         Roo.menu.MenuMgr.register(this);
14818     }
14819     
14820     var mis = this.items;
14821     this.items = new Roo.util.MixedCollection();
14822     if(mis){
14823         this.add.apply(this, mis);
14824     }
14825 };
14826
14827 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14828     /**
14829      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14830      */
14831     minWidth : 120,
14832     /**
14833      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14834      * for bottom-right shadow (defaults to "sides")
14835      */
14836     shadow : "sides",
14837     /**
14838      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14839      * this menu (defaults to "tl-tr?")
14840      */
14841     subMenuAlign : "tl-tr?",
14842     /**
14843      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14844      * relative to its element of origin (defaults to "tl-bl?")
14845      */
14846     defaultAlign : "tl-bl?",
14847     /**
14848      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14849      */
14850     allowOtherMenus : false,
14851     /**
14852      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14853      */
14854     registerMenu : true,
14855
14856     hidden:true,
14857
14858     // private
14859     render : function(){
14860         if(this.el){
14861             return;
14862         }
14863         var el = this.el = new Roo.Layer({
14864             cls: "x-menu",
14865             shadow:this.shadow,
14866             constrain: false,
14867             parentEl: this.parentEl || document.body,
14868             zindex:15000
14869         });
14870
14871         this.keyNav = new Roo.menu.MenuNav(this);
14872
14873         if(this.plain){
14874             el.addClass("x-menu-plain");
14875         }
14876         if(this.cls){
14877             el.addClass(this.cls);
14878         }
14879         // generic focus element
14880         this.focusEl = el.createChild({
14881             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14882         });
14883         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14884         //disabling touch- as it's causing issues ..
14885         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14886         ul.on('click'   , this.onClick, this);
14887         
14888         
14889         ul.on("mouseover", this.onMouseOver, this);
14890         ul.on("mouseout", this.onMouseOut, this);
14891         this.items.each(function(item){
14892             if (item.hidden) {
14893                 return;
14894             }
14895             
14896             var li = document.createElement("li");
14897             li.className = "x-menu-list-item";
14898             ul.dom.appendChild(li);
14899             item.render(li, this);
14900         }, this);
14901         this.ul = ul;
14902         this.autoWidth();
14903     },
14904
14905     // private
14906     autoWidth : function(){
14907         var el = this.el, ul = this.ul;
14908         if(!el){
14909             return;
14910         }
14911         var w = this.width;
14912         if(w){
14913             el.setWidth(w);
14914         }else if(Roo.isIE){
14915             el.setWidth(this.minWidth);
14916             var t = el.dom.offsetWidth; // force recalc
14917             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14918         }
14919     },
14920
14921     // private
14922     delayAutoWidth : function(){
14923         if(this.rendered){
14924             if(!this.awTask){
14925                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14926             }
14927             this.awTask.delay(20);
14928         }
14929     },
14930
14931     // private
14932     findTargetItem : function(e){
14933         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14934         if(t && t.menuItemId){
14935             return this.items.get(t.menuItemId);
14936         }
14937     },
14938
14939     // private
14940     onClick : function(e){
14941         Roo.log("menu.onClick");
14942         var t = this.findTargetItem(e);
14943         if(!t){
14944             return;
14945         }
14946         Roo.log(e);
14947         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14948             if(t == this.activeItem && t.shouldDeactivate(e)){
14949                 this.activeItem.deactivate();
14950                 delete this.activeItem;
14951                 return;
14952             }
14953             if(t.canActivate){
14954                 this.setActiveItem(t, true);
14955             }
14956             return;
14957             
14958             
14959         }
14960         
14961         t.onClick(e);
14962         this.fireEvent("click", this, t, e);
14963     },
14964
14965     // private
14966     setActiveItem : function(item, autoExpand){
14967         if(item != this.activeItem){
14968             if(this.activeItem){
14969                 this.activeItem.deactivate();
14970             }
14971             this.activeItem = item;
14972             item.activate(autoExpand);
14973         }else if(autoExpand){
14974             item.expandMenu();
14975         }
14976     },
14977
14978     // private
14979     tryActivate : function(start, step){
14980         var items = this.items;
14981         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14982             var item = items.get(i);
14983             if(!item.disabled && item.canActivate){
14984                 this.setActiveItem(item, false);
14985                 return item;
14986             }
14987         }
14988         return false;
14989     },
14990
14991     // private
14992     onMouseOver : function(e){
14993         var t;
14994         if(t = this.findTargetItem(e)){
14995             if(t.canActivate && !t.disabled){
14996                 this.setActiveItem(t, true);
14997             }
14998         }
14999         this.fireEvent("mouseover", this, e, t);
15000     },
15001
15002     // private
15003     onMouseOut : function(e){
15004         var t;
15005         if(t = this.findTargetItem(e)){
15006             if(t == this.activeItem && t.shouldDeactivate(e)){
15007                 this.activeItem.deactivate();
15008                 delete this.activeItem;
15009             }
15010         }
15011         this.fireEvent("mouseout", this, e, t);
15012     },
15013
15014     /**
15015      * Read-only.  Returns true if the menu is currently displayed, else false.
15016      * @type Boolean
15017      */
15018     isVisible : function(){
15019         return this.el && !this.hidden;
15020     },
15021
15022     /**
15023      * Displays this menu relative to another element
15024      * @param {String/HTMLElement/Roo.Element} element The element to align to
15025      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15026      * the element (defaults to this.defaultAlign)
15027      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15028      */
15029     show : function(el, pos, parentMenu){
15030         this.parentMenu = parentMenu;
15031         if(!this.el){
15032             this.render();
15033         }
15034         this.fireEvent("beforeshow", this);
15035         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15036     },
15037
15038     /**
15039      * Displays this menu at a specific xy position
15040      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15041      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15042      */
15043     showAt : function(xy, parentMenu, /* private: */_e){
15044         this.parentMenu = parentMenu;
15045         if(!this.el){
15046             this.render();
15047         }
15048         if(_e !== false){
15049             this.fireEvent("beforeshow", this);
15050             xy = this.el.adjustForConstraints(xy);
15051         }
15052         this.el.setXY(xy);
15053         this.el.show();
15054         this.hidden = false;
15055         this.focus();
15056         this.fireEvent("show", this);
15057     },
15058
15059     focus : function(){
15060         if(!this.hidden){
15061             this.doFocus.defer(50, this);
15062         }
15063     },
15064
15065     doFocus : function(){
15066         if(!this.hidden){
15067             this.focusEl.focus();
15068         }
15069     },
15070
15071     /**
15072      * Hides this menu and optionally all parent menus
15073      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15074      */
15075     hide : function(deep){
15076         if(this.el && this.isVisible()){
15077             this.fireEvent("beforehide", this);
15078             if(this.activeItem){
15079                 this.activeItem.deactivate();
15080                 this.activeItem = null;
15081             }
15082             this.el.hide();
15083             this.hidden = true;
15084             this.fireEvent("hide", this);
15085         }
15086         if(deep === true && this.parentMenu){
15087             this.parentMenu.hide(true);
15088         }
15089     },
15090
15091     /**
15092      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15093      * Any of the following are valid:
15094      * <ul>
15095      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15096      * <li>An HTMLElement object which will be converted to a menu item</li>
15097      * <li>A menu item config object that will be created as a new menu item</li>
15098      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15099      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15100      * </ul>
15101      * Usage:
15102      * <pre><code>
15103 // Create the menu
15104 var menu = new Roo.menu.Menu();
15105
15106 // Create a menu item to add by reference
15107 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15108
15109 // Add a bunch of items at once using different methods.
15110 // Only the last item added will be returned.
15111 var item = menu.add(
15112     menuItem,                // add existing item by ref
15113     'Dynamic Item',          // new TextItem
15114     '-',                     // new separator
15115     { text: 'Config Item' }  // new item by config
15116 );
15117 </code></pre>
15118      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15119      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15120      */
15121     add : function(){
15122         var a = arguments, l = a.length, item;
15123         for(var i = 0; i < l; i++){
15124             var el = a[i];
15125             if ((typeof(el) == "object") && el.xtype && el.xns) {
15126                 el = Roo.factory(el, Roo.menu);
15127             }
15128             
15129             if(el.render){ // some kind of Item
15130                 item = this.addItem(el);
15131             }else if(typeof el == "string"){ // string
15132                 if(el == "separator" || el == "-"){
15133                     item = this.addSeparator();
15134                 }else{
15135                     item = this.addText(el);
15136                 }
15137             }else if(el.tagName || el.el){ // element
15138                 item = this.addElement(el);
15139             }else if(typeof el == "object"){ // must be menu item config?
15140                 item = this.addMenuItem(el);
15141             }
15142         }
15143         return item;
15144     },
15145
15146     /**
15147      * Returns this menu's underlying {@link Roo.Element} object
15148      * @return {Roo.Element} The element
15149      */
15150     getEl : function(){
15151         if(!this.el){
15152             this.render();
15153         }
15154         return this.el;
15155     },
15156
15157     /**
15158      * Adds a separator bar to the menu
15159      * @return {Roo.menu.Item} The menu item that was added
15160      */
15161     addSeparator : function(){
15162         return this.addItem(new Roo.menu.Separator());
15163     },
15164
15165     /**
15166      * Adds an {@link Roo.Element} object to the menu
15167      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15168      * @return {Roo.menu.Item} The menu item that was added
15169      */
15170     addElement : function(el){
15171         return this.addItem(new Roo.menu.BaseItem(el));
15172     },
15173
15174     /**
15175      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15176      * @param {Roo.menu.Item} item The menu item to add
15177      * @return {Roo.menu.Item} The menu item that was added
15178      */
15179     addItem : function(item){
15180         this.items.add(item);
15181         if(this.ul){
15182             var li = document.createElement("li");
15183             li.className = "x-menu-list-item";
15184             this.ul.dom.appendChild(li);
15185             item.render(li, this);
15186             this.delayAutoWidth();
15187         }
15188         return item;
15189     },
15190
15191     /**
15192      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15193      * @param {Object} config A MenuItem config object
15194      * @return {Roo.menu.Item} The menu item that was added
15195      */
15196     addMenuItem : function(config){
15197         if(!(config instanceof Roo.menu.Item)){
15198             if(typeof config.checked == "boolean"){ // must be check menu item config?
15199                 config = new Roo.menu.CheckItem(config);
15200             }else{
15201                 config = new Roo.menu.Item(config);
15202             }
15203         }
15204         return this.addItem(config);
15205     },
15206
15207     /**
15208      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15209      * @param {String} text The text to display in the menu item
15210      * @return {Roo.menu.Item} The menu item that was added
15211      */
15212     addText : function(text){
15213         return this.addItem(new Roo.menu.TextItem({ text : text }));
15214     },
15215
15216     /**
15217      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15218      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15219      * @param {Roo.menu.Item} item The menu item to add
15220      * @return {Roo.menu.Item} The menu item that was added
15221      */
15222     insert : function(index, item){
15223         this.items.insert(index, item);
15224         if(this.ul){
15225             var li = document.createElement("li");
15226             li.className = "x-menu-list-item";
15227             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15228             item.render(li, this);
15229             this.delayAutoWidth();
15230         }
15231         return item;
15232     },
15233
15234     /**
15235      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15236      * @param {Roo.menu.Item} item The menu item to remove
15237      */
15238     remove : function(item){
15239         this.items.removeKey(item.id);
15240         item.destroy();
15241     },
15242
15243     /**
15244      * Removes and destroys all items in the menu
15245      */
15246     removeAll : function(){
15247         var f;
15248         while(f = this.items.first()){
15249             this.remove(f);
15250         }
15251     }
15252 });
15253
15254 // MenuNav is a private utility class used internally by the Menu
15255 Roo.menu.MenuNav = function(menu){
15256     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15257     this.scope = this.menu = menu;
15258 };
15259
15260 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15261     doRelay : function(e, h){
15262         var k = e.getKey();
15263         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15264             this.menu.tryActivate(0, 1);
15265             return false;
15266         }
15267         return h.call(this.scope || this, e, this.menu);
15268     },
15269
15270     up : function(e, m){
15271         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15272             m.tryActivate(m.items.length-1, -1);
15273         }
15274     },
15275
15276     down : function(e, m){
15277         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15278             m.tryActivate(0, 1);
15279         }
15280     },
15281
15282     right : function(e, m){
15283         if(m.activeItem){
15284             m.activeItem.expandMenu(true);
15285         }
15286     },
15287
15288     left : function(e, m){
15289         m.hide();
15290         if(m.parentMenu && m.parentMenu.activeItem){
15291             m.parentMenu.activeItem.activate();
15292         }
15293     },
15294
15295     enter : function(e, m){
15296         if(m.activeItem){
15297             e.stopPropagation();
15298             m.activeItem.onClick(e);
15299             m.fireEvent("click", this, m.activeItem);
15300             return true;
15301         }
15302     }
15303 });/*
15304  * Based on:
15305  * Ext JS Library 1.1.1
15306  * Copyright(c) 2006-2007, Ext JS, LLC.
15307  *
15308  * Originally Released Under LGPL - original licence link has changed is not relivant.
15309  *
15310  * Fork - LGPL
15311  * <script type="text/javascript">
15312  */
15313  
15314 /**
15315  * @class Roo.menu.MenuMgr
15316  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15317  * @singleton
15318  */
15319 Roo.menu.MenuMgr = function(){
15320    var menus, active, groups = {}, attached = false, lastShow = new Date();
15321
15322    // private - called when first menu is created
15323    function init(){
15324        menus = {};
15325        active = new Roo.util.MixedCollection();
15326        Roo.get(document).addKeyListener(27, function(){
15327            if(active.length > 0){
15328                hideAll();
15329            }
15330        });
15331    }
15332
15333    // private
15334    function hideAll(){
15335        if(active && active.length > 0){
15336            var c = active.clone();
15337            c.each(function(m){
15338                m.hide();
15339            });
15340        }
15341    }
15342
15343    // private
15344    function onHide(m){
15345        active.remove(m);
15346        if(active.length < 1){
15347            Roo.get(document).un("mousedown", onMouseDown);
15348            attached = false;
15349        }
15350    }
15351
15352    // private
15353    function onShow(m){
15354        var last = active.last();
15355        lastShow = new Date();
15356        active.add(m);
15357        if(!attached){
15358            Roo.get(document).on("mousedown", onMouseDown);
15359            attached = true;
15360        }
15361        if(m.parentMenu){
15362           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15363           m.parentMenu.activeChild = m;
15364        }else if(last && last.isVisible()){
15365           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15366        }
15367    }
15368
15369    // private
15370    function onBeforeHide(m){
15371        if(m.activeChild){
15372            m.activeChild.hide();
15373        }
15374        if(m.autoHideTimer){
15375            clearTimeout(m.autoHideTimer);
15376            delete m.autoHideTimer;
15377        }
15378    }
15379
15380    // private
15381    function onBeforeShow(m){
15382        var pm = m.parentMenu;
15383        if(!pm && !m.allowOtherMenus){
15384            hideAll();
15385        }else if(pm && pm.activeChild && active != m){
15386            pm.activeChild.hide();
15387        }
15388    }
15389
15390    // private
15391    function onMouseDown(e){
15392        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15393            hideAll();
15394        }
15395    }
15396
15397    // private
15398    function onBeforeCheck(mi, state){
15399        if(state){
15400            var g = groups[mi.group];
15401            for(var i = 0, l = g.length; i < l; i++){
15402                if(g[i] != mi){
15403                    g[i].setChecked(false);
15404                }
15405            }
15406        }
15407    }
15408
15409    return {
15410
15411        /**
15412         * Hides all menus that are currently visible
15413         */
15414        hideAll : function(){
15415             hideAll();  
15416        },
15417
15418        // private
15419        register : function(menu){
15420            if(!menus){
15421                init();
15422            }
15423            menus[menu.id] = menu;
15424            menu.on("beforehide", onBeforeHide);
15425            menu.on("hide", onHide);
15426            menu.on("beforeshow", onBeforeShow);
15427            menu.on("show", onShow);
15428            var g = menu.group;
15429            if(g && menu.events["checkchange"]){
15430                if(!groups[g]){
15431                    groups[g] = [];
15432                }
15433                groups[g].push(menu);
15434                menu.on("checkchange", onCheck);
15435            }
15436        },
15437
15438         /**
15439          * Returns a {@link Roo.menu.Menu} object
15440          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15441          * be used to generate and return a new Menu instance.
15442          */
15443        get : function(menu){
15444            if(typeof menu == "string"){ // menu id
15445                return menus[menu];
15446            }else if(menu.events){  // menu instance
15447                return menu;
15448            }else if(typeof menu.length == 'number'){ // array of menu items?
15449                return new Roo.menu.Menu({items:menu});
15450            }else{ // otherwise, must be a config
15451                return new Roo.menu.Menu(menu);
15452            }
15453        },
15454
15455        // private
15456        unregister : function(menu){
15457            delete menus[menu.id];
15458            menu.un("beforehide", onBeforeHide);
15459            menu.un("hide", onHide);
15460            menu.un("beforeshow", onBeforeShow);
15461            menu.un("show", onShow);
15462            var g = menu.group;
15463            if(g && menu.events["checkchange"]){
15464                groups[g].remove(menu);
15465                menu.un("checkchange", onCheck);
15466            }
15467        },
15468
15469        // private
15470        registerCheckable : function(menuItem){
15471            var g = menuItem.group;
15472            if(g){
15473                if(!groups[g]){
15474                    groups[g] = [];
15475                }
15476                groups[g].push(menuItem);
15477                menuItem.on("beforecheckchange", onBeforeCheck);
15478            }
15479        },
15480
15481        // private
15482        unregisterCheckable : function(menuItem){
15483            var g = menuItem.group;
15484            if(g){
15485                groups[g].remove(menuItem);
15486                menuItem.un("beforecheckchange", onBeforeCheck);
15487            }
15488        }
15489    };
15490 }();/*
15491  * Based on:
15492  * Ext JS Library 1.1.1
15493  * Copyright(c) 2006-2007, Ext JS, LLC.
15494  *
15495  * Originally Released Under LGPL - original licence link has changed is not relivant.
15496  *
15497  * Fork - LGPL
15498  * <script type="text/javascript">
15499  */
15500  
15501
15502 /**
15503  * @class Roo.menu.BaseItem
15504  * @extends Roo.Component
15505  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15506  * management and base configuration options shared by all menu components.
15507  * @constructor
15508  * Creates a new BaseItem
15509  * @param {Object} config Configuration options
15510  */
15511 Roo.menu.BaseItem = function(config){
15512     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15513
15514     this.addEvents({
15515         /**
15516          * @event click
15517          * Fires when this item is clicked
15518          * @param {Roo.menu.BaseItem} this
15519          * @param {Roo.EventObject} e
15520          */
15521         click: true,
15522         /**
15523          * @event activate
15524          * Fires when this item is activated
15525          * @param {Roo.menu.BaseItem} this
15526          */
15527         activate : true,
15528         /**
15529          * @event deactivate
15530          * Fires when this item is deactivated
15531          * @param {Roo.menu.BaseItem} this
15532          */
15533         deactivate : true
15534     });
15535
15536     if(this.handler){
15537         this.on("click", this.handler, this.scope, true);
15538     }
15539 };
15540
15541 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15542     /**
15543      * @cfg {Function} handler
15544      * A function that will handle the click event of this menu item (defaults to undefined)
15545      */
15546     /**
15547      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15548      */
15549     canActivate : false,
15550     
15551      /**
15552      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15553      */
15554     hidden: false,
15555     
15556     /**
15557      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15558      */
15559     activeClass : "x-menu-item-active",
15560     /**
15561      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15562      */
15563     hideOnClick : true,
15564     /**
15565      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15566      */
15567     hideDelay : 100,
15568
15569     // private
15570     ctype: "Roo.menu.BaseItem",
15571
15572     // private
15573     actionMode : "container",
15574
15575     // private
15576     render : function(container, parentMenu){
15577         this.parentMenu = parentMenu;
15578         Roo.menu.BaseItem.superclass.render.call(this, container);
15579         this.container.menuItemId = this.id;
15580     },
15581
15582     // private
15583     onRender : function(container, position){
15584         this.el = Roo.get(this.el);
15585         container.dom.appendChild(this.el.dom);
15586     },
15587
15588     // private
15589     onClick : function(e){
15590         if(!this.disabled && this.fireEvent("click", this, e) !== false
15591                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15592             this.handleClick(e);
15593         }else{
15594             e.stopEvent();
15595         }
15596     },
15597
15598     // private
15599     activate : function(){
15600         if(this.disabled){
15601             return false;
15602         }
15603         var li = this.container;
15604         li.addClass(this.activeClass);
15605         this.region = li.getRegion().adjust(2, 2, -2, -2);
15606         this.fireEvent("activate", this);
15607         return true;
15608     },
15609
15610     // private
15611     deactivate : function(){
15612         this.container.removeClass(this.activeClass);
15613         this.fireEvent("deactivate", this);
15614     },
15615
15616     // private
15617     shouldDeactivate : function(e){
15618         return !this.region || !this.region.contains(e.getPoint());
15619     },
15620
15621     // private
15622     handleClick : function(e){
15623         if(this.hideOnClick){
15624             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15625         }
15626     },
15627
15628     // private
15629     expandMenu : function(autoActivate){
15630         // do nothing
15631     },
15632
15633     // private
15634     hideMenu : function(){
15635         // do nothing
15636     }
15637 });/*
15638  * Based on:
15639  * Ext JS Library 1.1.1
15640  * Copyright(c) 2006-2007, Ext JS, LLC.
15641  *
15642  * Originally Released Under LGPL - original licence link has changed is not relivant.
15643  *
15644  * Fork - LGPL
15645  * <script type="text/javascript">
15646  */
15647  
15648 /**
15649  * @class Roo.menu.Adapter
15650  * @extends Roo.menu.BaseItem
15651  * 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.
15652  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15653  * @constructor
15654  * Creates a new Adapter
15655  * @param {Object} config Configuration options
15656  */
15657 Roo.menu.Adapter = function(component, config){
15658     Roo.menu.Adapter.superclass.constructor.call(this, config);
15659     this.component = component;
15660 };
15661 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15662     // private
15663     canActivate : true,
15664
15665     // private
15666     onRender : function(container, position){
15667         this.component.render(container);
15668         this.el = this.component.getEl();
15669     },
15670
15671     // private
15672     activate : function(){
15673         if(this.disabled){
15674             return false;
15675         }
15676         this.component.focus();
15677         this.fireEvent("activate", this);
15678         return true;
15679     },
15680
15681     // private
15682     deactivate : function(){
15683         this.fireEvent("deactivate", this);
15684     },
15685
15686     // private
15687     disable : function(){
15688         this.component.disable();
15689         Roo.menu.Adapter.superclass.disable.call(this);
15690     },
15691
15692     // private
15693     enable : function(){
15694         this.component.enable();
15695         Roo.menu.Adapter.superclass.enable.call(this);
15696     }
15697 });/*
15698  * Based on:
15699  * Ext JS Library 1.1.1
15700  * Copyright(c) 2006-2007, Ext JS, LLC.
15701  *
15702  * Originally Released Under LGPL - original licence link has changed is not relivant.
15703  *
15704  * Fork - LGPL
15705  * <script type="text/javascript">
15706  */
15707
15708 /**
15709  * @class Roo.menu.TextItem
15710  * @extends Roo.menu.BaseItem
15711  * Adds a static text string to a menu, usually used as either a heading or group separator.
15712  * Note: old style constructor with text is still supported.
15713  * 
15714  * @constructor
15715  * Creates a new TextItem
15716  * @param {Object} cfg Configuration
15717  */
15718 Roo.menu.TextItem = function(cfg){
15719     if (typeof(cfg) == 'string') {
15720         this.text = cfg;
15721     } else {
15722         Roo.apply(this,cfg);
15723     }
15724     
15725     Roo.menu.TextItem.superclass.constructor.call(this);
15726 };
15727
15728 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15729     /**
15730      * @cfg {Boolean} text Text to show on item.
15731      */
15732     text : '',
15733     
15734     /**
15735      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15736      */
15737     hideOnClick : false,
15738     /**
15739      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15740      */
15741     itemCls : "x-menu-text",
15742
15743     // private
15744     onRender : function(){
15745         var s = document.createElement("span");
15746         s.className = this.itemCls;
15747         s.innerHTML = this.text;
15748         this.el = s;
15749         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15750     }
15751 });/*
15752  * Based on:
15753  * Ext JS Library 1.1.1
15754  * Copyright(c) 2006-2007, Ext JS, LLC.
15755  *
15756  * Originally Released Under LGPL - original licence link has changed is not relivant.
15757  *
15758  * Fork - LGPL
15759  * <script type="text/javascript">
15760  */
15761
15762 /**
15763  * @class Roo.menu.Separator
15764  * @extends Roo.menu.BaseItem
15765  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15766  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15767  * @constructor
15768  * @param {Object} config Configuration options
15769  */
15770 Roo.menu.Separator = function(config){
15771     Roo.menu.Separator.superclass.constructor.call(this, config);
15772 };
15773
15774 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15775     /**
15776      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15777      */
15778     itemCls : "x-menu-sep",
15779     /**
15780      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15781      */
15782     hideOnClick : false,
15783
15784     // private
15785     onRender : function(li){
15786         var s = document.createElement("span");
15787         s.className = this.itemCls;
15788         s.innerHTML = "&#160;";
15789         this.el = s;
15790         li.addClass("x-menu-sep-li");
15791         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15792     }
15793 });/*
15794  * Based on:
15795  * Ext JS Library 1.1.1
15796  * Copyright(c) 2006-2007, Ext JS, LLC.
15797  *
15798  * Originally Released Under LGPL - original licence link has changed is not relivant.
15799  *
15800  * Fork - LGPL
15801  * <script type="text/javascript">
15802  */
15803 /**
15804  * @class Roo.menu.Item
15805  * @extends Roo.menu.BaseItem
15806  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15807  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15808  * activation and click handling.
15809  * @constructor
15810  * Creates a new Item
15811  * @param {Object} config Configuration options
15812  */
15813 Roo.menu.Item = function(config){
15814     Roo.menu.Item.superclass.constructor.call(this, config);
15815     if(this.menu){
15816         this.menu = Roo.menu.MenuMgr.get(this.menu);
15817     }
15818 };
15819 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15820     
15821     /**
15822      * @cfg {String} text
15823      * The text to show on the menu item.
15824      */
15825     text: '',
15826      /**
15827      * @cfg {String} HTML to render in menu
15828      * The text to show on the menu item (HTML version).
15829      */
15830     html: '',
15831     /**
15832      * @cfg {String} icon
15833      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15834      */
15835     icon: undefined,
15836     /**
15837      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15838      */
15839     itemCls : "x-menu-item",
15840     /**
15841      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15842      */
15843     canActivate : true,
15844     /**
15845      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15846      */
15847     showDelay: 200,
15848     // doc'd in BaseItem
15849     hideDelay: 200,
15850
15851     // private
15852     ctype: "Roo.menu.Item",
15853     
15854     // private
15855     onRender : function(container, position){
15856         var el = document.createElement("a");
15857         el.hideFocus = true;
15858         el.unselectable = "on";
15859         el.href = this.href || "#";
15860         if(this.hrefTarget){
15861             el.target = this.hrefTarget;
15862         }
15863         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15864         
15865         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15866         
15867         el.innerHTML = String.format(
15868                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15869                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15870         this.el = el;
15871         Roo.menu.Item.superclass.onRender.call(this, container, position);
15872     },
15873
15874     /**
15875      * Sets the text to display in this menu item
15876      * @param {String} text The text to display
15877      * @param {Boolean} isHTML true to indicate text is pure html.
15878      */
15879     setText : function(text, isHTML){
15880         if (isHTML) {
15881             this.html = text;
15882         } else {
15883             this.text = text;
15884             this.html = '';
15885         }
15886         if(this.rendered){
15887             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15888      
15889             this.el.update(String.format(
15890                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15891                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15892             this.parentMenu.autoWidth();
15893         }
15894     },
15895
15896     // private
15897     handleClick : function(e){
15898         if(!this.href){ // if no link defined, stop the event automatically
15899             e.stopEvent();
15900         }
15901         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15902     },
15903
15904     // private
15905     activate : function(autoExpand){
15906         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15907             this.focus();
15908             if(autoExpand){
15909                 this.expandMenu();
15910             }
15911         }
15912         return true;
15913     },
15914
15915     // private
15916     shouldDeactivate : function(e){
15917         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15918             if(this.menu && this.menu.isVisible()){
15919                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15920             }
15921             return true;
15922         }
15923         return false;
15924     },
15925
15926     // private
15927     deactivate : function(){
15928         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15929         this.hideMenu();
15930     },
15931
15932     // private
15933     expandMenu : function(autoActivate){
15934         if(!this.disabled && this.menu){
15935             clearTimeout(this.hideTimer);
15936             delete this.hideTimer;
15937             if(!this.menu.isVisible() && !this.showTimer){
15938                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15939             }else if (this.menu.isVisible() && autoActivate){
15940                 this.menu.tryActivate(0, 1);
15941             }
15942         }
15943     },
15944
15945     // private
15946     deferExpand : function(autoActivate){
15947         delete this.showTimer;
15948         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15949         if(autoActivate){
15950             this.menu.tryActivate(0, 1);
15951         }
15952     },
15953
15954     // private
15955     hideMenu : function(){
15956         clearTimeout(this.showTimer);
15957         delete this.showTimer;
15958         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15959             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15960         }
15961     },
15962
15963     // private
15964     deferHide : function(){
15965         delete this.hideTimer;
15966         this.menu.hide();
15967     }
15968 });/*
15969  * Based on:
15970  * Ext JS Library 1.1.1
15971  * Copyright(c) 2006-2007, Ext JS, LLC.
15972  *
15973  * Originally Released Under LGPL - original licence link has changed is not relivant.
15974  *
15975  * Fork - LGPL
15976  * <script type="text/javascript">
15977  */
15978  
15979 /**
15980  * @class Roo.menu.CheckItem
15981  * @extends Roo.menu.Item
15982  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15983  * @constructor
15984  * Creates a new CheckItem
15985  * @param {Object} config Configuration options
15986  */
15987 Roo.menu.CheckItem = function(config){
15988     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15989     this.addEvents({
15990         /**
15991          * @event beforecheckchange
15992          * Fires before the checked value is set, providing an opportunity to cancel if needed
15993          * @param {Roo.menu.CheckItem} this
15994          * @param {Boolean} checked The new checked value that will be set
15995          */
15996         "beforecheckchange" : true,
15997         /**
15998          * @event checkchange
15999          * Fires after the checked value has been set
16000          * @param {Roo.menu.CheckItem} this
16001          * @param {Boolean} checked The checked value that was set
16002          */
16003         "checkchange" : true
16004     });
16005     if(this.checkHandler){
16006         this.on('checkchange', this.checkHandler, this.scope);
16007     }
16008 };
16009 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16010     /**
16011      * @cfg {String} group
16012      * All check items with the same group name will automatically be grouped into a single-select
16013      * radio button group (defaults to '')
16014      */
16015     /**
16016      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16017      */
16018     itemCls : "x-menu-item x-menu-check-item",
16019     /**
16020      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16021      */
16022     groupClass : "x-menu-group-item",
16023
16024     /**
16025      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16026      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16027      * initialized with checked = true will be rendered as checked.
16028      */
16029     checked: false,
16030
16031     // private
16032     ctype: "Roo.menu.CheckItem",
16033
16034     // private
16035     onRender : function(c){
16036         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16037         if(this.group){
16038             this.el.addClass(this.groupClass);
16039         }
16040         Roo.menu.MenuMgr.registerCheckable(this);
16041         if(this.checked){
16042             this.checked = false;
16043             this.setChecked(true, true);
16044         }
16045     },
16046
16047     // private
16048     destroy : function(){
16049         if(this.rendered){
16050             Roo.menu.MenuMgr.unregisterCheckable(this);
16051         }
16052         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16053     },
16054
16055     /**
16056      * Set the checked state of this item
16057      * @param {Boolean} checked The new checked value
16058      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16059      */
16060     setChecked : function(state, suppressEvent){
16061         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16062             if(this.container){
16063                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16064             }
16065             this.checked = state;
16066             if(suppressEvent !== true){
16067                 this.fireEvent("checkchange", this, state);
16068             }
16069         }
16070     },
16071
16072     // private
16073     handleClick : function(e){
16074        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16075            this.setChecked(!this.checked);
16076        }
16077        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16078     }
16079 });/*
16080  * Based on:
16081  * Ext JS Library 1.1.1
16082  * Copyright(c) 2006-2007, Ext JS, LLC.
16083  *
16084  * Originally Released Under LGPL - original licence link has changed is not relivant.
16085  *
16086  * Fork - LGPL
16087  * <script type="text/javascript">
16088  */
16089  
16090 /**
16091  * @class Roo.menu.DateItem
16092  * @extends Roo.menu.Adapter
16093  * A menu item that wraps the {@link Roo.DatPicker} component.
16094  * @constructor
16095  * Creates a new DateItem
16096  * @param {Object} config Configuration options
16097  */
16098 Roo.menu.DateItem = function(config){
16099     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16100     /** The Roo.DatePicker object @type Roo.DatePicker */
16101     this.picker = this.component;
16102     this.addEvents({select: true});
16103     
16104     this.picker.on("render", function(picker){
16105         picker.getEl().swallowEvent("click");
16106         picker.container.addClass("x-menu-date-item");
16107     });
16108
16109     this.picker.on("select", this.onSelect, this);
16110 };
16111
16112 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16113     // private
16114     onSelect : function(picker, date){
16115         this.fireEvent("select", this, date, picker);
16116         Roo.menu.DateItem.superclass.handleClick.call(this);
16117     }
16118 });/*
16119  * Based on:
16120  * Ext JS Library 1.1.1
16121  * Copyright(c) 2006-2007, Ext JS, LLC.
16122  *
16123  * Originally Released Under LGPL - original licence link has changed is not relivant.
16124  *
16125  * Fork - LGPL
16126  * <script type="text/javascript">
16127  */
16128  
16129 /**
16130  * @class Roo.menu.ColorItem
16131  * @extends Roo.menu.Adapter
16132  * A menu item that wraps the {@link Roo.ColorPalette} component.
16133  * @constructor
16134  * Creates a new ColorItem
16135  * @param {Object} config Configuration options
16136  */
16137 Roo.menu.ColorItem = function(config){
16138     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16139     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16140     this.palette = this.component;
16141     this.relayEvents(this.palette, ["select"]);
16142     if(this.selectHandler){
16143         this.on('select', this.selectHandler, this.scope);
16144     }
16145 };
16146 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16147  * Based on:
16148  * Ext JS Library 1.1.1
16149  * Copyright(c) 2006-2007, Ext JS, LLC.
16150  *
16151  * Originally Released Under LGPL - original licence link has changed is not relivant.
16152  *
16153  * Fork - LGPL
16154  * <script type="text/javascript">
16155  */
16156  
16157
16158 /**
16159  * @class Roo.menu.DateMenu
16160  * @extends Roo.menu.Menu
16161  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16162  * @constructor
16163  * Creates a new DateMenu
16164  * @param {Object} config Configuration options
16165  */
16166 Roo.menu.DateMenu = function(config){
16167     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16168     this.plain = true;
16169     var di = new Roo.menu.DateItem(config);
16170     this.add(di);
16171     /**
16172      * The {@link Roo.DatePicker} instance for this DateMenu
16173      * @type DatePicker
16174      */
16175     this.picker = di.picker;
16176     /**
16177      * @event select
16178      * @param {DatePicker} picker
16179      * @param {Date} date
16180      */
16181     this.relayEvents(di, ["select"]);
16182     this.on('beforeshow', function(){
16183         if(this.picker){
16184             this.picker.hideMonthPicker(false);
16185         }
16186     }, this);
16187 };
16188 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16189     cls:'x-date-menu'
16190 });/*
16191  * Based on:
16192  * Ext JS Library 1.1.1
16193  * Copyright(c) 2006-2007, Ext JS, LLC.
16194  *
16195  * Originally Released Under LGPL - original licence link has changed is not relivant.
16196  *
16197  * Fork - LGPL
16198  * <script type="text/javascript">
16199  */
16200  
16201
16202 /**
16203  * @class Roo.menu.ColorMenu
16204  * @extends Roo.menu.Menu
16205  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16206  * @constructor
16207  * Creates a new ColorMenu
16208  * @param {Object} config Configuration options
16209  */
16210 Roo.menu.ColorMenu = function(config){
16211     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16212     this.plain = true;
16213     var ci = new Roo.menu.ColorItem(config);
16214     this.add(ci);
16215     /**
16216      * The {@link Roo.ColorPalette} instance for this ColorMenu
16217      * @type ColorPalette
16218      */
16219     this.palette = ci.palette;
16220     /**
16221      * @event select
16222      * @param {ColorPalette} palette
16223      * @param {String} color
16224      */
16225     this.relayEvents(ci, ["select"]);
16226 };
16227 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16228  * Based on:
16229  * Ext JS Library 1.1.1
16230  * Copyright(c) 2006-2007, Ext JS, LLC.
16231  *
16232  * Originally Released Under LGPL - original licence link has changed is not relivant.
16233  *
16234  * Fork - LGPL
16235  * <script type="text/javascript">
16236  */
16237  
16238 /**
16239  * @class Roo.form.Field
16240  * @extends Roo.BoxComponent
16241  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16242  * @constructor
16243  * Creates a new Field
16244  * @param {Object} config Configuration options
16245  */
16246 Roo.form.Field = function(config){
16247     Roo.form.Field.superclass.constructor.call(this, config);
16248 };
16249
16250 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16251     /**
16252      * @cfg {String} fieldLabel Label to use when rendering a form.
16253      */
16254        /**
16255      * @cfg {String} qtip Mouse over tip
16256      */
16257      
16258     /**
16259      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16260      */
16261     invalidClass : "x-form-invalid",
16262     /**
16263      * @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")
16264      */
16265     invalidText : "The value in this field is invalid",
16266     /**
16267      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16268      */
16269     focusClass : "x-form-focus",
16270     /**
16271      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16272       automatic validation (defaults to "keyup").
16273      */
16274     validationEvent : "keyup",
16275     /**
16276      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16277      */
16278     validateOnBlur : true,
16279     /**
16280      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16281      */
16282     validationDelay : 250,
16283     /**
16284      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16285      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16286      */
16287     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16288     /**
16289      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16290      */
16291     fieldClass : "x-form-field",
16292     /**
16293      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16294      *<pre>
16295 Value         Description
16296 -----------   ----------------------------------------------------------------------
16297 qtip          Display a quick tip when the user hovers over the field
16298 title         Display a default browser title attribute popup
16299 under         Add a block div beneath the field containing the error text
16300 side          Add an error icon to the right of the field with a popup on hover
16301 [element id]  Add the error text directly to the innerHTML of the specified element
16302 </pre>
16303      */
16304     msgTarget : 'qtip',
16305     /**
16306      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16307      */
16308     msgFx : 'normal',
16309
16310     /**
16311      * @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.
16312      */
16313     readOnly : false,
16314
16315     /**
16316      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16317      */
16318     disabled : false,
16319
16320     /**
16321      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16322      */
16323     inputType : undefined,
16324     
16325     /**
16326      * @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).
16327          */
16328         tabIndex : undefined,
16329         
16330     // private
16331     isFormField : true,
16332
16333     // private
16334     hasFocus : false,
16335     /**
16336      * @property {Roo.Element} fieldEl
16337      * Element Containing the rendered Field (with label etc.)
16338      */
16339     /**
16340      * @cfg {Mixed} value A value to initialize this field with.
16341      */
16342     value : undefined,
16343
16344     /**
16345      * @cfg {String} name The field's HTML name attribute.
16346      */
16347     /**
16348      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16349      */
16350     // private
16351     loadedValue : false,
16352      
16353      
16354         // private ??
16355         initComponent : function(){
16356         Roo.form.Field.superclass.initComponent.call(this);
16357         this.addEvents({
16358             /**
16359              * @event focus
16360              * Fires when this field receives input focus.
16361              * @param {Roo.form.Field} this
16362              */
16363             focus : true,
16364             /**
16365              * @event blur
16366              * Fires when this field loses input focus.
16367              * @param {Roo.form.Field} this
16368              */
16369             blur : true,
16370             /**
16371              * @event specialkey
16372              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16373              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16374              * @param {Roo.form.Field} this
16375              * @param {Roo.EventObject} e The event object
16376              */
16377             specialkey : true,
16378             /**
16379              * @event change
16380              * Fires just before the field blurs if the field value has changed.
16381              * @param {Roo.form.Field} this
16382              * @param {Mixed} newValue The new value
16383              * @param {Mixed} oldValue The original value
16384              */
16385             change : true,
16386             /**
16387              * @event invalid
16388              * Fires after the field has been marked as invalid.
16389              * @param {Roo.form.Field} this
16390              * @param {String} msg The validation message
16391              */
16392             invalid : true,
16393             /**
16394              * @event valid
16395              * Fires after the field has been validated with no errors.
16396              * @param {Roo.form.Field} this
16397              */
16398             valid : true,
16399              /**
16400              * @event keyup
16401              * Fires after the key up
16402              * @param {Roo.form.Field} this
16403              * @param {Roo.EventObject}  e The event Object
16404              */
16405             keyup : true
16406         });
16407     },
16408
16409     /**
16410      * Returns the name attribute of the field if available
16411      * @return {String} name The field name
16412      */
16413     getName: function(){
16414          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16415     },
16416
16417     // private
16418     onRender : function(ct, position){
16419         Roo.form.Field.superclass.onRender.call(this, ct, position);
16420         if(!this.el){
16421             var cfg = this.getAutoCreate();
16422             if(!cfg.name){
16423                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16424             }
16425             if (!cfg.name.length) {
16426                 delete cfg.name;
16427             }
16428             if(this.inputType){
16429                 cfg.type = this.inputType;
16430             }
16431             this.el = ct.createChild(cfg, position);
16432         }
16433         var type = this.el.dom.type;
16434         if(type){
16435             if(type == 'password'){
16436                 type = 'text';
16437             }
16438             this.el.addClass('x-form-'+type);
16439         }
16440         if(this.readOnly){
16441             this.el.dom.readOnly = true;
16442         }
16443         if(this.tabIndex !== undefined){
16444             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16445         }
16446
16447         this.el.addClass([this.fieldClass, this.cls]);
16448         this.initValue();
16449     },
16450
16451     /**
16452      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16453      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16454      * @return {Roo.form.Field} this
16455      */
16456     applyTo : function(target){
16457         this.allowDomMove = false;
16458         this.el = Roo.get(target);
16459         this.render(this.el.dom.parentNode);
16460         return this;
16461     },
16462
16463     // private
16464     initValue : function(){
16465         if(this.value !== undefined){
16466             this.setValue(this.value);
16467         }else if(this.el.dom.value.length > 0){
16468             this.setValue(this.el.dom.value);
16469         }
16470     },
16471
16472     /**
16473      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16474      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16475      */
16476     isDirty : function() {
16477         if(this.disabled) {
16478             return false;
16479         }
16480         return String(this.getValue()) !== String(this.originalValue);
16481     },
16482
16483     /**
16484      * stores the current value in loadedValue
16485      */
16486     resetHasChanged : function()
16487     {
16488         this.loadedValue = String(this.getValue());
16489     },
16490     /**
16491      * checks the current value against the 'loaded' value.
16492      * Note - will return false if 'resetHasChanged' has not been called first.
16493      */
16494     hasChanged : function()
16495     {
16496         if(this.disabled || this.readOnly) {
16497             return false;
16498         }
16499         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16500     },
16501     
16502     
16503     
16504     // private
16505     afterRender : function(){
16506         Roo.form.Field.superclass.afterRender.call(this);
16507         this.initEvents();
16508     },
16509
16510     // private
16511     fireKey : function(e){
16512         //Roo.log('field ' + e.getKey());
16513         if(e.isNavKeyPress()){
16514             this.fireEvent("specialkey", this, e);
16515         }
16516     },
16517
16518     /**
16519      * Resets the current field value to the originally loaded value and clears any validation messages
16520      */
16521     reset : function(){
16522         this.setValue(this.resetValue);
16523         this.clearInvalid();
16524     },
16525
16526     // private
16527     initEvents : function(){
16528         // safari killled keypress - so keydown is now used..
16529         this.el.on("keydown" , this.fireKey,  this);
16530         this.el.on("focus", this.onFocus,  this);
16531         this.el.on("blur", this.onBlur,  this);
16532         this.el.relayEvent('keyup', this);
16533
16534         // reference to original value for reset
16535         this.originalValue = this.getValue();
16536         this.resetValue =  this.getValue();
16537     },
16538
16539     // private
16540     onFocus : function(){
16541         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16542             this.el.addClass(this.focusClass);
16543         }
16544         if(!this.hasFocus){
16545             this.hasFocus = true;
16546             this.startValue = this.getValue();
16547             this.fireEvent("focus", this);
16548         }
16549     },
16550
16551     beforeBlur : Roo.emptyFn,
16552
16553     // private
16554     onBlur : function(){
16555         this.beforeBlur();
16556         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16557             this.el.removeClass(this.focusClass);
16558         }
16559         this.hasFocus = false;
16560         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16561             this.validate();
16562         }
16563         var v = this.getValue();
16564         if(String(v) !== String(this.startValue)){
16565             this.fireEvent('change', this, v, this.startValue);
16566         }
16567         this.fireEvent("blur", this);
16568     },
16569
16570     /**
16571      * Returns whether or not the field value is currently valid
16572      * @param {Boolean} preventMark True to disable marking the field invalid
16573      * @return {Boolean} True if the value is valid, else false
16574      */
16575     isValid : function(preventMark){
16576         if(this.disabled){
16577             return true;
16578         }
16579         var restore = this.preventMark;
16580         this.preventMark = preventMark === true;
16581         var v = this.validateValue(this.processValue(this.getRawValue()));
16582         this.preventMark = restore;
16583         return v;
16584     },
16585
16586     /**
16587      * Validates the field value
16588      * @return {Boolean} True if the value is valid, else false
16589      */
16590     validate : function(){
16591         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16592             this.clearInvalid();
16593             return true;
16594         }
16595         return false;
16596     },
16597
16598     processValue : function(value){
16599         return value;
16600     },
16601
16602     // private
16603     // Subclasses should provide the validation implementation by overriding this
16604     validateValue : function(value){
16605         return true;
16606     },
16607
16608     /**
16609      * Mark this field as invalid
16610      * @param {String} msg The validation message
16611      */
16612     markInvalid : function(msg){
16613         if(!this.rendered || this.preventMark){ // not rendered
16614             return;
16615         }
16616         
16617         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16618         
16619         obj.el.addClass(this.invalidClass);
16620         msg = msg || this.invalidText;
16621         switch(this.msgTarget){
16622             case 'qtip':
16623                 obj.el.dom.qtip = msg;
16624                 obj.el.dom.qclass = 'x-form-invalid-tip';
16625                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16626                     Roo.QuickTips.enable();
16627                 }
16628                 break;
16629             case 'title':
16630                 this.el.dom.title = msg;
16631                 break;
16632             case 'under':
16633                 if(!this.errorEl){
16634                     var elp = this.el.findParent('.x-form-element', 5, true);
16635                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16636                     this.errorEl.setWidth(elp.getWidth(true)-20);
16637                 }
16638                 this.errorEl.update(msg);
16639                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16640                 break;
16641             case 'side':
16642                 if(!this.errorIcon){
16643                     var elp = this.el.findParent('.x-form-element', 5, true);
16644                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16645                 }
16646                 this.alignErrorIcon();
16647                 this.errorIcon.dom.qtip = msg;
16648                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16649                 this.errorIcon.show();
16650                 this.on('resize', this.alignErrorIcon, this);
16651                 break;
16652             default:
16653                 var t = Roo.getDom(this.msgTarget);
16654                 t.innerHTML = msg;
16655                 t.style.display = this.msgDisplay;
16656                 break;
16657         }
16658         this.fireEvent('invalid', this, msg);
16659     },
16660
16661     // private
16662     alignErrorIcon : function(){
16663         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16664     },
16665
16666     /**
16667      * Clear any invalid styles/messages for this field
16668      */
16669     clearInvalid : function(){
16670         if(!this.rendered || this.preventMark){ // not rendered
16671             return;
16672         }
16673         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16674         
16675         obj.el.removeClass(this.invalidClass);
16676         switch(this.msgTarget){
16677             case 'qtip':
16678                 obj.el.dom.qtip = '';
16679                 break;
16680             case 'title':
16681                 this.el.dom.title = '';
16682                 break;
16683             case 'under':
16684                 if(this.errorEl){
16685                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16686                 }
16687                 break;
16688             case 'side':
16689                 if(this.errorIcon){
16690                     this.errorIcon.dom.qtip = '';
16691                     this.errorIcon.hide();
16692                     this.un('resize', this.alignErrorIcon, this);
16693                 }
16694                 break;
16695             default:
16696                 var t = Roo.getDom(this.msgTarget);
16697                 t.innerHTML = '';
16698                 t.style.display = 'none';
16699                 break;
16700         }
16701         this.fireEvent('valid', this);
16702     },
16703
16704     /**
16705      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16706      * @return {Mixed} value The field value
16707      */
16708     getRawValue : function(){
16709         var v = this.el.getValue();
16710         
16711         return v;
16712     },
16713
16714     /**
16715      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16716      * @return {Mixed} value The field value
16717      */
16718     getValue : function(){
16719         var v = this.el.getValue();
16720          
16721         return v;
16722     },
16723
16724     /**
16725      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16726      * @param {Mixed} value The value to set
16727      */
16728     setRawValue : function(v){
16729         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16730     },
16731
16732     /**
16733      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16734      * @param {Mixed} value The value to set
16735      */
16736     setValue : function(v){
16737         this.value = v;
16738         if(this.rendered){
16739             this.el.dom.value = (v === null || v === undefined ? '' : v);
16740              this.validate();
16741         }
16742     },
16743
16744     adjustSize : function(w, h){
16745         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16746         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16747         return s;
16748     },
16749
16750     adjustWidth : function(tag, w){
16751         tag = tag.toLowerCase();
16752         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16753             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16754                 if(tag == 'input'){
16755                     return w + 2;
16756                 }
16757                 if(tag == 'textarea'){
16758                     return w-2;
16759                 }
16760             }else if(Roo.isOpera){
16761                 if(tag == 'input'){
16762                     return w + 2;
16763                 }
16764                 if(tag == 'textarea'){
16765                     return w-2;
16766                 }
16767             }
16768         }
16769         return w;
16770     }
16771 });
16772
16773
16774 // anything other than normal should be considered experimental
16775 Roo.form.Field.msgFx = {
16776     normal : {
16777         show: function(msgEl, f){
16778             msgEl.setDisplayed('block');
16779         },
16780
16781         hide : function(msgEl, f){
16782             msgEl.setDisplayed(false).update('');
16783         }
16784     },
16785
16786     slide : {
16787         show: function(msgEl, f){
16788             msgEl.slideIn('t', {stopFx:true});
16789         },
16790
16791         hide : function(msgEl, f){
16792             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16793         }
16794     },
16795
16796     slideRight : {
16797         show: function(msgEl, f){
16798             msgEl.fixDisplay();
16799             msgEl.alignTo(f.el, 'tl-tr');
16800             msgEl.slideIn('l', {stopFx:true});
16801         },
16802
16803         hide : function(msgEl, f){
16804             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16805         }
16806     }
16807 };/*
16808  * Based on:
16809  * Ext JS Library 1.1.1
16810  * Copyright(c) 2006-2007, Ext JS, LLC.
16811  *
16812  * Originally Released Under LGPL - original licence link has changed is not relivant.
16813  *
16814  * Fork - LGPL
16815  * <script type="text/javascript">
16816  */
16817  
16818
16819 /**
16820  * @class Roo.form.TextField
16821  * @extends Roo.form.Field
16822  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16823  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16824  * @constructor
16825  * Creates a new TextField
16826  * @param {Object} config Configuration options
16827  */
16828 Roo.form.TextField = function(config){
16829     Roo.form.TextField.superclass.constructor.call(this, config);
16830     this.addEvents({
16831         /**
16832          * @event autosize
16833          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16834          * according to the default logic, but this event provides a hook for the developer to apply additional
16835          * logic at runtime to resize the field if needed.
16836              * @param {Roo.form.Field} this This text field
16837              * @param {Number} width The new field width
16838              */
16839         autosize : true
16840     });
16841 };
16842
16843 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16844     /**
16845      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16846      */
16847     grow : false,
16848     /**
16849      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16850      */
16851     growMin : 30,
16852     /**
16853      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16854      */
16855     growMax : 800,
16856     /**
16857      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16858      */
16859     vtype : null,
16860     /**
16861      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16862      */
16863     maskRe : null,
16864     /**
16865      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16866      */
16867     disableKeyFilter : false,
16868     /**
16869      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16870      */
16871     allowBlank : true,
16872     /**
16873      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16874      */
16875     minLength : 0,
16876     /**
16877      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16878      */
16879     maxLength : Number.MAX_VALUE,
16880     /**
16881      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16882      */
16883     minLengthText : "The minimum length for this field is {0}",
16884     /**
16885      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16886      */
16887     maxLengthText : "The maximum length for this field is {0}",
16888     /**
16889      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16890      */
16891     selectOnFocus : false,
16892     /**
16893      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16894      */
16895     blankText : "This field is required",
16896     /**
16897      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16898      * If available, this function will be called only after the basic validators all return true, and will be passed the
16899      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16900      */
16901     validator : null,
16902     /**
16903      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16904      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16905      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16906      */
16907     regex : null,
16908     /**
16909      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16910      */
16911     regexText : "",
16912     /**
16913      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16914      */
16915     emptyText : null,
16916    
16917
16918     // private
16919     initEvents : function()
16920     {
16921         if (this.emptyText) {
16922             this.el.attr('placeholder', this.emptyText);
16923         }
16924         
16925         Roo.form.TextField.superclass.initEvents.call(this);
16926         if(this.validationEvent == 'keyup'){
16927             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16928             this.el.on('keyup', this.filterValidation, this);
16929         }
16930         else if(this.validationEvent !== false){
16931             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16932         }
16933         
16934         if(this.selectOnFocus){
16935             this.on("focus", this.preFocus, this);
16936             
16937         }
16938         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16939             this.el.on("keypress", this.filterKeys, this);
16940         }
16941         if(this.grow){
16942             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16943             this.el.on("click", this.autoSize,  this);
16944         }
16945         if(this.el.is('input[type=password]') && Roo.isSafari){
16946             this.el.on('keydown', this.SafariOnKeyDown, this);
16947         }
16948     },
16949
16950     processValue : function(value){
16951         if(this.stripCharsRe){
16952             var newValue = value.replace(this.stripCharsRe, '');
16953             if(newValue !== value){
16954                 this.setRawValue(newValue);
16955                 return newValue;
16956             }
16957         }
16958         return value;
16959     },
16960
16961     filterValidation : function(e){
16962         if(!e.isNavKeyPress()){
16963             this.validationTask.delay(this.validationDelay);
16964         }
16965     },
16966
16967     // private
16968     onKeyUp : function(e){
16969         if(!e.isNavKeyPress()){
16970             this.autoSize();
16971         }
16972     },
16973
16974     /**
16975      * Resets the current field value to the originally-loaded value and clears any validation messages.
16976      *  
16977      */
16978     reset : function(){
16979         Roo.form.TextField.superclass.reset.call(this);
16980        
16981     },
16982
16983     
16984     // private
16985     preFocus : function(){
16986         
16987         if(this.selectOnFocus){
16988             this.el.dom.select();
16989         }
16990     },
16991
16992     
16993     // private
16994     filterKeys : function(e){
16995         var k = e.getKey();
16996         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16997             return;
16998         }
16999         var c = e.getCharCode(), cc = String.fromCharCode(c);
17000         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17001             return;
17002         }
17003         if(!this.maskRe.test(cc)){
17004             e.stopEvent();
17005         }
17006     },
17007
17008     setValue : function(v){
17009         
17010         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17011         
17012         this.autoSize();
17013     },
17014
17015     /**
17016      * Validates a value according to the field's validation rules and marks the field as invalid
17017      * if the validation fails
17018      * @param {Mixed} value The value to validate
17019      * @return {Boolean} True if the value is valid, else false
17020      */
17021     validateValue : function(value){
17022         if(value.length < 1)  { // if it's blank
17023              if(this.allowBlank){
17024                 this.clearInvalid();
17025                 return true;
17026              }else{
17027                 this.markInvalid(this.blankText);
17028                 return false;
17029              }
17030         }
17031         if(value.length < this.minLength){
17032             this.markInvalid(String.format(this.minLengthText, this.minLength));
17033             return false;
17034         }
17035         if(value.length > this.maxLength){
17036             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17037             return false;
17038         }
17039         if(this.vtype){
17040             var vt = Roo.form.VTypes;
17041             if(!vt[this.vtype](value, this)){
17042                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17043                 return false;
17044             }
17045         }
17046         if(typeof this.validator == "function"){
17047             var msg = this.validator(value);
17048             if(msg !== true){
17049                 this.markInvalid(msg);
17050                 return false;
17051             }
17052         }
17053         if(this.regex && !this.regex.test(value)){
17054             this.markInvalid(this.regexText);
17055             return false;
17056         }
17057         return true;
17058     },
17059
17060     /**
17061      * Selects text in this field
17062      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17063      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17064      */
17065     selectText : function(start, end){
17066         var v = this.getRawValue();
17067         if(v.length > 0){
17068             start = start === undefined ? 0 : start;
17069             end = end === undefined ? v.length : end;
17070             var d = this.el.dom;
17071             if(d.setSelectionRange){
17072                 d.setSelectionRange(start, end);
17073             }else if(d.createTextRange){
17074                 var range = d.createTextRange();
17075                 range.moveStart("character", start);
17076                 range.moveEnd("character", v.length-end);
17077                 range.select();
17078             }
17079         }
17080     },
17081
17082     /**
17083      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17084      * This only takes effect if grow = true, and fires the autosize event.
17085      */
17086     autoSize : function(){
17087         if(!this.grow || !this.rendered){
17088             return;
17089         }
17090         if(!this.metrics){
17091             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17092         }
17093         var el = this.el;
17094         var v = el.dom.value;
17095         var d = document.createElement('div');
17096         d.appendChild(document.createTextNode(v));
17097         v = d.innerHTML;
17098         d = null;
17099         v += "&#160;";
17100         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17101         this.el.setWidth(w);
17102         this.fireEvent("autosize", this, w);
17103     },
17104     
17105     // private
17106     SafariOnKeyDown : function(event)
17107     {
17108         // this is a workaround for a password hang bug on chrome/ webkit.
17109         
17110         var isSelectAll = false;
17111         
17112         if(this.el.dom.selectionEnd > 0){
17113             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17114         }
17115         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17116             event.preventDefault();
17117             this.setValue('');
17118             return;
17119         }
17120         
17121         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17122             
17123             event.preventDefault();
17124             // this is very hacky as keydown always get's upper case.
17125             
17126             var cc = String.fromCharCode(event.getCharCode());
17127             
17128             
17129             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17130             
17131         }
17132         
17133         
17134     }
17135 });/*
17136  * Based on:
17137  * Ext JS Library 1.1.1
17138  * Copyright(c) 2006-2007, Ext JS, LLC.
17139  *
17140  * Originally Released Under LGPL - original licence link has changed is not relivant.
17141  *
17142  * Fork - LGPL
17143  * <script type="text/javascript">
17144  */
17145  
17146 /**
17147  * @class Roo.form.Hidden
17148  * @extends Roo.form.TextField
17149  * Simple Hidden element used on forms 
17150  * 
17151  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17152  * 
17153  * @constructor
17154  * Creates a new Hidden form element.
17155  * @param {Object} config Configuration options
17156  */
17157
17158
17159
17160 // easy hidden field...
17161 Roo.form.Hidden = function(config){
17162     Roo.form.Hidden.superclass.constructor.call(this, config);
17163 };
17164   
17165 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17166     fieldLabel:      '',
17167     inputType:      'hidden',
17168     width:          50,
17169     allowBlank:     true,
17170     labelSeparator: '',
17171     hidden:         true,
17172     itemCls :       'x-form-item-display-none'
17173
17174
17175 });
17176
17177
17178 /*
17179  * Based on:
17180  * Ext JS Library 1.1.1
17181  * Copyright(c) 2006-2007, Ext JS, LLC.
17182  *
17183  * Originally Released Under LGPL - original licence link has changed is not relivant.
17184  *
17185  * Fork - LGPL
17186  * <script type="text/javascript">
17187  */
17188  
17189 /**
17190  * @class Roo.form.TriggerField
17191  * @extends Roo.form.TextField
17192  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17193  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17194  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17195  * for which you can provide a custom implementation.  For example:
17196  * <pre><code>
17197 var trigger = new Roo.form.TriggerField();
17198 trigger.onTriggerClick = myTriggerFn;
17199 trigger.applyTo('my-field');
17200 </code></pre>
17201  *
17202  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17203  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17204  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17205  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17206  * @constructor
17207  * Create a new TriggerField.
17208  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17209  * to the base TextField)
17210  */
17211 Roo.form.TriggerField = function(config){
17212     this.mimicing = false;
17213     Roo.form.TriggerField.superclass.constructor.call(this, config);
17214 };
17215
17216 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17217     /**
17218      * @cfg {String} triggerClass A CSS class to apply to the trigger
17219      */
17220     /**
17221      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17222      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17223      */
17224     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17225     /**
17226      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17227      */
17228     hideTrigger:false,
17229
17230     /** @cfg {Boolean} grow @hide */
17231     /** @cfg {Number} growMin @hide */
17232     /** @cfg {Number} growMax @hide */
17233
17234     /**
17235      * @hide 
17236      * @method
17237      */
17238     autoSize: Roo.emptyFn,
17239     // private
17240     monitorTab : true,
17241     // private
17242     deferHeight : true,
17243
17244     
17245     actionMode : 'wrap',
17246     // private
17247     onResize : function(w, h){
17248         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17249         if(typeof w == 'number'){
17250             var x = w - this.trigger.getWidth();
17251             this.el.setWidth(this.adjustWidth('input', x));
17252             this.trigger.setStyle('left', x+'px');
17253         }
17254     },
17255
17256     // private
17257     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17258
17259     // private
17260     getResizeEl : function(){
17261         return this.wrap;
17262     },
17263
17264     // private
17265     getPositionEl : function(){
17266         return this.wrap;
17267     },
17268
17269     // private
17270     alignErrorIcon : function(){
17271         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17272     },
17273
17274     // private
17275     onRender : function(ct, position){
17276         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17277         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17278         this.trigger = this.wrap.createChild(this.triggerConfig ||
17279                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17280         if(this.hideTrigger){
17281             this.trigger.setDisplayed(false);
17282         }
17283         this.initTrigger();
17284         if(!this.width){
17285             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17286         }
17287     },
17288
17289     // private
17290     initTrigger : function(){
17291         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17292         this.trigger.addClassOnOver('x-form-trigger-over');
17293         this.trigger.addClassOnClick('x-form-trigger-click');
17294     },
17295
17296     // private
17297     onDestroy : function(){
17298         if(this.trigger){
17299             this.trigger.removeAllListeners();
17300             this.trigger.remove();
17301         }
17302         if(this.wrap){
17303             this.wrap.remove();
17304         }
17305         Roo.form.TriggerField.superclass.onDestroy.call(this);
17306     },
17307
17308     // private
17309     onFocus : function(){
17310         Roo.form.TriggerField.superclass.onFocus.call(this);
17311         if(!this.mimicing){
17312             this.wrap.addClass('x-trigger-wrap-focus');
17313             this.mimicing = true;
17314             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17315             if(this.monitorTab){
17316                 this.el.on("keydown", this.checkTab, this);
17317             }
17318         }
17319     },
17320
17321     // private
17322     checkTab : function(e){
17323         if(e.getKey() == e.TAB){
17324             this.triggerBlur();
17325         }
17326     },
17327
17328     // private
17329     onBlur : function(){
17330         // do nothing
17331     },
17332
17333     // private
17334     mimicBlur : function(e, t){
17335         if(!this.wrap.contains(t) && this.validateBlur()){
17336             this.triggerBlur();
17337         }
17338     },
17339
17340     // private
17341     triggerBlur : function(){
17342         this.mimicing = false;
17343         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17344         if(this.monitorTab){
17345             this.el.un("keydown", this.checkTab, this);
17346         }
17347         this.wrap.removeClass('x-trigger-wrap-focus');
17348         Roo.form.TriggerField.superclass.onBlur.call(this);
17349     },
17350
17351     // private
17352     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17353     validateBlur : function(e, t){
17354         return true;
17355     },
17356
17357     // private
17358     onDisable : function(){
17359         Roo.form.TriggerField.superclass.onDisable.call(this);
17360         if(this.wrap){
17361             this.wrap.addClass('x-item-disabled');
17362         }
17363     },
17364
17365     // private
17366     onEnable : function(){
17367         Roo.form.TriggerField.superclass.onEnable.call(this);
17368         if(this.wrap){
17369             this.wrap.removeClass('x-item-disabled');
17370         }
17371     },
17372
17373     // private
17374     onShow : function(){
17375         var ae = this.getActionEl();
17376         
17377         if(ae){
17378             ae.dom.style.display = '';
17379             ae.dom.style.visibility = 'visible';
17380         }
17381     },
17382
17383     // private
17384     
17385     onHide : function(){
17386         var ae = this.getActionEl();
17387         ae.dom.style.display = 'none';
17388     },
17389
17390     /**
17391      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17392      * by an implementing function.
17393      * @method
17394      * @param {EventObject} e
17395      */
17396     onTriggerClick : Roo.emptyFn
17397 });
17398
17399 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17400 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17401 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17402 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17403     initComponent : function(){
17404         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17405
17406         this.triggerConfig = {
17407             tag:'span', cls:'x-form-twin-triggers', cn:[
17408             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17409             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17410         ]};
17411     },
17412
17413     getTrigger : function(index){
17414         return this.triggers[index];
17415     },
17416
17417     initTrigger : function(){
17418         var ts = this.trigger.select('.x-form-trigger', true);
17419         this.wrap.setStyle('overflow', 'hidden');
17420         var triggerField = this;
17421         ts.each(function(t, all, index){
17422             t.hide = function(){
17423                 var w = triggerField.wrap.getWidth();
17424                 this.dom.style.display = 'none';
17425                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17426             };
17427             t.show = function(){
17428                 var w = triggerField.wrap.getWidth();
17429                 this.dom.style.display = '';
17430                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17431             };
17432             var triggerIndex = 'Trigger'+(index+1);
17433
17434             if(this['hide'+triggerIndex]){
17435                 t.dom.style.display = 'none';
17436             }
17437             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17438             t.addClassOnOver('x-form-trigger-over');
17439             t.addClassOnClick('x-form-trigger-click');
17440         }, this);
17441         this.triggers = ts.elements;
17442     },
17443
17444     onTrigger1Click : Roo.emptyFn,
17445     onTrigger2Click : Roo.emptyFn
17446 });/*
17447  * Based on:
17448  * Ext JS Library 1.1.1
17449  * Copyright(c) 2006-2007, Ext JS, LLC.
17450  *
17451  * Originally Released Under LGPL - original licence link has changed is not relivant.
17452  *
17453  * Fork - LGPL
17454  * <script type="text/javascript">
17455  */
17456  
17457 /**
17458  * @class Roo.form.TextArea
17459  * @extends Roo.form.TextField
17460  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17461  * support for auto-sizing.
17462  * @constructor
17463  * Creates a new TextArea
17464  * @param {Object} config Configuration options
17465  */
17466 Roo.form.TextArea = function(config){
17467     Roo.form.TextArea.superclass.constructor.call(this, config);
17468     // these are provided exchanges for backwards compat
17469     // minHeight/maxHeight were replaced by growMin/growMax to be
17470     // compatible with TextField growing config values
17471     if(this.minHeight !== undefined){
17472         this.growMin = this.minHeight;
17473     }
17474     if(this.maxHeight !== undefined){
17475         this.growMax = this.maxHeight;
17476     }
17477 };
17478
17479 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17480     /**
17481      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17482      */
17483     growMin : 60,
17484     /**
17485      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17486      */
17487     growMax: 1000,
17488     /**
17489      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17490      * in the field (equivalent to setting overflow: hidden, defaults to false)
17491      */
17492     preventScrollbars: false,
17493     /**
17494      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17495      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17496      */
17497
17498     // private
17499     onRender : function(ct, position){
17500         if(!this.el){
17501             this.defaultAutoCreate = {
17502                 tag: "textarea",
17503                 style:"width:300px;height:60px;",
17504                 autocomplete: "new-password"
17505             };
17506         }
17507         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17508         if(this.grow){
17509             this.textSizeEl = Roo.DomHelper.append(document.body, {
17510                 tag: "pre", cls: "x-form-grow-sizer"
17511             });
17512             if(this.preventScrollbars){
17513                 this.el.setStyle("overflow", "hidden");
17514             }
17515             this.el.setHeight(this.growMin);
17516         }
17517     },
17518
17519     onDestroy : function(){
17520         if(this.textSizeEl){
17521             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17522         }
17523         Roo.form.TextArea.superclass.onDestroy.call(this);
17524     },
17525
17526     // private
17527     onKeyUp : function(e){
17528         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17529             this.autoSize();
17530         }
17531     },
17532
17533     /**
17534      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17535      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17536      */
17537     autoSize : function(){
17538         if(!this.grow || !this.textSizeEl){
17539             return;
17540         }
17541         var el = this.el;
17542         var v = el.dom.value;
17543         var ts = this.textSizeEl;
17544
17545         ts.innerHTML = '';
17546         ts.appendChild(document.createTextNode(v));
17547         v = ts.innerHTML;
17548
17549         Roo.fly(ts).setWidth(this.el.getWidth());
17550         if(v.length < 1){
17551             v = "&#160;&#160;";
17552         }else{
17553             if(Roo.isIE){
17554                 v = v.replace(/\n/g, '<p>&#160;</p>');
17555             }
17556             v += "&#160;\n&#160;";
17557         }
17558         ts.innerHTML = v;
17559         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17560         if(h != this.lastHeight){
17561             this.lastHeight = h;
17562             this.el.setHeight(h);
17563             this.fireEvent("autosize", this, h);
17564         }
17565     }
17566 });/*
17567  * Based on:
17568  * Ext JS Library 1.1.1
17569  * Copyright(c) 2006-2007, Ext JS, LLC.
17570  *
17571  * Originally Released Under LGPL - original licence link has changed is not relivant.
17572  *
17573  * Fork - LGPL
17574  * <script type="text/javascript">
17575  */
17576  
17577
17578 /**
17579  * @class Roo.form.NumberField
17580  * @extends Roo.form.TextField
17581  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17582  * @constructor
17583  * Creates a new NumberField
17584  * @param {Object} config Configuration options
17585  */
17586 Roo.form.NumberField = function(config){
17587     Roo.form.NumberField.superclass.constructor.call(this, config);
17588 };
17589
17590 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17591     /**
17592      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17593      */
17594     fieldClass: "x-form-field x-form-num-field",
17595     /**
17596      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17597      */
17598     allowDecimals : true,
17599     /**
17600      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17601      */
17602     decimalSeparator : ".",
17603     /**
17604      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17605      */
17606     decimalPrecision : 2,
17607     /**
17608      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17609      */
17610     allowNegative : true,
17611     /**
17612      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17613      */
17614     minValue : Number.NEGATIVE_INFINITY,
17615     /**
17616      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17617      */
17618     maxValue : Number.MAX_VALUE,
17619     /**
17620      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17621      */
17622     minText : "The minimum value for this field is {0}",
17623     /**
17624      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17625      */
17626     maxText : "The maximum value for this field is {0}",
17627     /**
17628      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17629      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17630      */
17631     nanText : "{0} is not a valid number",
17632
17633     // private
17634     initEvents : function(){
17635         Roo.form.NumberField.superclass.initEvents.call(this);
17636         var allowed = "0123456789";
17637         if(this.allowDecimals){
17638             allowed += this.decimalSeparator;
17639         }
17640         if(this.allowNegative){
17641             allowed += "-";
17642         }
17643         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17644         var keyPress = function(e){
17645             var k = e.getKey();
17646             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17647                 return;
17648             }
17649             var c = e.getCharCode();
17650             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17651                 e.stopEvent();
17652             }
17653         };
17654         this.el.on("keypress", keyPress, this);
17655     },
17656
17657     // private
17658     validateValue : function(value){
17659         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17660             return false;
17661         }
17662         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17663              return true;
17664         }
17665         var num = this.parseValue(value);
17666         if(isNaN(num)){
17667             this.markInvalid(String.format(this.nanText, value));
17668             return false;
17669         }
17670         if(num < this.minValue){
17671             this.markInvalid(String.format(this.minText, this.minValue));
17672             return false;
17673         }
17674         if(num > this.maxValue){
17675             this.markInvalid(String.format(this.maxText, this.maxValue));
17676             return false;
17677         }
17678         return true;
17679     },
17680
17681     getValue : function(){
17682         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17683     },
17684
17685     // private
17686     parseValue : function(value){
17687         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17688         return isNaN(value) ? '' : value;
17689     },
17690
17691     // private
17692     fixPrecision : function(value){
17693         var nan = isNaN(value);
17694         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17695             return nan ? '' : value;
17696         }
17697         return parseFloat(value).toFixed(this.decimalPrecision);
17698     },
17699
17700     setValue : function(v){
17701         v = this.fixPrecision(v);
17702         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17703     },
17704
17705     // private
17706     decimalPrecisionFcn : function(v){
17707         return Math.floor(v);
17708     },
17709
17710     beforeBlur : function(){
17711         var v = this.parseValue(this.getRawValue());
17712         if(v){
17713             this.setValue(v);
17714         }
17715     }
17716 });/*
17717  * Based on:
17718  * Ext JS Library 1.1.1
17719  * Copyright(c) 2006-2007, Ext JS, LLC.
17720  *
17721  * Originally Released Under LGPL - original licence link has changed is not relivant.
17722  *
17723  * Fork - LGPL
17724  * <script type="text/javascript">
17725  */
17726  
17727 /**
17728  * @class Roo.form.DateField
17729  * @extends Roo.form.TriggerField
17730  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17731 * @constructor
17732 * Create a new DateField
17733 * @param {Object} config
17734  */
17735 Roo.form.DateField = function(config){
17736     Roo.form.DateField.superclass.constructor.call(this, config);
17737     
17738       this.addEvents({
17739          
17740         /**
17741          * @event select
17742          * Fires when a date is selected
17743              * @param {Roo.form.DateField} combo This combo box
17744              * @param {Date} date The date selected
17745              */
17746         'select' : true
17747          
17748     });
17749     
17750     
17751     if(typeof this.minValue == "string") {
17752         this.minValue = this.parseDate(this.minValue);
17753     }
17754     if(typeof this.maxValue == "string") {
17755         this.maxValue = this.parseDate(this.maxValue);
17756     }
17757     this.ddMatch = null;
17758     if(this.disabledDates){
17759         var dd = this.disabledDates;
17760         var re = "(?:";
17761         for(var i = 0; i < dd.length; i++){
17762             re += dd[i];
17763             if(i != dd.length-1) {
17764                 re += "|";
17765             }
17766         }
17767         this.ddMatch = new RegExp(re + ")");
17768     }
17769 };
17770
17771 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17772     /**
17773      * @cfg {String} format
17774      * The default date format string which can be overriden for localization support.  The format must be
17775      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17776      */
17777     format : "m/d/y",
17778     /**
17779      * @cfg {String} altFormats
17780      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17781      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17782      */
17783     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17784     /**
17785      * @cfg {Array} disabledDays
17786      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17787      */
17788     disabledDays : null,
17789     /**
17790      * @cfg {String} disabledDaysText
17791      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17792      */
17793     disabledDaysText : "Disabled",
17794     /**
17795      * @cfg {Array} disabledDates
17796      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17797      * expression so they are very powerful. Some examples:
17798      * <ul>
17799      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17800      * <li>["03/08", "09/16"] would disable those days for every year</li>
17801      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17802      * <li>["03/../2006"] would disable every day in March 2006</li>
17803      * <li>["^03"] would disable every day in every March</li>
17804      * </ul>
17805      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17806      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17807      */
17808     disabledDates : null,
17809     /**
17810      * @cfg {String} disabledDatesText
17811      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17812      */
17813     disabledDatesText : "Disabled",
17814     /**
17815      * @cfg {Date/String} minValue
17816      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17817      * valid format (defaults to null).
17818      */
17819     minValue : null,
17820     /**
17821      * @cfg {Date/String} maxValue
17822      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17823      * valid format (defaults to null).
17824      */
17825     maxValue : null,
17826     /**
17827      * @cfg {String} minText
17828      * The error text to display when the date in the cell is before minValue (defaults to
17829      * 'The date in this field must be after {minValue}').
17830      */
17831     minText : "The date in this field must be equal to or after {0}",
17832     /**
17833      * @cfg {String} maxText
17834      * The error text to display when the date in the cell is after maxValue (defaults to
17835      * 'The date in this field must be before {maxValue}').
17836      */
17837     maxText : "The date in this field must be equal to or before {0}",
17838     /**
17839      * @cfg {String} invalidText
17840      * The error text to display when the date in the field is invalid (defaults to
17841      * '{value} is not a valid date - it must be in the format {format}').
17842      */
17843     invalidText : "{0} is not a valid date - it must be in the format {1}",
17844     /**
17845      * @cfg {String} triggerClass
17846      * An additional CSS class used to style the trigger button.  The trigger will always get the
17847      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17848      * which displays a calendar icon).
17849      */
17850     triggerClass : 'x-form-date-trigger',
17851     
17852
17853     /**
17854      * @cfg {Boolean} useIso
17855      * if enabled, then the date field will use a hidden field to store the 
17856      * real value as iso formated date. default (false)
17857      */ 
17858     useIso : false,
17859     /**
17860      * @cfg {String/Object} autoCreate
17861      * A DomHelper element spec, or true for a default element spec (defaults to
17862      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17863      */ 
17864     // private
17865     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17866     
17867     // private
17868     hiddenField: false,
17869     
17870     onRender : function(ct, position)
17871     {
17872         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17873         if (this.useIso) {
17874             //this.el.dom.removeAttribute('name'); 
17875             Roo.log("Changing name?");
17876             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17877             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17878                     'before', true);
17879             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17880             // prevent input submission
17881             this.hiddenName = this.name;
17882         }
17883             
17884             
17885     },
17886     
17887     // private
17888     validateValue : function(value)
17889     {
17890         value = this.formatDate(value);
17891         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17892             Roo.log('super failed');
17893             return false;
17894         }
17895         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17896              return true;
17897         }
17898         var svalue = value;
17899         value = this.parseDate(value);
17900         if(!value){
17901             Roo.log('parse date failed' + svalue);
17902             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17903             return false;
17904         }
17905         var time = value.getTime();
17906         if(this.minValue && time < this.minValue.getTime()){
17907             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17908             return false;
17909         }
17910         if(this.maxValue && time > this.maxValue.getTime()){
17911             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17912             return false;
17913         }
17914         if(this.disabledDays){
17915             var day = value.getDay();
17916             for(var i = 0; i < this.disabledDays.length; i++) {
17917                 if(day === this.disabledDays[i]){
17918                     this.markInvalid(this.disabledDaysText);
17919                     return false;
17920                 }
17921             }
17922         }
17923         var fvalue = this.formatDate(value);
17924         if(this.ddMatch && this.ddMatch.test(fvalue)){
17925             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17926             return false;
17927         }
17928         return true;
17929     },
17930
17931     // private
17932     // Provides logic to override the default TriggerField.validateBlur which just returns true
17933     validateBlur : function(){
17934         return !this.menu || !this.menu.isVisible();
17935     },
17936     
17937     getName: function()
17938     {
17939         // returns hidden if it's set..
17940         if (!this.rendered) {return ''};
17941         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17942         
17943     },
17944
17945     /**
17946      * Returns the current date value of the date field.
17947      * @return {Date} The date value
17948      */
17949     getValue : function(){
17950         
17951         return  this.hiddenField ?
17952                 this.hiddenField.value :
17953                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17954     },
17955
17956     /**
17957      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17958      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17959      * (the default format used is "m/d/y").
17960      * <br />Usage:
17961      * <pre><code>
17962 //All of these calls set the same date value (May 4, 2006)
17963
17964 //Pass a date object:
17965 var dt = new Date('5/4/06');
17966 dateField.setValue(dt);
17967
17968 //Pass a date string (default format):
17969 dateField.setValue('5/4/06');
17970
17971 //Pass a date string (custom format):
17972 dateField.format = 'Y-m-d';
17973 dateField.setValue('2006-5-4');
17974 </code></pre>
17975      * @param {String/Date} date The date or valid date string
17976      */
17977     setValue : function(date){
17978         if (this.hiddenField) {
17979             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17980         }
17981         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17982         // make sure the value field is always stored as a date..
17983         this.value = this.parseDate(date);
17984         
17985         
17986     },
17987
17988     // private
17989     parseDate : function(value){
17990         if(!value || value instanceof Date){
17991             return value;
17992         }
17993         var v = Date.parseDate(value, this.format);
17994          if (!v && this.useIso) {
17995             v = Date.parseDate(value, 'Y-m-d');
17996         }
17997         if(!v && this.altFormats){
17998             if(!this.altFormatsArray){
17999                 this.altFormatsArray = this.altFormats.split("|");
18000             }
18001             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18002                 v = Date.parseDate(value, this.altFormatsArray[i]);
18003             }
18004         }
18005         return v;
18006     },
18007
18008     // private
18009     formatDate : function(date, fmt){
18010         return (!date || !(date instanceof Date)) ?
18011                date : date.dateFormat(fmt || this.format);
18012     },
18013
18014     // private
18015     menuListeners : {
18016         select: function(m, d){
18017             
18018             this.setValue(d);
18019             this.fireEvent('select', this, d);
18020         },
18021         show : function(){ // retain focus styling
18022             this.onFocus();
18023         },
18024         hide : function(){
18025             this.focus.defer(10, this);
18026             var ml = this.menuListeners;
18027             this.menu.un("select", ml.select,  this);
18028             this.menu.un("show", ml.show,  this);
18029             this.menu.un("hide", ml.hide,  this);
18030         }
18031     },
18032
18033     // private
18034     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18035     onTriggerClick : function(){
18036         if(this.disabled){
18037             return;
18038         }
18039         if(this.menu == null){
18040             this.menu = new Roo.menu.DateMenu();
18041         }
18042         Roo.apply(this.menu.picker,  {
18043             showClear: this.allowBlank,
18044             minDate : this.minValue,
18045             maxDate : this.maxValue,
18046             disabledDatesRE : this.ddMatch,
18047             disabledDatesText : this.disabledDatesText,
18048             disabledDays : this.disabledDays,
18049             disabledDaysText : this.disabledDaysText,
18050             format : this.useIso ? 'Y-m-d' : this.format,
18051             minText : String.format(this.minText, this.formatDate(this.minValue)),
18052             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18053         });
18054         this.menu.on(Roo.apply({}, this.menuListeners, {
18055             scope:this
18056         }));
18057         this.menu.picker.setValue(this.getValue() || new Date());
18058         this.menu.show(this.el, "tl-bl?");
18059     },
18060
18061     beforeBlur : function(){
18062         var v = this.parseDate(this.getRawValue());
18063         if(v){
18064             this.setValue(v);
18065         }
18066     },
18067
18068     /*@
18069      * overide
18070      * 
18071      */
18072     isDirty : function() {
18073         if(this.disabled) {
18074             return false;
18075         }
18076         
18077         if(typeof(this.startValue) === 'undefined'){
18078             return false;
18079         }
18080         
18081         return String(this.getValue()) !== String(this.startValue);
18082         
18083     }
18084 });/*
18085  * Based on:
18086  * Ext JS Library 1.1.1
18087  * Copyright(c) 2006-2007, Ext JS, LLC.
18088  *
18089  * Originally Released Under LGPL - original licence link has changed is not relivant.
18090  *
18091  * Fork - LGPL
18092  * <script type="text/javascript">
18093  */
18094  
18095 /**
18096  * @class Roo.form.MonthField
18097  * @extends Roo.form.TriggerField
18098  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18099 * @constructor
18100 * Create a new MonthField
18101 * @param {Object} config
18102  */
18103 Roo.form.MonthField = function(config){
18104     
18105     Roo.form.MonthField.superclass.constructor.call(this, config);
18106     
18107       this.addEvents({
18108          
18109         /**
18110          * @event select
18111          * Fires when a date is selected
18112              * @param {Roo.form.MonthFieeld} combo This combo box
18113              * @param {Date} date The date selected
18114              */
18115         'select' : true
18116          
18117     });
18118     
18119     
18120     if(typeof this.minValue == "string") {
18121         this.minValue = this.parseDate(this.minValue);
18122     }
18123     if(typeof this.maxValue == "string") {
18124         this.maxValue = this.parseDate(this.maxValue);
18125     }
18126     this.ddMatch = null;
18127     if(this.disabledDates){
18128         var dd = this.disabledDates;
18129         var re = "(?:";
18130         for(var i = 0; i < dd.length; i++){
18131             re += dd[i];
18132             if(i != dd.length-1) {
18133                 re += "|";
18134             }
18135         }
18136         this.ddMatch = new RegExp(re + ")");
18137     }
18138 };
18139
18140 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18141     /**
18142      * @cfg {String} format
18143      * The default date format string which can be overriden for localization support.  The format must be
18144      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18145      */
18146     format : "M Y",
18147     /**
18148      * @cfg {String} altFormats
18149      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18150      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18151      */
18152     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18153     /**
18154      * @cfg {Array} disabledDays
18155      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18156      */
18157     disabledDays : [0,1,2,3,4,5,6],
18158     /**
18159      * @cfg {String} disabledDaysText
18160      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18161      */
18162     disabledDaysText : "Disabled",
18163     /**
18164      * @cfg {Array} disabledDates
18165      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18166      * expression so they are very powerful. Some examples:
18167      * <ul>
18168      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18169      * <li>["03/08", "09/16"] would disable those days for every year</li>
18170      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18171      * <li>["03/../2006"] would disable every day in March 2006</li>
18172      * <li>["^03"] would disable every day in every March</li>
18173      * </ul>
18174      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18175      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18176      */
18177     disabledDates : null,
18178     /**
18179      * @cfg {String} disabledDatesText
18180      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18181      */
18182     disabledDatesText : "Disabled",
18183     /**
18184      * @cfg {Date/String} minValue
18185      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18186      * valid format (defaults to null).
18187      */
18188     minValue : null,
18189     /**
18190      * @cfg {Date/String} maxValue
18191      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18192      * valid format (defaults to null).
18193      */
18194     maxValue : null,
18195     /**
18196      * @cfg {String} minText
18197      * The error text to display when the date in the cell is before minValue (defaults to
18198      * 'The date in this field must be after {minValue}').
18199      */
18200     minText : "The date in this field must be equal to or after {0}",
18201     /**
18202      * @cfg {String} maxTextf
18203      * The error text to display when the date in the cell is after maxValue (defaults to
18204      * 'The date in this field must be before {maxValue}').
18205      */
18206     maxText : "The date in this field must be equal to or before {0}",
18207     /**
18208      * @cfg {String} invalidText
18209      * The error text to display when the date in the field is invalid (defaults to
18210      * '{value} is not a valid date - it must be in the format {format}').
18211      */
18212     invalidText : "{0} is not a valid date - it must be in the format {1}",
18213     /**
18214      * @cfg {String} triggerClass
18215      * An additional CSS class used to style the trigger button.  The trigger will always get the
18216      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18217      * which displays a calendar icon).
18218      */
18219     triggerClass : 'x-form-date-trigger',
18220     
18221
18222     /**
18223      * @cfg {Boolean} useIso
18224      * if enabled, then the date field will use a hidden field to store the 
18225      * real value as iso formated date. default (true)
18226      */ 
18227     useIso : true,
18228     /**
18229      * @cfg {String/Object} autoCreate
18230      * A DomHelper element spec, or true for a default element spec (defaults to
18231      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18232      */ 
18233     // private
18234     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18235     
18236     // private
18237     hiddenField: false,
18238     
18239     hideMonthPicker : false,
18240     
18241     onRender : function(ct, position)
18242     {
18243         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18244         if (this.useIso) {
18245             this.el.dom.removeAttribute('name'); 
18246             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18247                     'before', true);
18248             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18249             // prevent input submission
18250             this.hiddenName = this.name;
18251         }
18252             
18253             
18254     },
18255     
18256     // private
18257     validateValue : function(value)
18258     {
18259         value = this.formatDate(value);
18260         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18261             return false;
18262         }
18263         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18264              return true;
18265         }
18266         var svalue = value;
18267         value = this.parseDate(value);
18268         if(!value){
18269             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18270             return false;
18271         }
18272         var time = value.getTime();
18273         if(this.minValue && time < this.minValue.getTime()){
18274             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18275             return false;
18276         }
18277         if(this.maxValue && time > this.maxValue.getTime()){
18278             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18279             return false;
18280         }
18281         /*if(this.disabledDays){
18282             var day = value.getDay();
18283             for(var i = 0; i < this.disabledDays.length; i++) {
18284                 if(day === this.disabledDays[i]){
18285                     this.markInvalid(this.disabledDaysText);
18286                     return false;
18287                 }
18288             }
18289         }
18290         */
18291         var fvalue = this.formatDate(value);
18292         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18293             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18294             return false;
18295         }
18296         */
18297         return true;
18298     },
18299
18300     // private
18301     // Provides logic to override the default TriggerField.validateBlur which just returns true
18302     validateBlur : function(){
18303         return !this.menu || !this.menu.isVisible();
18304     },
18305
18306     /**
18307      * Returns the current date value of the date field.
18308      * @return {Date} The date value
18309      */
18310     getValue : function(){
18311         
18312         
18313         
18314         return  this.hiddenField ?
18315                 this.hiddenField.value :
18316                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18317     },
18318
18319     /**
18320      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18321      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18322      * (the default format used is "m/d/y").
18323      * <br />Usage:
18324      * <pre><code>
18325 //All of these calls set the same date value (May 4, 2006)
18326
18327 //Pass a date object:
18328 var dt = new Date('5/4/06');
18329 monthField.setValue(dt);
18330
18331 //Pass a date string (default format):
18332 monthField.setValue('5/4/06');
18333
18334 //Pass a date string (custom format):
18335 monthField.format = 'Y-m-d';
18336 monthField.setValue('2006-5-4');
18337 </code></pre>
18338      * @param {String/Date} date The date or valid date string
18339      */
18340     setValue : function(date){
18341         Roo.log('month setValue' + date);
18342         // can only be first of month..
18343         
18344         var val = this.parseDate(date);
18345         
18346         if (this.hiddenField) {
18347             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18348         }
18349         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18350         this.value = this.parseDate(date);
18351     },
18352
18353     // private
18354     parseDate : function(value){
18355         if(!value || value instanceof Date){
18356             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18357             return value;
18358         }
18359         var v = Date.parseDate(value, this.format);
18360         if (!v && this.useIso) {
18361             v = Date.parseDate(value, 'Y-m-d');
18362         }
18363         if (v) {
18364             // 
18365             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18366         }
18367         
18368         
18369         if(!v && this.altFormats){
18370             if(!this.altFormatsArray){
18371                 this.altFormatsArray = this.altFormats.split("|");
18372             }
18373             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18374                 v = Date.parseDate(value, this.altFormatsArray[i]);
18375             }
18376         }
18377         return v;
18378     },
18379
18380     // private
18381     formatDate : function(date, fmt){
18382         return (!date || !(date instanceof Date)) ?
18383                date : date.dateFormat(fmt || this.format);
18384     },
18385
18386     // private
18387     menuListeners : {
18388         select: function(m, d){
18389             this.setValue(d);
18390             this.fireEvent('select', this, d);
18391         },
18392         show : function(){ // retain focus styling
18393             this.onFocus();
18394         },
18395         hide : function(){
18396             this.focus.defer(10, this);
18397             var ml = this.menuListeners;
18398             this.menu.un("select", ml.select,  this);
18399             this.menu.un("show", ml.show,  this);
18400             this.menu.un("hide", ml.hide,  this);
18401         }
18402     },
18403     // private
18404     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18405     onTriggerClick : function(){
18406         if(this.disabled){
18407             return;
18408         }
18409         if(this.menu == null){
18410             this.menu = new Roo.menu.DateMenu();
18411            
18412         }
18413         
18414         Roo.apply(this.menu.picker,  {
18415             
18416             showClear: this.allowBlank,
18417             minDate : this.minValue,
18418             maxDate : this.maxValue,
18419             disabledDatesRE : this.ddMatch,
18420             disabledDatesText : this.disabledDatesText,
18421             
18422             format : this.useIso ? 'Y-m-d' : this.format,
18423             minText : String.format(this.minText, this.formatDate(this.minValue)),
18424             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18425             
18426         });
18427          this.menu.on(Roo.apply({}, this.menuListeners, {
18428             scope:this
18429         }));
18430        
18431         
18432         var m = this.menu;
18433         var p = m.picker;
18434         
18435         // hide month picker get's called when we called by 'before hide';
18436         
18437         var ignorehide = true;
18438         p.hideMonthPicker  = function(disableAnim){
18439             if (ignorehide) {
18440                 return;
18441             }
18442              if(this.monthPicker){
18443                 Roo.log("hideMonthPicker called");
18444                 if(disableAnim === true){
18445                     this.monthPicker.hide();
18446                 }else{
18447                     this.monthPicker.slideOut('t', {duration:.2});
18448                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18449                     p.fireEvent("select", this, this.value);
18450                     m.hide();
18451                 }
18452             }
18453         }
18454         
18455         Roo.log('picker set value');
18456         Roo.log(this.getValue());
18457         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18458         m.show(this.el, 'tl-bl?');
18459         ignorehide  = false;
18460         // this will trigger hideMonthPicker..
18461         
18462         
18463         // hidden the day picker
18464         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18465         
18466         
18467         
18468       
18469         
18470         p.showMonthPicker.defer(100, p);
18471     
18472         
18473        
18474     },
18475
18476     beforeBlur : function(){
18477         var v = this.parseDate(this.getRawValue());
18478         if(v){
18479             this.setValue(v);
18480         }
18481     }
18482
18483     /** @cfg {Boolean} grow @hide */
18484     /** @cfg {Number} growMin @hide */
18485     /** @cfg {Number} growMax @hide */
18486     /**
18487      * @hide
18488      * @method autoSize
18489      */
18490 });/*
18491  * Based on:
18492  * Ext JS Library 1.1.1
18493  * Copyright(c) 2006-2007, Ext JS, LLC.
18494  *
18495  * Originally Released Under LGPL - original licence link has changed is not relivant.
18496  *
18497  * Fork - LGPL
18498  * <script type="text/javascript">
18499  */
18500  
18501
18502 /**
18503  * @class Roo.form.ComboBox
18504  * @extends Roo.form.TriggerField
18505  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18506  * @constructor
18507  * Create a new ComboBox.
18508  * @param {Object} config Configuration options
18509  */
18510 Roo.form.ComboBox = function(config){
18511     Roo.form.ComboBox.superclass.constructor.call(this, config);
18512     this.addEvents({
18513         /**
18514          * @event expand
18515          * Fires when the dropdown list is expanded
18516              * @param {Roo.form.ComboBox} combo This combo box
18517              */
18518         'expand' : true,
18519         /**
18520          * @event collapse
18521          * Fires when the dropdown list is collapsed
18522              * @param {Roo.form.ComboBox} combo This combo box
18523              */
18524         'collapse' : true,
18525         /**
18526          * @event beforeselect
18527          * Fires before a list item is selected. Return false to cancel the selection.
18528              * @param {Roo.form.ComboBox} combo This combo box
18529              * @param {Roo.data.Record} record The data record returned from the underlying store
18530              * @param {Number} index The index of the selected item in the dropdown list
18531              */
18532         'beforeselect' : true,
18533         /**
18534          * @event select
18535          * Fires when a list item is selected
18536              * @param {Roo.form.ComboBox} combo This combo box
18537              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18538              * @param {Number} index The index of the selected item in the dropdown list
18539              */
18540         'select' : true,
18541         /**
18542          * @event beforequery
18543          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18544          * The event object passed has these properties:
18545              * @param {Roo.form.ComboBox} combo This combo box
18546              * @param {String} query The query
18547              * @param {Boolean} forceAll true to force "all" query
18548              * @param {Boolean} cancel true to cancel the query
18549              * @param {Object} e The query event object
18550              */
18551         'beforequery': true,
18552          /**
18553          * @event add
18554          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18555              * @param {Roo.form.ComboBox} combo This combo box
18556              */
18557         'add' : true,
18558         /**
18559          * @event edit
18560          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18561              * @param {Roo.form.ComboBox} combo This combo box
18562              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18563              */
18564         'edit' : true
18565         
18566         
18567     });
18568     if(this.transform){
18569         this.allowDomMove = false;
18570         var s = Roo.getDom(this.transform);
18571         if(!this.hiddenName){
18572             this.hiddenName = s.name;
18573         }
18574         if(!this.store){
18575             this.mode = 'local';
18576             var d = [], opts = s.options;
18577             for(var i = 0, len = opts.length;i < len; i++){
18578                 var o = opts[i];
18579                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18580                 if(o.selected) {
18581                     this.value = value;
18582                 }
18583                 d.push([value, o.text]);
18584             }
18585             this.store = new Roo.data.SimpleStore({
18586                 'id': 0,
18587                 fields: ['value', 'text'],
18588                 data : d
18589             });
18590             this.valueField = 'value';
18591             this.displayField = 'text';
18592         }
18593         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18594         if(!this.lazyRender){
18595             this.target = true;
18596             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18597             s.parentNode.removeChild(s); // remove it
18598             this.render(this.el.parentNode);
18599         }else{
18600             s.parentNode.removeChild(s); // remove it
18601         }
18602
18603     }
18604     if (this.store) {
18605         this.store = Roo.factory(this.store, Roo.data);
18606     }
18607     
18608     this.selectedIndex = -1;
18609     if(this.mode == 'local'){
18610         if(config.queryDelay === undefined){
18611             this.queryDelay = 10;
18612         }
18613         if(config.minChars === undefined){
18614             this.minChars = 0;
18615         }
18616     }
18617 };
18618
18619 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18620     /**
18621      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18622      */
18623     /**
18624      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18625      * rendering into an Roo.Editor, defaults to false)
18626      */
18627     /**
18628      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18629      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18630      */
18631     /**
18632      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18633      */
18634     /**
18635      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18636      * the dropdown list (defaults to undefined, with no header element)
18637      */
18638
18639      /**
18640      * @cfg {String/Roo.Template} tpl The template to use to render the output
18641      */
18642      
18643     // private
18644     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18645     /**
18646      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18647      */
18648     listWidth: undefined,
18649     /**
18650      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18651      * mode = 'remote' or 'text' if mode = 'local')
18652      */
18653     displayField: undefined,
18654     /**
18655      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18656      * mode = 'remote' or 'value' if mode = 'local'). 
18657      * Note: use of a valueField requires the user make a selection
18658      * in order for a value to be mapped.
18659      */
18660     valueField: undefined,
18661     
18662     
18663     /**
18664      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18665      * field's data value (defaults to the underlying DOM element's name)
18666      */
18667     hiddenName: undefined,
18668     /**
18669      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18670      */
18671     listClass: '',
18672     /**
18673      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18674      */
18675     selectedClass: 'x-combo-selected',
18676     /**
18677      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18678      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18679      * which displays a downward arrow icon).
18680      */
18681     triggerClass : 'x-form-arrow-trigger',
18682     /**
18683      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18684      */
18685     shadow:'sides',
18686     /**
18687      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18688      * anchor positions (defaults to 'tl-bl')
18689      */
18690     listAlign: 'tl-bl?',
18691     /**
18692      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18693      */
18694     maxHeight: 300,
18695     /**
18696      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18697      * query specified by the allQuery config option (defaults to 'query')
18698      */
18699     triggerAction: 'query',
18700     /**
18701      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18702      * (defaults to 4, does not apply if editable = false)
18703      */
18704     minChars : 4,
18705     /**
18706      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18707      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18708      */
18709     typeAhead: false,
18710     /**
18711      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18712      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18713      */
18714     queryDelay: 500,
18715     /**
18716      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18717      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18718      */
18719     pageSize: 0,
18720     /**
18721      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18722      * when editable = true (defaults to false)
18723      */
18724     selectOnFocus:false,
18725     /**
18726      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18727      */
18728     queryParam: 'query',
18729     /**
18730      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18731      * when mode = 'remote' (defaults to 'Loading...')
18732      */
18733     loadingText: 'Loading...',
18734     /**
18735      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18736      */
18737     resizable: false,
18738     /**
18739      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18740      */
18741     handleHeight : 8,
18742     /**
18743      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18744      * traditional select (defaults to true)
18745      */
18746     editable: true,
18747     /**
18748      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18749      */
18750     allQuery: '',
18751     /**
18752      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18753      */
18754     mode: 'remote',
18755     /**
18756      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18757      * listWidth has a higher value)
18758      */
18759     minListWidth : 70,
18760     /**
18761      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18762      * allow the user to set arbitrary text into the field (defaults to false)
18763      */
18764     forceSelection:false,
18765     /**
18766      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18767      * if typeAhead = true (defaults to 250)
18768      */
18769     typeAheadDelay : 250,
18770     /**
18771      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18772      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18773      */
18774     valueNotFoundText : undefined,
18775     /**
18776      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18777      */
18778     blockFocus : false,
18779     
18780     /**
18781      * @cfg {Boolean} disableClear Disable showing of clear button.
18782      */
18783     disableClear : false,
18784     /**
18785      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18786      */
18787     alwaysQuery : false,
18788     
18789     //private
18790     addicon : false,
18791     editicon: false,
18792     
18793     // element that contains real text value.. (when hidden is used..)
18794      
18795     // private
18796     onRender : function(ct, position){
18797         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18798         if(this.hiddenName){
18799             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18800                     'before', true);
18801             this.hiddenField.value =
18802                 this.hiddenValue !== undefined ? this.hiddenValue :
18803                 this.value !== undefined ? this.value : '';
18804
18805             // prevent input submission
18806             this.el.dom.removeAttribute('name');
18807              
18808              
18809         }
18810         if(Roo.isGecko){
18811             this.el.dom.setAttribute('autocomplete', 'off');
18812         }
18813
18814         var cls = 'x-combo-list';
18815
18816         this.list = new Roo.Layer({
18817             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18818         });
18819
18820         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18821         this.list.setWidth(lw);
18822         this.list.swallowEvent('mousewheel');
18823         this.assetHeight = 0;
18824
18825         if(this.title){
18826             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18827             this.assetHeight += this.header.getHeight();
18828         }
18829
18830         this.innerList = this.list.createChild({cls:cls+'-inner'});
18831         this.innerList.on('mouseover', this.onViewOver, this);
18832         this.innerList.on('mousemove', this.onViewMove, this);
18833         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18834         
18835         if(this.allowBlank && !this.pageSize && !this.disableClear){
18836             this.footer = this.list.createChild({cls:cls+'-ft'});
18837             this.pageTb = new Roo.Toolbar(this.footer);
18838            
18839         }
18840         if(this.pageSize){
18841             this.footer = this.list.createChild({cls:cls+'-ft'});
18842             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18843                     {pageSize: this.pageSize});
18844             
18845         }
18846         
18847         if (this.pageTb && this.allowBlank && !this.disableClear) {
18848             var _this = this;
18849             this.pageTb.add(new Roo.Toolbar.Fill(), {
18850                 cls: 'x-btn-icon x-btn-clear',
18851                 text: '&#160;',
18852                 handler: function()
18853                 {
18854                     _this.collapse();
18855                     _this.clearValue();
18856                     _this.onSelect(false, -1);
18857                 }
18858             });
18859         }
18860         if (this.footer) {
18861             this.assetHeight += this.footer.getHeight();
18862         }
18863         
18864
18865         if(!this.tpl){
18866             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18867         }
18868
18869         this.view = new Roo.View(this.innerList, this.tpl, {
18870             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18871         });
18872
18873         this.view.on('click', this.onViewClick, this);
18874
18875         this.store.on('beforeload', this.onBeforeLoad, this);
18876         this.store.on('load', this.onLoad, this);
18877         this.store.on('loadexception', this.onLoadException, this);
18878
18879         if(this.resizable){
18880             this.resizer = new Roo.Resizable(this.list,  {
18881                pinned:true, handles:'se'
18882             });
18883             this.resizer.on('resize', function(r, w, h){
18884                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18885                 this.listWidth = w;
18886                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18887                 this.restrictHeight();
18888             }, this);
18889             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18890         }
18891         if(!this.editable){
18892             this.editable = true;
18893             this.setEditable(false);
18894         }  
18895         
18896         
18897         if (typeof(this.events.add.listeners) != 'undefined') {
18898             
18899             this.addicon = this.wrap.createChild(
18900                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18901        
18902             this.addicon.on('click', function(e) {
18903                 this.fireEvent('add', this);
18904             }, this);
18905         }
18906         if (typeof(this.events.edit.listeners) != 'undefined') {
18907             
18908             this.editicon = this.wrap.createChild(
18909                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18910             if (this.addicon) {
18911                 this.editicon.setStyle('margin-left', '40px');
18912             }
18913             this.editicon.on('click', function(e) {
18914                 
18915                 // we fire even  if inothing is selected..
18916                 this.fireEvent('edit', this, this.lastData );
18917                 
18918             }, this);
18919         }
18920         
18921         
18922         
18923     },
18924
18925     // private
18926     initEvents : function(){
18927         Roo.form.ComboBox.superclass.initEvents.call(this);
18928
18929         this.keyNav = new Roo.KeyNav(this.el, {
18930             "up" : function(e){
18931                 this.inKeyMode = true;
18932                 this.selectPrev();
18933             },
18934
18935             "down" : function(e){
18936                 if(!this.isExpanded()){
18937                     this.onTriggerClick();
18938                 }else{
18939                     this.inKeyMode = true;
18940                     this.selectNext();
18941                 }
18942             },
18943
18944             "enter" : function(e){
18945                 this.onViewClick();
18946                 //return true;
18947             },
18948
18949             "esc" : function(e){
18950                 this.collapse();
18951             },
18952
18953             "tab" : function(e){
18954                 this.onViewClick(false);
18955                 this.fireEvent("specialkey", this, e);
18956                 return true;
18957             },
18958
18959             scope : this,
18960
18961             doRelay : function(foo, bar, hname){
18962                 if(hname == 'down' || this.scope.isExpanded()){
18963                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18964                 }
18965                 return true;
18966             },
18967
18968             forceKeyDown: true
18969         });
18970         this.queryDelay = Math.max(this.queryDelay || 10,
18971                 this.mode == 'local' ? 10 : 250);
18972         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18973         if(this.typeAhead){
18974             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18975         }
18976         if(this.editable !== false){
18977             this.el.on("keyup", this.onKeyUp, this);
18978         }
18979         if(this.forceSelection){
18980             this.on('blur', this.doForce, this);
18981         }
18982     },
18983
18984     onDestroy : function(){
18985         if(this.view){
18986             this.view.setStore(null);
18987             this.view.el.removeAllListeners();
18988             this.view.el.remove();
18989             this.view.purgeListeners();
18990         }
18991         if(this.list){
18992             this.list.destroy();
18993         }
18994         if(this.store){
18995             this.store.un('beforeload', this.onBeforeLoad, this);
18996             this.store.un('load', this.onLoad, this);
18997             this.store.un('loadexception', this.onLoadException, this);
18998         }
18999         Roo.form.ComboBox.superclass.onDestroy.call(this);
19000     },
19001
19002     // private
19003     fireKey : function(e){
19004         if(e.isNavKeyPress() && !this.list.isVisible()){
19005             this.fireEvent("specialkey", this, e);
19006         }
19007     },
19008
19009     // private
19010     onResize: function(w, h){
19011         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19012         
19013         if(typeof w != 'number'){
19014             // we do not handle it!?!?
19015             return;
19016         }
19017         var tw = this.trigger.getWidth();
19018         tw += this.addicon ? this.addicon.getWidth() : 0;
19019         tw += this.editicon ? this.editicon.getWidth() : 0;
19020         var x = w - tw;
19021         this.el.setWidth( this.adjustWidth('input', x));
19022             
19023         this.trigger.setStyle('left', x+'px');
19024         
19025         if(this.list && this.listWidth === undefined){
19026             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19027             this.list.setWidth(lw);
19028             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19029         }
19030         
19031     
19032         
19033     },
19034
19035     /**
19036      * Allow or prevent the user from directly editing the field text.  If false is passed,
19037      * the user will only be able to select from the items defined in the dropdown list.  This method
19038      * is the runtime equivalent of setting the 'editable' config option at config time.
19039      * @param {Boolean} value True to allow the user to directly edit the field text
19040      */
19041     setEditable : function(value){
19042         if(value == this.editable){
19043             return;
19044         }
19045         this.editable = value;
19046         if(!value){
19047             this.el.dom.setAttribute('readOnly', true);
19048             this.el.on('mousedown', this.onTriggerClick,  this);
19049             this.el.addClass('x-combo-noedit');
19050         }else{
19051             this.el.dom.setAttribute('readOnly', false);
19052             this.el.un('mousedown', this.onTriggerClick,  this);
19053             this.el.removeClass('x-combo-noedit');
19054         }
19055     },
19056
19057     // private
19058     onBeforeLoad : function(){
19059         if(!this.hasFocus){
19060             return;
19061         }
19062         this.innerList.update(this.loadingText ?
19063                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19064         this.restrictHeight();
19065         this.selectedIndex = -1;
19066     },
19067
19068     // private
19069     onLoad : function(){
19070         if(!this.hasFocus){
19071             return;
19072         }
19073         if(this.store.getCount() > 0){
19074             this.expand();
19075             this.restrictHeight();
19076             if(this.lastQuery == this.allQuery){
19077                 if(this.editable){
19078                     this.el.dom.select();
19079                 }
19080                 if(!this.selectByValue(this.value, true)){
19081                     this.select(0, true);
19082                 }
19083             }else{
19084                 this.selectNext();
19085                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19086                     this.taTask.delay(this.typeAheadDelay);
19087                 }
19088             }
19089         }else{
19090             this.onEmptyResults();
19091         }
19092         //this.el.focus();
19093     },
19094     // private
19095     onLoadException : function()
19096     {
19097         this.collapse();
19098         Roo.log(this.store.reader.jsonData);
19099         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19100             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19101         }
19102         
19103         
19104     },
19105     // private
19106     onTypeAhead : function(){
19107         if(this.store.getCount() > 0){
19108             var r = this.store.getAt(0);
19109             var newValue = r.data[this.displayField];
19110             var len = newValue.length;
19111             var selStart = this.getRawValue().length;
19112             if(selStart != len){
19113                 this.setRawValue(newValue);
19114                 this.selectText(selStart, newValue.length);
19115             }
19116         }
19117     },
19118
19119     // private
19120     onSelect : function(record, index){
19121         if(this.fireEvent('beforeselect', this, record, index) !== false){
19122             this.setFromData(index > -1 ? record.data : false);
19123             this.collapse();
19124             this.fireEvent('select', this, record, index);
19125         }
19126     },
19127
19128     /**
19129      * Returns the currently selected field value or empty string if no value is set.
19130      * @return {String} value The selected value
19131      */
19132     getValue : function(){
19133         if(this.valueField){
19134             return typeof this.value != 'undefined' ? this.value : '';
19135         }
19136         return Roo.form.ComboBox.superclass.getValue.call(this);
19137     },
19138
19139     /**
19140      * Clears any text/value currently set in the field
19141      */
19142     clearValue : function(){
19143         if(this.hiddenField){
19144             this.hiddenField.value = '';
19145         }
19146         this.value = '';
19147         this.setRawValue('');
19148         this.lastSelectionText = '';
19149         
19150     },
19151
19152     /**
19153      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19154      * will be displayed in the field.  If the value does not match the data value of an existing item,
19155      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19156      * Otherwise the field will be blank (although the value will still be set).
19157      * @param {String} value The value to match
19158      */
19159     setValue : function(v){
19160         var text = v;
19161         if(this.valueField){
19162             var r = this.findRecord(this.valueField, v);
19163             if(r){
19164                 text = r.data[this.displayField];
19165             }else if(this.valueNotFoundText !== undefined){
19166                 text = this.valueNotFoundText;
19167             }
19168         }
19169         this.lastSelectionText = text;
19170         if(this.hiddenField){
19171             this.hiddenField.value = v;
19172         }
19173         Roo.form.ComboBox.superclass.setValue.call(this, text);
19174         this.value = v;
19175     },
19176     /**
19177      * @property {Object} the last set data for the element
19178      */
19179     
19180     lastData : false,
19181     /**
19182      * Sets the value of the field based on a object which is related to the record format for the store.
19183      * @param {Object} value the value to set as. or false on reset?
19184      */
19185     setFromData : function(o){
19186         var dv = ''; // display value
19187         var vv = ''; // value value..
19188         this.lastData = o;
19189         if (this.displayField) {
19190             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19191         } else {
19192             // this is an error condition!!!
19193             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19194         }
19195         
19196         if(this.valueField){
19197             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19198         }
19199         if(this.hiddenField){
19200             this.hiddenField.value = vv;
19201             
19202             this.lastSelectionText = dv;
19203             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19204             this.value = vv;
19205             return;
19206         }
19207         // no hidden field.. - we store the value in 'value', but still display
19208         // display field!!!!
19209         this.lastSelectionText = dv;
19210         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19211         this.value = vv;
19212         
19213         
19214     },
19215     // private
19216     reset : function(){
19217         // overridden so that last data is reset..
19218         this.setValue(this.resetValue);
19219         this.clearInvalid();
19220         this.lastData = false;
19221         if (this.view) {
19222             this.view.clearSelections();
19223         }
19224     },
19225     // private
19226     findRecord : function(prop, value){
19227         var record;
19228         if(this.store.getCount() > 0){
19229             this.store.each(function(r){
19230                 if(r.data[prop] == value){
19231                     record = r;
19232                     return false;
19233                 }
19234                 return true;
19235             });
19236         }
19237         return record;
19238     },
19239     
19240     getName: function()
19241     {
19242         // returns hidden if it's set..
19243         if (!this.rendered) {return ''};
19244         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19245         
19246     },
19247     // private
19248     onViewMove : function(e, t){
19249         this.inKeyMode = false;
19250     },
19251
19252     // private
19253     onViewOver : function(e, t){
19254         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19255             return;
19256         }
19257         var item = this.view.findItemFromChild(t);
19258         if(item){
19259             var index = this.view.indexOf(item);
19260             this.select(index, false);
19261         }
19262     },
19263
19264     // private
19265     onViewClick : function(doFocus)
19266     {
19267         var index = this.view.getSelectedIndexes()[0];
19268         var r = this.store.getAt(index);
19269         if(r){
19270             this.onSelect(r, index);
19271         }
19272         if(doFocus !== false && !this.blockFocus){
19273             this.el.focus();
19274         }
19275     },
19276
19277     // private
19278     restrictHeight : function(){
19279         this.innerList.dom.style.height = '';
19280         var inner = this.innerList.dom;
19281         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19282         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19283         this.list.beginUpdate();
19284         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19285         this.list.alignTo(this.el, this.listAlign);
19286         this.list.endUpdate();
19287     },
19288
19289     // private
19290     onEmptyResults : function(){
19291         this.collapse();
19292     },
19293
19294     /**
19295      * Returns true if the dropdown list is expanded, else false.
19296      */
19297     isExpanded : function(){
19298         return this.list.isVisible();
19299     },
19300
19301     /**
19302      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19303      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19304      * @param {String} value The data value of the item to select
19305      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19306      * selected item if it is not currently in view (defaults to true)
19307      * @return {Boolean} True if the value matched an item in the list, else false
19308      */
19309     selectByValue : function(v, scrollIntoView){
19310         if(v !== undefined && v !== null){
19311             var r = this.findRecord(this.valueField || this.displayField, v);
19312             if(r){
19313                 this.select(this.store.indexOf(r), scrollIntoView);
19314                 return true;
19315             }
19316         }
19317         return false;
19318     },
19319
19320     /**
19321      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19322      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19323      * @param {Number} index The zero-based index of the list item to select
19324      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19325      * selected item if it is not currently in view (defaults to true)
19326      */
19327     select : function(index, scrollIntoView){
19328         this.selectedIndex = index;
19329         this.view.select(index);
19330         if(scrollIntoView !== false){
19331             var el = this.view.getNode(index);
19332             if(el){
19333                 this.innerList.scrollChildIntoView(el, false);
19334             }
19335         }
19336     },
19337
19338     // private
19339     selectNext : function(){
19340         var ct = this.store.getCount();
19341         if(ct > 0){
19342             if(this.selectedIndex == -1){
19343                 this.select(0);
19344             }else if(this.selectedIndex < ct-1){
19345                 this.select(this.selectedIndex+1);
19346             }
19347         }
19348     },
19349
19350     // private
19351     selectPrev : function(){
19352         var ct = this.store.getCount();
19353         if(ct > 0){
19354             if(this.selectedIndex == -1){
19355                 this.select(0);
19356             }else if(this.selectedIndex != 0){
19357                 this.select(this.selectedIndex-1);
19358             }
19359         }
19360     },
19361
19362     // private
19363     onKeyUp : function(e){
19364         if(this.editable !== false && !e.isSpecialKey()){
19365             this.lastKey = e.getKey();
19366             this.dqTask.delay(this.queryDelay);
19367         }
19368     },
19369
19370     // private
19371     validateBlur : function(){
19372         return !this.list || !this.list.isVisible();   
19373     },
19374
19375     // private
19376     initQuery : function(){
19377         this.doQuery(this.getRawValue());
19378     },
19379
19380     // private
19381     doForce : function(){
19382         if(this.el.dom.value.length > 0){
19383             this.el.dom.value =
19384                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19385              
19386         }
19387     },
19388
19389     /**
19390      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19391      * query allowing the query action to be canceled if needed.
19392      * @param {String} query The SQL query to execute
19393      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19394      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19395      * saved in the current store (defaults to false)
19396      */
19397     doQuery : function(q, forceAll){
19398         if(q === undefined || q === null){
19399             q = '';
19400         }
19401         var qe = {
19402             query: q,
19403             forceAll: forceAll,
19404             combo: this,
19405             cancel:false
19406         };
19407         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19408             return false;
19409         }
19410         q = qe.query;
19411         forceAll = qe.forceAll;
19412         if(forceAll === true || (q.length >= this.minChars)){
19413             if(this.lastQuery != q || this.alwaysQuery){
19414                 this.lastQuery = q;
19415                 if(this.mode == 'local'){
19416                     this.selectedIndex = -1;
19417                     if(forceAll){
19418                         this.store.clearFilter();
19419                     }else{
19420                         this.store.filter(this.displayField, q);
19421                     }
19422                     this.onLoad();
19423                 }else{
19424                     this.store.baseParams[this.queryParam] = q;
19425                     this.store.load({
19426                         params: this.getParams(q)
19427                     });
19428                     this.expand();
19429                 }
19430             }else{
19431                 this.selectedIndex = -1;
19432                 this.onLoad();   
19433             }
19434         }
19435     },
19436
19437     // private
19438     getParams : function(q){
19439         var p = {};
19440         //p[this.queryParam] = q;
19441         if(this.pageSize){
19442             p.start = 0;
19443             p.limit = this.pageSize;
19444         }
19445         return p;
19446     },
19447
19448     /**
19449      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19450      */
19451     collapse : function(){
19452         if(!this.isExpanded()){
19453             return;
19454         }
19455         this.list.hide();
19456         Roo.get(document).un('mousedown', this.collapseIf, this);
19457         Roo.get(document).un('mousewheel', this.collapseIf, this);
19458         if (!this.editable) {
19459             Roo.get(document).un('keydown', this.listKeyPress, this);
19460         }
19461         this.fireEvent('collapse', this);
19462     },
19463
19464     // private
19465     collapseIf : function(e){
19466         if(!e.within(this.wrap) && !e.within(this.list)){
19467             this.collapse();
19468         }
19469     },
19470
19471     /**
19472      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19473      */
19474     expand : function(){
19475         if(this.isExpanded() || !this.hasFocus){
19476             return;
19477         }
19478         this.list.alignTo(this.el, this.listAlign);
19479         this.list.show();
19480         Roo.get(document).on('mousedown', this.collapseIf, this);
19481         Roo.get(document).on('mousewheel', this.collapseIf, this);
19482         if (!this.editable) {
19483             Roo.get(document).on('keydown', this.listKeyPress, this);
19484         }
19485         
19486         this.fireEvent('expand', this);
19487     },
19488
19489     // private
19490     // Implements the default empty TriggerField.onTriggerClick function
19491     onTriggerClick : function(){
19492         if(this.disabled){
19493             return;
19494         }
19495         if(this.isExpanded()){
19496             this.collapse();
19497             if (!this.blockFocus) {
19498                 this.el.focus();
19499             }
19500             
19501         }else {
19502             this.hasFocus = true;
19503             if(this.triggerAction == 'all') {
19504                 this.doQuery(this.allQuery, true);
19505             } else {
19506                 this.doQuery(this.getRawValue());
19507             }
19508             if (!this.blockFocus) {
19509                 this.el.focus();
19510             }
19511         }
19512     },
19513     listKeyPress : function(e)
19514     {
19515         //Roo.log('listkeypress');
19516         // scroll to first matching element based on key pres..
19517         if (e.isSpecialKey()) {
19518             return false;
19519         }
19520         var k = String.fromCharCode(e.getKey()).toUpperCase();
19521         //Roo.log(k);
19522         var match  = false;
19523         var csel = this.view.getSelectedNodes();
19524         var cselitem = false;
19525         if (csel.length) {
19526             var ix = this.view.indexOf(csel[0]);
19527             cselitem  = this.store.getAt(ix);
19528             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19529                 cselitem = false;
19530             }
19531             
19532         }
19533         
19534         this.store.each(function(v) { 
19535             if (cselitem) {
19536                 // start at existing selection.
19537                 if (cselitem.id == v.id) {
19538                     cselitem = false;
19539                 }
19540                 return;
19541             }
19542                 
19543             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19544                 match = this.store.indexOf(v);
19545                 return false;
19546             }
19547         }, this);
19548         
19549         if (match === false) {
19550             return true; // no more action?
19551         }
19552         // scroll to?
19553         this.view.select(match);
19554         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19555         sn.scrollIntoView(sn.dom.parentNode, false);
19556     }
19557
19558     /** 
19559     * @cfg {Boolean} grow 
19560     * @hide 
19561     */
19562     /** 
19563     * @cfg {Number} growMin 
19564     * @hide 
19565     */
19566     /** 
19567     * @cfg {Number} growMax 
19568     * @hide 
19569     */
19570     /**
19571      * @hide
19572      * @method autoSize
19573      */
19574 });/*
19575  * Copyright(c) 2010-2012, Roo J Solutions Limited
19576  *
19577  * Licence LGPL
19578  *
19579  */
19580
19581 /**
19582  * @class Roo.form.ComboBoxArray
19583  * @extends Roo.form.TextField
19584  * A facebook style adder... for lists of email / people / countries  etc...
19585  * pick multiple items from a combo box, and shows each one.
19586  *
19587  *  Fred [x]  Brian [x]  [Pick another |v]
19588  *
19589  *
19590  *  For this to work: it needs various extra information
19591  *    - normal combo problay has
19592  *      name, hiddenName
19593  *    + displayField, valueField
19594  *
19595  *    For our purpose...
19596  *
19597  *
19598  *   If we change from 'extends' to wrapping...
19599  *   
19600  *  
19601  *
19602  
19603  
19604  * @constructor
19605  * Create a new ComboBoxArray.
19606  * @param {Object} config Configuration options
19607  */
19608  
19609
19610 Roo.form.ComboBoxArray = function(config)
19611 {
19612     this.addEvents({
19613         /**
19614          * @event beforeremove
19615          * Fires before remove the value from the list
19616              * @param {Roo.form.ComboBoxArray} _self This combo box array
19617              * @param {Roo.form.ComboBoxArray.Item} item removed item
19618              */
19619         'beforeremove' : true,
19620         /**
19621          * @event remove
19622          * Fires when remove the value from the list
19623              * @param {Roo.form.ComboBoxArray} _self This combo box array
19624              * @param {Roo.form.ComboBoxArray.Item} item removed item
19625              */
19626         'remove' : true
19627         
19628         
19629     });
19630     
19631     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19632     
19633     this.items = new Roo.util.MixedCollection(false);
19634     
19635     // construct the child combo...
19636     
19637     
19638     
19639     
19640    
19641     
19642 }
19643
19644  
19645 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19646
19647     /**
19648      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19649      */
19650     
19651     lastData : false,
19652     
19653     // behavies liek a hiddne field
19654     inputType:      'hidden',
19655     /**
19656      * @cfg {Number} width The width of the box that displays the selected element
19657      */ 
19658     width:          300,
19659
19660     
19661     
19662     /**
19663      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19664      */
19665     name : false,
19666     /**
19667      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19668      */
19669     hiddenName : false,
19670     
19671     
19672     // private the array of items that are displayed..
19673     items  : false,
19674     // private - the hidden field el.
19675     hiddenEl : false,
19676     // private - the filed el..
19677     el : false,
19678     
19679     //validateValue : function() { return true; }, // all values are ok!
19680     //onAddClick: function() { },
19681     
19682     onRender : function(ct, position) 
19683     {
19684         
19685         // create the standard hidden element
19686         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19687         
19688         
19689         // give fake names to child combo;
19690         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19691         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19692         
19693         this.combo = Roo.factory(this.combo, Roo.form);
19694         this.combo.onRender(ct, position);
19695         if (typeof(this.combo.width) != 'undefined') {
19696             this.combo.onResize(this.combo.width,0);
19697         }
19698         
19699         this.combo.initEvents();
19700         
19701         // assigned so form know we need to do this..
19702         this.store          = this.combo.store;
19703         this.valueField     = this.combo.valueField;
19704         this.displayField   = this.combo.displayField ;
19705         
19706         
19707         this.combo.wrap.addClass('x-cbarray-grp');
19708         
19709         var cbwrap = this.combo.wrap.createChild(
19710             {tag: 'div', cls: 'x-cbarray-cb'},
19711             this.combo.el.dom
19712         );
19713         
19714              
19715         this.hiddenEl = this.combo.wrap.createChild({
19716             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19717         });
19718         this.el = this.combo.wrap.createChild({
19719             tag: 'input',  type:'hidden' , name: this.name, value : ''
19720         });
19721          //   this.el.dom.removeAttribute("name");
19722         
19723         
19724         this.outerWrap = this.combo.wrap;
19725         this.wrap = cbwrap;
19726         
19727         this.outerWrap.setWidth(this.width);
19728         this.outerWrap.dom.removeChild(this.el.dom);
19729         
19730         this.wrap.dom.appendChild(this.el.dom);
19731         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19732         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19733         
19734         this.combo.trigger.setStyle('position','relative');
19735         this.combo.trigger.setStyle('left', '0px');
19736         this.combo.trigger.setStyle('top', '2px');
19737         
19738         this.combo.el.setStyle('vertical-align', 'text-bottom');
19739         
19740         //this.trigger.setStyle('vertical-align', 'top');
19741         
19742         // this should use the code from combo really... on('add' ....)
19743         if (this.adder) {
19744             
19745         
19746             this.adder = this.outerWrap.createChild(
19747                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19748             var _t = this;
19749             this.adder.on('click', function(e) {
19750                 _t.fireEvent('adderclick', this, e);
19751             }, _t);
19752         }
19753         //var _t = this;
19754         //this.adder.on('click', this.onAddClick, _t);
19755         
19756         
19757         this.combo.on('select', function(cb, rec, ix) {
19758             this.addItem(rec.data);
19759             
19760             cb.setValue('');
19761             cb.el.dom.value = '';
19762             //cb.lastData = rec.data;
19763             // add to list
19764             
19765         }, this);
19766         
19767         
19768     },
19769     
19770     
19771     getName: function()
19772     {
19773         // returns hidden if it's set..
19774         if (!this.rendered) {return ''};
19775         return  this.hiddenName ? this.hiddenName : this.name;
19776         
19777     },
19778     
19779     
19780     onResize: function(w, h){
19781         
19782         return;
19783         // not sure if this is needed..
19784         //this.combo.onResize(w,h);
19785         
19786         if(typeof w != 'number'){
19787             // we do not handle it!?!?
19788             return;
19789         }
19790         var tw = this.combo.trigger.getWidth();
19791         tw += this.addicon ? this.addicon.getWidth() : 0;
19792         tw += this.editicon ? this.editicon.getWidth() : 0;
19793         var x = w - tw;
19794         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19795             
19796         this.combo.trigger.setStyle('left', '0px');
19797         
19798         if(this.list && this.listWidth === undefined){
19799             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19800             this.list.setWidth(lw);
19801             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19802         }
19803         
19804     
19805         
19806     },
19807     
19808     addItem: function(rec)
19809     {
19810         var valueField = this.combo.valueField;
19811         var displayField = this.combo.displayField;
19812         if (this.items.indexOfKey(rec[valueField]) > -1) {
19813             //console.log("GOT " + rec.data.id);
19814             return;
19815         }
19816         
19817         var x = new Roo.form.ComboBoxArray.Item({
19818             //id : rec[this.idField],
19819             data : rec,
19820             displayField : displayField ,
19821             tipField : displayField ,
19822             cb : this
19823         });
19824         // use the 
19825         this.items.add(rec[valueField],x);
19826         // add it before the element..
19827         this.updateHiddenEl();
19828         x.render(this.outerWrap, this.wrap.dom);
19829         // add the image handler..
19830     },
19831     
19832     updateHiddenEl : function()
19833     {
19834         this.validate();
19835         if (!this.hiddenEl) {
19836             return;
19837         }
19838         var ar = [];
19839         var idField = this.combo.valueField;
19840         
19841         this.items.each(function(f) {
19842             ar.push(f.data[idField]);
19843            
19844         });
19845         this.hiddenEl.dom.value = ar.join(',');
19846         this.validate();
19847     },
19848     
19849     reset : function()
19850     {
19851         this.items.clear();
19852         
19853         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19854            el.remove();
19855         });
19856         
19857         this.el.dom.value = '';
19858         if (this.hiddenEl) {
19859             this.hiddenEl.dom.value = '';
19860         }
19861         
19862     },
19863     getValue: function()
19864     {
19865         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19866     },
19867     setValue: function(v) // not a valid action - must use addItems..
19868     {
19869          
19870         this.reset();
19871         
19872         
19873         
19874         if (this.store.isLocal && (typeof(v) == 'string')) {
19875             // then we can use the store to find the values..
19876             // comma seperated at present.. this needs to allow JSON based encoding..
19877             this.hiddenEl.value  = v;
19878             var v_ar = [];
19879             Roo.each(v.split(','), function(k) {
19880                 Roo.log("CHECK " + this.valueField + ',' + k);
19881                 var li = this.store.query(this.valueField, k);
19882                 if (!li.length) {
19883                     return;
19884                 }
19885                 var add = {};
19886                 add[this.valueField] = k;
19887                 add[this.displayField] = li.item(0).data[this.displayField];
19888                 
19889                 this.addItem(add);
19890             }, this) 
19891              
19892         }
19893         if (typeof(v) == 'object' ) {
19894             // then let's assume it's an array of objects..
19895             Roo.each(v, function(l) {
19896                 this.addItem(l);
19897             }, this);
19898              
19899         }
19900         
19901         
19902     },
19903     setFromData: function(v)
19904     {
19905         // this recieves an object, if setValues is called.
19906         this.reset();
19907         this.el.dom.value = v[this.displayField];
19908         this.hiddenEl.dom.value = v[this.valueField];
19909         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19910             return;
19911         }
19912         var kv = v[this.valueField];
19913         var dv = v[this.displayField];
19914         kv = typeof(kv) != 'string' ? '' : kv;
19915         dv = typeof(dv) != 'string' ? '' : dv;
19916         
19917         
19918         var keys = kv.split(',');
19919         var display = dv.split(',');
19920         for (var i = 0 ; i < keys.length; i++) {
19921             
19922             add = {};
19923             add[this.valueField] = keys[i];
19924             add[this.displayField] = display[i];
19925             this.addItem(add);
19926         }
19927       
19928         
19929     },
19930     
19931     /**
19932      * Validates the combox array value
19933      * @return {Boolean} True if the value is valid, else false
19934      */
19935     validate : function(){
19936         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19937             this.clearInvalid();
19938             return true;
19939         }
19940         return false;
19941     },
19942     
19943     validateValue : function(value){
19944         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19945         
19946     },
19947     
19948     /*@
19949      * overide
19950      * 
19951      */
19952     isDirty : function() {
19953         if(this.disabled) {
19954             return false;
19955         }
19956         
19957         try {
19958             var d = Roo.decode(String(this.originalValue));
19959         } catch (e) {
19960             return String(this.getValue()) !== String(this.originalValue);
19961         }
19962         
19963         var originalValue = [];
19964         
19965         for (var i = 0; i < d.length; i++){
19966             originalValue.push(d[i][this.valueField]);
19967         }
19968         
19969         return String(this.getValue()) !== String(originalValue.join(','));
19970         
19971     }
19972     
19973 });
19974
19975
19976
19977 /**
19978  * @class Roo.form.ComboBoxArray.Item
19979  * @extends Roo.BoxComponent
19980  * A selected item in the list
19981  *  Fred [x]  Brian [x]  [Pick another |v]
19982  * 
19983  * @constructor
19984  * Create a new item.
19985  * @param {Object} config Configuration options
19986  */
19987  
19988 Roo.form.ComboBoxArray.Item = function(config) {
19989     config.id = Roo.id();
19990     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19991 }
19992
19993 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19994     data : {},
19995     cb: false,
19996     displayField : false,
19997     tipField : false,
19998     
19999     
20000     defaultAutoCreate : {
20001         tag: 'div',
20002         cls: 'x-cbarray-item',
20003         cn : [ 
20004             { tag: 'div' },
20005             {
20006                 tag: 'img',
20007                 width:16,
20008                 height : 16,
20009                 src : Roo.BLANK_IMAGE_URL ,
20010                 align: 'center'
20011             }
20012         ]
20013         
20014     },
20015     
20016  
20017     onRender : function(ct, position)
20018     {
20019         Roo.form.Field.superclass.onRender.call(this, ct, position);
20020         
20021         if(!this.el){
20022             var cfg = this.getAutoCreate();
20023             this.el = ct.createChild(cfg, position);
20024         }
20025         
20026         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20027         
20028         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20029             this.cb.renderer(this.data) :
20030             String.format('{0}',this.data[this.displayField]);
20031         
20032             
20033         this.el.child('div').dom.setAttribute('qtip',
20034                         String.format('{0}',this.data[this.tipField])
20035         );
20036         
20037         this.el.child('img').on('click', this.remove, this);
20038         
20039     },
20040    
20041     remove : function()
20042     {
20043         if(this.cb.disabled){
20044             return;
20045         }
20046         
20047         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20048             this.cb.items.remove(this);
20049             this.el.child('img').un('click', this.remove, this);
20050             this.el.remove();
20051             this.cb.updateHiddenEl();
20052
20053             this.cb.fireEvent('remove', this.cb, this);
20054         }
20055         
20056     }
20057 });/*
20058  * Based on:
20059  * Ext JS Library 1.1.1
20060  * Copyright(c) 2006-2007, Ext JS, LLC.
20061  *
20062  * Originally Released Under LGPL - original licence link has changed is not relivant.
20063  *
20064  * Fork - LGPL
20065  * <script type="text/javascript">
20066  */
20067 /**
20068  * @class Roo.form.Checkbox
20069  * @extends Roo.form.Field
20070  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20071  * @constructor
20072  * Creates a new Checkbox
20073  * @param {Object} config Configuration options
20074  */
20075 Roo.form.Checkbox = function(config){
20076     Roo.form.Checkbox.superclass.constructor.call(this, config);
20077     this.addEvents({
20078         /**
20079          * @event check
20080          * Fires when the checkbox is checked or unchecked.
20081              * @param {Roo.form.Checkbox} this This checkbox
20082              * @param {Boolean} checked The new checked value
20083              */
20084         check : true
20085     });
20086 };
20087
20088 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20089     /**
20090      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20091      */
20092     focusClass : undefined,
20093     /**
20094      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20095      */
20096     fieldClass: "x-form-field",
20097     /**
20098      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20099      */
20100     checked: false,
20101     /**
20102      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20103      * {tag: "input", type: "checkbox", autocomplete: "off"})
20104      */
20105     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20106     /**
20107      * @cfg {String} boxLabel The text that appears beside the checkbox
20108      */
20109     boxLabel : "",
20110     /**
20111      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20112      */  
20113     inputValue : '1',
20114     /**
20115      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20116      */
20117      valueOff: '0', // value when not checked..
20118
20119     actionMode : 'viewEl', 
20120     //
20121     // private
20122     itemCls : 'x-menu-check-item x-form-item',
20123     groupClass : 'x-menu-group-item',
20124     inputType : 'hidden',
20125     
20126     
20127     inSetChecked: false, // check that we are not calling self...
20128     
20129     inputElement: false, // real input element?
20130     basedOn: false, // ????
20131     
20132     isFormField: true, // not sure where this is needed!!!!
20133
20134     onResize : function(){
20135         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20136         if(!this.boxLabel){
20137             this.el.alignTo(this.wrap, 'c-c');
20138         }
20139     },
20140
20141     initEvents : function(){
20142         Roo.form.Checkbox.superclass.initEvents.call(this);
20143         this.el.on("click", this.onClick,  this);
20144         this.el.on("change", this.onClick,  this);
20145     },
20146
20147
20148     getResizeEl : function(){
20149         return this.wrap;
20150     },
20151
20152     getPositionEl : function(){
20153         return this.wrap;
20154     },
20155
20156     // private
20157     onRender : function(ct, position){
20158         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20159         /*
20160         if(this.inputValue !== undefined){
20161             this.el.dom.value = this.inputValue;
20162         }
20163         */
20164         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20165         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20166         var viewEl = this.wrap.createChild({ 
20167             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20168         this.viewEl = viewEl;   
20169         this.wrap.on('click', this.onClick,  this); 
20170         
20171         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20172         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20173         
20174         
20175         
20176         if(this.boxLabel){
20177             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20178         //    viewEl.on('click', this.onClick,  this); 
20179         }
20180         //if(this.checked){
20181             this.setChecked(this.checked);
20182         //}else{
20183             //this.checked = this.el.dom;
20184         //}
20185
20186     },
20187
20188     // private
20189     initValue : Roo.emptyFn,
20190
20191     /**
20192      * Returns the checked state of the checkbox.
20193      * @return {Boolean} True if checked, else false
20194      */
20195     getValue : function(){
20196         if(this.el){
20197             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20198         }
20199         return this.valueOff;
20200         
20201     },
20202
20203         // private
20204     onClick : function(){ 
20205         if (this.disabled) {
20206             return;
20207         }
20208         this.setChecked(!this.checked);
20209
20210         //if(this.el.dom.checked != this.checked){
20211         //    this.setValue(this.el.dom.checked);
20212        // }
20213     },
20214
20215     /**
20216      * Sets the checked state of the checkbox.
20217      * On is always based on a string comparison between inputValue and the param.
20218      * @param {Boolean/String} value - the value to set 
20219      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20220      */
20221     setValue : function(v,suppressEvent){
20222         
20223         
20224         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20225         //if(this.el && this.el.dom){
20226         //    this.el.dom.checked = this.checked;
20227         //    this.el.dom.defaultChecked = this.checked;
20228         //}
20229         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20230         //this.fireEvent("check", this, this.checked);
20231     },
20232     // private..
20233     setChecked : function(state,suppressEvent)
20234     {
20235         if (this.inSetChecked) {
20236             this.checked = state;
20237             return;
20238         }
20239         
20240     
20241         if(this.wrap){
20242             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20243         }
20244         this.checked = state;
20245         if(suppressEvent !== true){
20246             this.fireEvent('check', this, state);
20247         }
20248         this.inSetChecked = true;
20249         this.el.dom.value = state ? this.inputValue : this.valueOff;
20250         this.inSetChecked = false;
20251         
20252     },
20253     // handle setting of hidden value by some other method!!?!?
20254     setFromHidden: function()
20255     {
20256         if(!this.el){
20257             return;
20258         }
20259         //console.log("SET FROM HIDDEN");
20260         //alert('setFrom hidden');
20261         this.setValue(this.el.dom.value);
20262     },
20263     
20264     onDestroy : function()
20265     {
20266         if(this.viewEl){
20267             Roo.get(this.viewEl).remove();
20268         }
20269          
20270         Roo.form.Checkbox.superclass.onDestroy.call(this);
20271     },
20272     
20273     setBoxLabel : function(str)
20274     {
20275         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20276     }
20277
20278 });/*
20279  * Based on:
20280  * Ext JS Library 1.1.1
20281  * Copyright(c) 2006-2007, Ext JS, LLC.
20282  *
20283  * Originally Released Under LGPL - original licence link has changed is not relivant.
20284  *
20285  * Fork - LGPL
20286  * <script type="text/javascript">
20287  */
20288  
20289 /**
20290  * @class Roo.form.Radio
20291  * @extends Roo.form.Checkbox
20292  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20293  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20294  * @constructor
20295  * Creates a new Radio
20296  * @param {Object} config Configuration options
20297  */
20298 Roo.form.Radio = function(){
20299     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20300 };
20301 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20302     inputType: 'radio',
20303
20304     /**
20305      * If this radio is part of a group, it will return the selected value
20306      * @return {String}
20307      */
20308     getGroupValue : function(){
20309         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20310     },
20311     
20312     
20313     onRender : function(ct, position){
20314         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20315         
20316         if(this.inputValue !== undefined){
20317             this.el.dom.value = this.inputValue;
20318         }
20319          
20320         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20321         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20322         //var viewEl = this.wrap.createChild({ 
20323         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20324         //this.viewEl = viewEl;   
20325         //this.wrap.on('click', this.onClick,  this); 
20326         
20327         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20328         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20329         
20330         
20331         
20332         if(this.boxLabel){
20333             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20334         //    viewEl.on('click', this.onClick,  this); 
20335         }
20336          if(this.checked){
20337             this.el.dom.checked =   'checked' ;
20338         }
20339          
20340     } 
20341     
20342     
20343 });//<script type="text/javascript">
20344
20345 /*
20346  * Based  Ext JS Library 1.1.1
20347  * Copyright(c) 2006-2007, Ext JS, LLC.
20348  * LGPL
20349  *
20350  */
20351  
20352 /**
20353  * @class Roo.HtmlEditorCore
20354  * @extends Roo.Component
20355  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20356  *
20357  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20358  */
20359
20360 Roo.HtmlEditorCore = function(config){
20361     
20362     
20363     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20364     
20365     
20366     this.addEvents({
20367         /**
20368          * @event initialize
20369          * Fires when the editor is fully initialized (including the iframe)
20370          * @param {Roo.HtmlEditorCore} this
20371          */
20372         initialize: true,
20373         /**
20374          * @event activate
20375          * Fires when the editor is first receives the focus. Any insertion must wait
20376          * until after this event.
20377          * @param {Roo.HtmlEditorCore} this
20378          */
20379         activate: true,
20380          /**
20381          * @event beforesync
20382          * Fires before the textarea is updated with content from the editor iframe. Return false
20383          * to cancel the sync.
20384          * @param {Roo.HtmlEditorCore} this
20385          * @param {String} html
20386          */
20387         beforesync: true,
20388          /**
20389          * @event beforepush
20390          * Fires before the iframe editor is updated with content from the textarea. Return false
20391          * to cancel the push.
20392          * @param {Roo.HtmlEditorCore} this
20393          * @param {String} html
20394          */
20395         beforepush: true,
20396          /**
20397          * @event sync
20398          * Fires when the textarea is updated with content from the editor iframe.
20399          * @param {Roo.HtmlEditorCore} this
20400          * @param {String} html
20401          */
20402         sync: true,
20403          /**
20404          * @event push
20405          * Fires when the iframe editor is updated with content from the textarea.
20406          * @param {Roo.HtmlEditorCore} this
20407          * @param {String} html
20408          */
20409         push: true,
20410         
20411         /**
20412          * @event editorevent
20413          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20414          * @param {Roo.HtmlEditorCore} this
20415          */
20416         editorevent: true
20417         
20418     });
20419     
20420     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20421     
20422     // defaults : white / black...
20423     this.applyBlacklists();
20424     
20425     
20426     
20427 };
20428
20429
20430 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20431
20432
20433      /**
20434      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20435      */
20436     
20437     owner : false,
20438     
20439      /**
20440      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20441      *                        Roo.resizable.
20442      */
20443     resizable : false,
20444      /**
20445      * @cfg {Number} height (in pixels)
20446      */   
20447     height: 300,
20448    /**
20449      * @cfg {Number} width (in pixels)
20450      */   
20451     width: 500,
20452     
20453     /**
20454      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20455      * 
20456      */
20457     stylesheets: false,
20458     
20459     // id of frame..
20460     frameId: false,
20461     
20462     // private properties
20463     validationEvent : false,
20464     deferHeight: true,
20465     initialized : false,
20466     activated : false,
20467     sourceEditMode : false,
20468     onFocus : Roo.emptyFn,
20469     iframePad:3,
20470     hideMode:'offsets',
20471     
20472     clearUp: true,
20473     
20474     // blacklist + whitelisted elements..
20475     black: false,
20476     white: false,
20477      
20478     
20479
20480     /**
20481      * Protected method that will not generally be called directly. It
20482      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20483      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20484      */
20485     getDocMarkup : function(){
20486         // body styles..
20487         var st = '';
20488         
20489         // inherit styels from page...?? 
20490         if (this.stylesheets === false) {
20491             
20492             Roo.get(document.head).select('style').each(function(node) {
20493                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20494             });
20495             
20496             Roo.get(document.head).select('link').each(function(node) { 
20497                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20498             });
20499             
20500         } else if (!this.stylesheets.length) {
20501                 // simple..
20502                 st = '<style type="text/css">' +
20503                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20504                    '</style>';
20505         } else { 
20506             
20507         }
20508         
20509         st +=  '<style type="text/css">' +
20510             'IMG { cursor: pointer } ' +
20511         '</style>';
20512
20513         
20514         return '<html><head>' + st  +
20515             //<style type="text/css">' +
20516             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20517             //'</style>' +
20518             ' </head><body class="roo-htmleditor-body"></body></html>';
20519     },
20520
20521     // private
20522     onRender : function(ct, position)
20523     {
20524         var _t = this;
20525         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20526         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20527         
20528         
20529         this.el.dom.style.border = '0 none';
20530         this.el.dom.setAttribute('tabIndex', -1);
20531         this.el.addClass('x-hidden hide');
20532         
20533         
20534         
20535         if(Roo.isIE){ // fix IE 1px bogus margin
20536             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20537         }
20538        
20539         
20540         this.frameId = Roo.id();
20541         
20542          
20543         
20544         var iframe = this.owner.wrap.createChild({
20545             tag: 'iframe',
20546             cls: 'form-control', // bootstrap..
20547             id: this.frameId,
20548             name: this.frameId,
20549             frameBorder : 'no',
20550             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20551         }, this.el
20552         );
20553         
20554         
20555         this.iframe = iframe.dom;
20556
20557          this.assignDocWin();
20558         
20559         this.doc.designMode = 'on';
20560        
20561         this.doc.open();
20562         this.doc.write(this.getDocMarkup());
20563         this.doc.close();
20564
20565         
20566         var task = { // must defer to wait for browser to be ready
20567             run : function(){
20568                 //console.log("run task?" + this.doc.readyState);
20569                 this.assignDocWin();
20570                 if(this.doc.body || this.doc.readyState == 'complete'){
20571                     try {
20572                         this.doc.designMode="on";
20573                     } catch (e) {
20574                         return;
20575                     }
20576                     Roo.TaskMgr.stop(task);
20577                     this.initEditor.defer(10, this);
20578                 }
20579             },
20580             interval : 10,
20581             duration: 10000,
20582             scope: this
20583         };
20584         Roo.TaskMgr.start(task);
20585
20586     },
20587
20588     // private
20589     onResize : function(w, h)
20590     {
20591          Roo.log('resize: ' +w + ',' + h );
20592         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20593         if(!this.iframe){
20594             return;
20595         }
20596         if(typeof w == 'number'){
20597             
20598             this.iframe.style.width = w + 'px';
20599         }
20600         if(typeof h == 'number'){
20601             
20602             this.iframe.style.height = h + 'px';
20603             if(this.doc){
20604                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20605             }
20606         }
20607         
20608     },
20609
20610     /**
20611      * Toggles the editor between standard and source edit mode.
20612      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20613      */
20614     toggleSourceEdit : function(sourceEditMode){
20615         
20616         this.sourceEditMode = sourceEditMode === true;
20617         
20618         if(this.sourceEditMode){
20619  
20620             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20621             
20622         }else{
20623             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20624             //this.iframe.className = '';
20625             this.deferFocus();
20626         }
20627         //this.setSize(this.owner.wrap.getSize());
20628         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20629     },
20630
20631     
20632   
20633
20634     /**
20635      * Protected method that will not generally be called directly. If you need/want
20636      * custom HTML cleanup, this is the method you should override.
20637      * @param {String} html The HTML to be cleaned
20638      * return {String} The cleaned HTML
20639      */
20640     cleanHtml : function(html){
20641         html = String(html);
20642         if(html.length > 5){
20643             if(Roo.isSafari){ // strip safari nonsense
20644                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20645             }
20646         }
20647         if(html == '&nbsp;'){
20648             html = '';
20649         }
20650         return html;
20651     },
20652
20653     /**
20654      * HTML Editor -> Textarea
20655      * Protected method that will not generally be called directly. Syncs the contents
20656      * of the editor iframe with the textarea.
20657      */
20658     syncValue : function(){
20659         if(this.initialized){
20660             var bd = (this.doc.body || this.doc.documentElement);
20661             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20662             var html = bd.innerHTML;
20663             if(Roo.isSafari){
20664                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20665                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20666                 if(m && m[1]){
20667                     html = '<div style="'+m[0]+'">' + html + '</div>';
20668                 }
20669             }
20670             html = this.cleanHtml(html);
20671             // fix up the special chars.. normaly like back quotes in word...
20672             // however we do not want to do this with chinese..
20673             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20674                 var cc = b.charCodeAt();
20675                 if (
20676                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20677                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20678                     (cc >= 0xf900 && cc < 0xfb00 )
20679                 ) {
20680                         return b;
20681                 }
20682                 return "&#"+cc+";" 
20683             });
20684             if(this.owner.fireEvent('beforesync', this, html) !== false){
20685                 this.el.dom.value = html;
20686                 this.owner.fireEvent('sync', this, html);
20687             }
20688         }
20689     },
20690
20691     /**
20692      * Protected method that will not generally be called directly. Pushes the value of the textarea
20693      * into the iframe editor.
20694      */
20695     pushValue : function(){
20696         if(this.initialized){
20697             var v = this.el.dom.value.trim();
20698             
20699 //            if(v.length < 1){
20700 //                v = '&#160;';
20701 //            }
20702             
20703             if(this.owner.fireEvent('beforepush', this, v) !== false){
20704                 var d = (this.doc.body || this.doc.documentElement);
20705                 d.innerHTML = v;
20706                 this.cleanUpPaste();
20707                 this.el.dom.value = d.innerHTML;
20708                 this.owner.fireEvent('push', this, v);
20709             }
20710         }
20711     },
20712
20713     // private
20714     deferFocus : function(){
20715         this.focus.defer(10, this);
20716     },
20717
20718     // doc'ed in Field
20719     focus : function(){
20720         if(this.win && !this.sourceEditMode){
20721             this.win.focus();
20722         }else{
20723             this.el.focus();
20724         }
20725     },
20726     
20727     assignDocWin: function()
20728     {
20729         var iframe = this.iframe;
20730         
20731          if(Roo.isIE){
20732             this.doc = iframe.contentWindow.document;
20733             this.win = iframe.contentWindow;
20734         } else {
20735 //            if (!Roo.get(this.frameId)) {
20736 //                return;
20737 //            }
20738 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20739 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20740             
20741             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20742                 return;
20743             }
20744             
20745             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20746             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20747         }
20748     },
20749     
20750     // private
20751     initEditor : function(){
20752         //console.log("INIT EDITOR");
20753         this.assignDocWin();
20754         
20755         
20756         
20757         this.doc.designMode="on";
20758         this.doc.open();
20759         this.doc.write(this.getDocMarkup());
20760         this.doc.close();
20761         
20762         var dbody = (this.doc.body || this.doc.documentElement);
20763         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20764         // this copies styles from the containing element into thsi one..
20765         // not sure why we need all of this..
20766         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20767         
20768         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20769         //ss['background-attachment'] = 'fixed'; // w3c
20770         dbody.bgProperties = 'fixed'; // ie
20771         //Roo.DomHelper.applyStyles(dbody, ss);
20772         Roo.EventManager.on(this.doc, {
20773             //'mousedown': this.onEditorEvent,
20774             'mouseup': this.onEditorEvent,
20775             'dblclick': this.onEditorEvent,
20776             'click': this.onEditorEvent,
20777             'keyup': this.onEditorEvent,
20778             buffer:100,
20779             scope: this
20780         });
20781         if(Roo.isGecko){
20782             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20783         }
20784         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20785             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20786         }
20787         this.initialized = true;
20788
20789         this.owner.fireEvent('initialize', this);
20790         this.pushValue();
20791     },
20792
20793     // private
20794     onDestroy : function(){
20795         
20796         
20797         
20798         if(this.rendered){
20799             
20800             //for (var i =0; i < this.toolbars.length;i++) {
20801             //    // fixme - ask toolbars for heights?
20802             //    this.toolbars[i].onDestroy();
20803            // }
20804             
20805             //this.wrap.dom.innerHTML = '';
20806             //this.wrap.remove();
20807         }
20808     },
20809
20810     // private
20811     onFirstFocus : function(){
20812         
20813         this.assignDocWin();
20814         
20815         
20816         this.activated = true;
20817          
20818     
20819         if(Roo.isGecko){ // prevent silly gecko errors
20820             this.win.focus();
20821             var s = this.win.getSelection();
20822             if(!s.focusNode || s.focusNode.nodeType != 3){
20823                 var r = s.getRangeAt(0);
20824                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20825                 r.collapse(true);
20826                 this.deferFocus();
20827             }
20828             try{
20829                 this.execCmd('useCSS', true);
20830                 this.execCmd('styleWithCSS', false);
20831             }catch(e){}
20832         }
20833         this.owner.fireEvent('activate', this);
20834     },
20835
20836     // private
20837     adjustFont: function(btn){
20838         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20839         //if(Roo.isSafari){ // safari
20840         //    adjust *= 2;
20841        // }
20842         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20843         if(Roo.isSafari){ // safari
20844             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20845             v =  (v < 10) ? 10 : v;
20846             v =  (v > 48) ? 48 : v;
20847             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20848             
20849         }
20850         
20851         
20852         v = Math.max(1, v+adjust);
20853         
20854         this.execCmd('FontSize', v  );
20855     },
20856
20857     onEditorEvent : function(e)
20858     {
20859         this.owner.fireEvent('editorevent', this, e);
20860       //  this.updateToolbar();
20861         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20862     },
20863
20864     insertTag : function(tg)
20865     {
20866         // could be a bit smarter... -> wrap the current selected tRoo..
20867         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20868             
20869             range = this.createRange(this.getSelection());
20870             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20871             wrappingNode.appendChild(range.extractContents());
20872             range.insertNode(wrappingNode);
20873
20874             return;
20875             
20876             
20877             
20878         }
20879         this.execCmd("formatblock",   tg);
20880         
20881     },
20882     
20883     insertText : function(txt)
20884     {
20885         
20886         
20887         var range = this.createRange();
20888         range.deleteContents();
20889                //alert(Sender.getAttribute('label'));
20890                
20891         range.insertNode(this.doc.createTextNode(txt));
20892     } ,
20893     
20894      
20895
20896     /**
20897      * Executes a Midas editor command on the editor document and performs necessary focus and
20898      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20899      * @param {String} cmd The Midas command
20900      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20901      */
20902     relayCmd : function(cmd, value){
20903         this.win.focus();
20904         this.execCmd(cmd, value);
20905         this.owner.fireEvent('editorevent', this);
20906         //this.updateToolbar();
20907         this.owner.deferFocus();
20908     },
20909
20910     /**
20911      * Executes a Midas editor command directly on the editor document.
20912      * For visual commands, you should use {@link #relayCmd} instead.
20913      * <b>This should only be called after the editor is initialized.</b>
20914      * @param {String} cmd The Midas command
20915      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20916      */
20917     execCmd : function(cmd, value){
20918         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20919         this.syncValue();
20920     },
20921  
20922  
20923    
20924     /**
20925      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20926      * to insert tRoo.
20927      * @param {String} text | dom node.. 
20928      */
20929     insertAtCursor : function(text)
20930     {
20931         
20932         
20933         
20934         if(!this.activated){
20935             return;
20936         }
20937         /*
20938         if(Roo.isIE){
20939             this.win.focus();
20940             var r = this.doc.selection.createRange();
20941             if(r){
20942                 r.collapse(true);
20943                 r.pasteHTML(text);
20944                 this.syncValue();
20945                 this.deferFocus();
20946             
20947             }
20948             return;
20949         }
20950         */
20951         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20952             this.win.focus();
20953             
20954             
20955             // from jquery ui (MIT licenced)
20956             var range, node;
20957             var win = this.win;
20958             
20959             if (win.getSelection && win.getSelection().getRangeAt) {
20960                 range = win.getSelection().getRangeAt(0);
20961                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20962                 range.insertNode(node);
20963             } else if (win.document.selection && win.document.selection.createRange) {
20964                 // no firefox support
20965                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20966                 win.document.selection.createRange().pasteHTML(txt);
20967             } else {
20968                 // no firefox support
20969                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20970                 this.execCmd('InsertHTML', txt);
20971             } 
20972             
20973             this.syncValue();
20974             
20975             this.deferFocus();
20976         }
20977     },
20978  // private
20979     mozKeyPress : function(e){
20980         if(e.ctrlKey){
20981             var c = e.getCharCode(), cmd;
20982           
20983             if(c > 0){
20984                 c = String.fromCharCode(c).toLowerCase();
20985                 switch(c){
20986                     case 'b':
20987                         cmd = 'bold';
20988                         break;
20989                     case 'i':
20990                         cmd = 'italic';
20991                         break;
20992                     
20993                     case 'u':
20994                         cmd = 'underline';
20995                         break;
20996                     
20997                     case 'v':
20998                         this.cleanUpPaste.defer(100, this);
20999                         return;
21000                         
21001                 }
21002                 if(cmd){
21003                     this.win.focus();
21004                     this.execCmd(cmd);
21005                     this.deferFocus();
21006                     e.preventDefault();
21007                 }
21008                 
21009             }
21010         }
21011     },
21012
21013     // private
21014     fixKeys : function(){ // load time branching for fastest keydown performance
21015         if(Roo.isIE){
21016             return function(e){
21017                 var k = e.getKey(), r;
21018                 if(k == e.TAB){
21019                     e.stopEvent();
21020                     r = this.doc.selection.createRange();
21021                     if(r){
21022                         r.collapse(true);
21023                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21024                         this.deferFocus();
21025                     }
21026                     return;
21027                 }
21028                 
21029                 if(k == e.ENTER){
21030                     r = this.doc.selection.createRange();
21031                     if(r){
21032                         var target = r.parentElement();
21033                         if(!target || target.tagName.toLowerCase() != 'li'){
21034                             e.stopEvent();
21035                             r.pasteHTML('<br />');
21036                             r.collapse(false);
21037                             r.select();
21038                         }
21039                     }
21040                 }
21041                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21042                     this.cleanUpPaste.defer(100, this);
21043                     return;
21044                 }
21045                 
21046                 
21047             };
21048         }else if(Roo.isOpera){
21049             return function(e){
21050                 var k = e.getKey();
21051                 if(k == e.TAB){
21052                     e.stopEvent();
21053                     this.win.focus();
21054                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21055                     this.deferFocus();
21056                 }
21057                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21058                     this.cleanUpPaste.defer(100, this);
21059                     return;
21060                 }
21061                 
21062             };
21063         }else if(Roo.isSafari){
21064             return function(e){
21065                 var k = e.getKey();
21066                 
21067                 if(k == e.TAB){
21068                     e.stopEvent();
21069                     this.execCmd('InsertText','\t');
21070                     this.deferFocus();
21071                     return;
21072                 }
21073                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21074                     this.cleanUpPaste.defer(100, this);
21075                     return;
21076                 }
21077                 
21078              };
21079         }
21080     }(),
21081     
21082     getAllAncestors: function()
21083     {
21084         var p = this.getSelectedNode();
21085         var a = [];
21086         if (!p) {
21087             a.push(p); // push blank onto stack..
21088             p = this.getParentElement();
21089         }
21090         
21091         
21092         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21093             a.push(p);
21094             p = p.parentNode;
21095         }
21096         a.push(this.doc.body);
21097         return a;
21098     },
21099     lastSel : false,
21100     lastSelNode : false,
21101     
21102     
21103     getSelection : function() 
21104     {
21105         this.assignDocWin();
21106         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21107     },
21108     
21109     getSelectedNode: function() 
21110     {
21111         // this may only work on Gecko!!!
21112         
21113         // should we cache this!!!!
21114         
21115         
21116         
21117          
21118         var range = this.createRange(this.getSelection()).cloneRange();
21119         
21120         if (Roo.isIE) {
21121             var parent = range.parentElement();
21122             while (true) {
21123                 var testRange = range.duplicate();
21124                 testRange.moveToElementText(parent);
21125                 if (testRange.inRange(range)) {
21126                     break;
21127                 }
21128                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21129                     break;
21130                 }
21131                 parent = parent.parentElement;
21132             }
21133             return parent;
21134         }
21135         
21136         // is ancestor a text element.
21137         var ac =  range.commonAncestorContainer;
21138         if (ac.nodeType == 3) {
21139             ac = ac.parentNode;
21140         }
21141         
21142         var ar = ac.childNodes;
21143          
21144         var nodes = [];
21145         var other_nodes = [];
21146         var has_other_nodes = false;
21147         for (var i=0;i<ar.length;i++) {
21148             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21149                 continue;
21150             }
21151             // fullly contained node.
21152             
21153             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21154                 nodes.push(ar[i]);
21155                 continue;
21156             }
21157             
21158             // probably selected..
21159             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21160                 other_nodes.push(ar[i]);
21161                 continue;
21162             }
21163             // outer..
21164             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21165                 continue;
21166             }
21167             
21168             
21169             has_other_nodes = true;
21170         }
21171         if (!nodes.length && other_nodes.length) {
21172             nodes= other_nodes;
21173         }
21174         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21175             return false;
21176         }
21177         
21178         return nodes[0];
21179     },
21180     createRange: function(sel)
21181     {
21182         // this has strange effects when using with 
21183         // top toolbar - not sure if it's a great idea.
21184         //this.editor.contentWindow.focus();
21185         if (typeof sel != "undefined") {
21186             try {
21187                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21188             } catch(e) {
21189                 return this.doc.createRange();
21190             }
21191         } else {
21192             return this.doc.createRange();
21193         }
21194     },
21195     getParentElement: function()
21196     {
21197         
21198         this.assignDocWin();
21199         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21200         
21201         var range = this.createRange(sel);
21202          
21203         try {
21204             var p = range.commonAncestorContainer;
21205             while (p.nodeType == 3) { // text node
21206                 p = p.parentNode;
21207             }
21208             return p;
21209         } catch (e) {
21210             return null;
21211         }
21212     
21213     },
21214     /***
21215      *
21216      * Range intersection.. the hard stuff...
21217      *  '-1' = before
21218      *  '0' = hits..
21219      *  '1' = after.
21220      *         [ -- selected range --- ]
21221      *   [fail]                        [fail]
21222      *
21223      *    basically..
21224      *      if end is before start or  hits it. fail.
21225      *      if start is after end or hits it fail.
21226      *
21227      *   if either hits (but other is outside. - then it's not 
21228      *   
21229      *    
21230      **/
21231     
21232     
21233     // @see http://www.thismuchiknow.co.uk/?p=64.
21234     rangeIntersectsNode : function(range, node)
21235     {
21236         var nodeRange = node.ownerDocument.createRange();
21237         try {
21238             nodeRange.selectNode(node);
21239         } catch (e) {
21240             nodeRange.selectNodeContents(node);
21241         }
21242     
21243         var rangeStartRange = range.cloneRange();
21244         rangeStartRange.collapse(true);
21245     
21246         var rangeEndRange = range.cloneRange();
21247         rangeEndRange.collapse(false);
21248     
21249         var nodeStartRange = nodeRange.cloneRange();
21250         nodeStartRange.collapse(true);
21251     
21252         var nodeEndRange = nodeRange.cloneRange();
21253         nodeEndRange.collapse(false);
21254     
21255         return rangeStartRange.compareBoundaryPoints(
21256                  Range.START_TO_START, nodeEndRange) == -1 &&
21257                rangeEndRange.compareBoundaryPoints(
21258                  Range.START_TO_START, nodeStartRange) == 1;
21259         
21260          
21261     },
21262     rangeCompareNode : function(range, node)
21263     {
21264         var nodeRange = node.ownerDocument.createRange();
21265         try {
21266             nodeRange.selectNode(node);
21267         } catch (e) {
21268             nodeRange.selectNodeContents(node);
21269         }
21270         
21271         
21272         range.collapse(true);
21273     
21274         nodeRange.collapse(true);
21275      
21276         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21277         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21278          
21279         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21280         
21281         var nodeIsBefore   =  ss == 1;
21282         var nodeIsAfter    = ee == -1;
21283         
21284         if (nodeIsBefore && nodeIsAfter) {
21285             return 0; // outer
21286         }
21287         if (!nodeIsBefore && nodeIsAfter) {
21288             return 1; //right trailed.
21289         }
21290         
21291         if (nodeIsBefore && !nodeIsAfter) {
21292             return 2;  // left trailed.
21293         }
21294         // fully contined.
21295         return 3;
21296     },
21297
21298     // private? - in a new class?
21299     cleanUpPaste :  function()
21300     {
21301         // cleans up the whole document..
21302         Roo.log('cleanuppaste');
21303         
21304         this.cleanUpChildren(this.doc.body);
21305         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21306         if (clean != this.doc.body.innerHTML) {
21307             this.doc.body.innerHTML = clean;
21308         }
21309         
21310     },
21311     
21312     cleanWordChars : function(input) {// change the chars to hex code
21313         var he = Roo.HtmlEditorCore;
21314         
21315         var output = input;
21316         Roo.each(he.swapCodes, function(sw) { 
21317             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21318             
21319             output = output.replace(swapper, sw[1]);
21320         });
21321         
21322         return output;
21323     },
21324     
21325     
21326     cleanUpChildren : function (n)
21327     {
21328         if (!n.childNodes.length) {
21329             return;
21330         }
21331         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21332            this.cleanUpChild(n.childNodes[i]);
21333         }
21334     },
21335     
21336     
21337         
21338     
21339     cleanUpChild : function (node)
21340     {
21341         var ed = this;
21342         //console.log(node);
21343         if (node.nodeName == "#text") {
21344             // clean up silly Windows -- stuff?
21345             return; 
21346         }
21347         if (node.nodeName == "#comment") {
21348             node.parentNode.removeChild(node);
21349             // clean up silly Windows -- stuff?
21350             return; 
21351         }
21352         var lcname = node.tagName.toLowerCase();
21353         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21354         // whitelist of tags..
21355         
21356         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21357             // remove node.
21358             node.parentNode.removeChild(node);
21359             return;
21360             
21361         }
21362         
21363         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21364         
21365         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21366         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21367         
21368         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21369         //    remove_keep_children = true;
21370         //}
21371         
21372         if (remove_keep_children) {
21373             this.cleanUpChildren(node);
21374             // inserts everything just before this node...
21375             while (node.childNodes.length) {
21376                 var cn = node.childNodes[0];
21377                 node.removeChild(cn);
21378                 node.parentNode.insertBefore(cn, node);
21379             }
21380             node.parentNode.removeChild(node);
21381             return;
21382         }
21383         
21384         if (!node.attributes || !node.attributes.length) {
21385             this.cleanUpChildren(node);
21386             return;
21387         }
21388         
21389         function cleanAttr(n,v)
21390         {
21391             
21392             if (v.match(/^\./) || v.match(/^\//)) {
21393                 return;
21394             }
21395             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21396                 return;
21397             }
21398             if (v.match(/^#/)) {
21399                 return;
21400             }
21401 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21402             node.removeAttribute(n);
21403             
21404         }
21405         
21406         var cwhite = this.cwhite;
21407         var cblack = this.cblack;
21408             
21409         function cleanStyle(n,v)
21410         {
21411             if (v.match(/expression/)) { //XSS?? should we even bother..
21412                 node.removeAttribute(n);
21413                 return;
21414             }
21415             
21416             var parts = v.split(/;/);
21417             var clean = [];
21418             
21419             Roo.each(parts, function(p) {
21420                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21421                 if (!p.length) {
21422                     return true;
21423                 }
21424                 var l = p.split(':').shift().replace(/\s+/g,'');
21425                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21426                 
21427                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21428 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21429                     //node.removeAttribute(n);
21430                     return true;
21431                 }
21432                 //Roo.log()
21433                 // only allow 'c whitelisted system attributes'
21434                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21435 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21436                     //node.removeAttribute(n);
21437                     return true;
21438                 }
21439                 
21440                 
21441                  
21442                 
21443                 clean.push(p);
21444                 return true;
21445             });
21446             if (clean.length) { 
21447                 node.setAttribute(n, clean.join(';'));
21448             } else {
21449                 node.removeAttribute(n);
21450             }
21451             
21452         }
21453         
21454         
21455         for (var i = node.attributes.length-1; i > -1 ; i--) {
21456             var a = node.attributes[i];
21457             //console.log(a);
21458             
21459             if (a.name.toLowerCase().substr(0,2)=='on')  {
21460                 node.removeAttribute(a.name);
21461                 continue;
21462             }
21463             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21464                 node.removeAttribute(a.name);
21465                 continue;
21466             }
21467             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21468                 cleanAttr(a.name,a.value); // fixme..
21469                 continue;
21470             }
21471             if (a.name == 'style') {
21472                 cleanStyle(a.name,a.value);
21473                 continue;
21474             }
21475             /// clean up MS crap..
21476             // tecnically this should be a list of valid class'es..
21477             
21478             
21479             if (a.name == 'class') {
21480                 if (a.value.match(/^Mso/)) {
21481                     node.className = '';
21482                 }
21483                 
21484                 if (a.value.match(/body/)) {
21485                     node.className = '';
21486                 }
21487                 continue;
21488             }
21489             
21490             // style cleanup!?
21491             // class cleanup?
21492             
21493         }
21494         
21495         
21496         this.cleanUpChildren(node);
21497         
21498         
21499     },
21500     
21501     /**
21502      * Clean up MS wordisms...
21503      */
21504     cleanWord : function(node)
21505     {
21506         
21507         
21508         if (!node) {
21509             this.cleanWord(this.doc.body);
21510             return;
21511         }
21512         if (node.nodeName == "#text") {
21513             // clean up silly Windows -- stuff?
21514             return; 
21515         }
21516         if (node.nodeName == "#comment") {
21517             node.parentNode.removeChild(node);
21518             // clean up silly Windows -- stuff?
21519             return; 
21520         }
21521         
21522         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21523             node.parentNode.removeChild(node);
21524             return;
21525         }
21526         
21527         // remove - but keep children..
21528         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21529             while (node.childNodes.length) {
21530                 var cn = node.childNodes[0];
21531                 node.removeChild(cn);
21532                 node.parentNode.insertBefore(cn, node);
21533             }
21534             node.parentNode.removeChild(node);
21535             this.iterateChildren(node, this.cleanWord);
21536             return;
21537         }
21538         // clean styles
21539         if (node.className.length) {
21540             
21541             var cn = node.className.split(/\W+/);
21542             var cna = [];
21543             Roo.each(cn, function(cls) {
21544                 if (cls.match(/Mso[a-zA-Z]+/)) {
21545                     return;
21546                 }
21547                 cna.push(cls);
21548             });
21549             node.className = cna.length ? cna.join(' ') : '';
21550             if (!cna.length) {
21551                 node.removeAttribute("class");
21552             }
21553         }
21554         
21555         if (node.hasAttribute("lang")) {
21556             node.removeAttribute("lang");
21557         }
21558         
21559         if (node.hasAttribute("style")) {
21560             
21561             var styles = node.getAttribute("style").split(";");
21562             var nstyle = [];
21563             Roo.each(styles, function(s) {
21564                 if (!s.match(/:/)) {
21565                     return;
21566                 }
21567                 var kv = s.split(":");
21568                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21569                     return;
21570                 }
21571                 // what ever is left... we allow.
21572                 nstyle.push(s);
21573             });
21574             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21575             if (!nstyle.length) {
21576                 node.removeAttribute('style');
21577             }
21578         }
21579         this.iterateChildren(node, this.cleanWord);
21580         
21581         
21582         
21583     },
21584     /**
21585      * iterateChildren of a Node, calling fn each time, using this as the scole..
21586      * @param {DomNode} node node to iterate children of.
21587      * @param {Function} fn method of this class to call on each item.
21588      */
21589     iterateChildren : function(node, fn)
21590     {
21591         if (!node.childNodes.length) {
21592                 return;
21593         }
21594         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21595            fn.call(this, node.childNodes[i])
21596         }
21597     },
21598     
21599     
21600     /**
21601      * cleanTableWidths.
21602      *
21603      * Quite often pasting from word etc.. results in tables with column and widths.
21604      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21605      *
21606      */
21607     cleanTableWidths : function(node)
21608     {
21609          
21610          
21611         if (!node) {
21612             this.cleanTableWidths(this.doc.body);
21613             return;
21614         }
21615         
21616         // ignore list...
21617         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21618             return; 
21619         }
21620         Roo.log(node.tagName);
21621         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21622             this.iterateChildren(node, this.cleanTableWidths);
21623             return;
21624         }
21625         if (node.hasAttribute('width')) {
21626             node.removeAttribute('width');
21627         }
21628         
21629          
21630         if (node.hasAttribute("style")) {
21631             // pretty basic...
21632             
21633             var styles = node.getAttribute("style").split(";");
21634             var nstyle = [];
21635             Roo.each(styles, function(s) {
21636                 if (!s.match(/:/)) {
21637                     return;
21638                 }
21639                 var kv = s.split(":");
21640                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21641                     return;
21642                 }
21643                 // what ever is left... we allow.
21644                 nstyle.push(s);
21645             });
21646             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21647             if (!nstyle.length) {
21648                 node.removeAttribute('style');
21649             }
21650         }
21651         
21652         this.iterateChildren(node, this.cleanTableWidths);
21653         
21654         
21655     },
21656     
21657     
21658     
21659     
21660     domToHTML : function(currentElement, depth, nopadtext) {
21661         
21662         depth = depth || 0;
21663         nopadtext = nopadtext || false;
21664     
21665         if (!currentElement) {
21666             return this.domToHTML(this.doc.body);
21667         }
21668         
21669         //Roo.log(currentElement);
21670         var j;
21671         var allText = false;
21672         var nodeName = currentElement.nodeName;
21673         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21674         
21675         if  (nodeName == '#text') {
21676             
21677             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21678         }
21679         
21680         
21681         var ret = '';
21682         if (nodeName != 'BODY') {
21683              
21684             var i = 0;
21685             // Prints the node tagName, such as <A>, <IMG>, etc
21686             if (tagName) {
21687                 var attr = [];
21688                 for(i = 0; i < currentElement.attributes.length;i++) {
21689                     // quoting?
21690                     var aname = currentElement.attributes.item(i).name;
21691                     if (!currentElement.attributes.item(i).value.length) {
21692                         continue;
21693                     }
21694                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21695                 }
21696                 
21697                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21698             } 
21699             else {
21700                 
21701                 // eack
21702             }
21703         } else {
21704             tagName = false;
21705         }
21706         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21707             return ret;
21708         }
21709         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21710             nopadtext = true;
21711         }
21712         
21713         
21714         // Traverse the tree
21715         i = 0;
21716         var currentElementChild = currentElement.childNodes.item(i);
21717         var allText = true;
21718         var innerHTML  = '';
21719         lastnode = '';
21720         while (currentElementChild) {
21721             // Formatting code (indent the tree so it looks nice on the screen)
21722             var nopad = nopadtext;
21723             if (lastnode == 'SPAN') {
21724                 nopad  = true;
21725             }
21726             // text
21727             if  (currentElementChild.nodeName == '#text') {
21728                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21729                 toadd = nopadtext ? toadd : toadd.trim();
21730                 if (!nopad && toadd.length > 80) {
21731                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21732                 }
21733                 innerHTML  += toadd;
21734                 
21735                 i++;
21736                 currentElementChild = currentElement.childNodes.item(i);
21737                 lastNode = '';
21738                 continue;
21739             }
21740             allText = false;
21741             
21742             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21743                 
21744             // Recursively traverse the tree structure of the child node
21745             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21746             lastnode = currentElementChild.nodeName;
21747             i++;
21748             currentElementChild=currentElement.childNodes.item(i);
21749         }
21750         
21751         ret += innerHTML;
21752         
21753         if (!allText) {
21754                 // The remaining code is mostly for formatting the tree
21755             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21756         }
21757         
21758         
21759         if (tagName) {
21760             ret+= "</"+tagName+">";
21761         }
21762         return ret;
21763         
21764     },
21765         
21766     applyBlacklists : function()
21767     {
21768         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21769         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21770         
21771         this.white = [];
21772         this.black = [];
21773         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21774             if (b.indexOf(tag) > -1) {
21775                 return;
21776             }
21777             this.white.push(tag);
21778             
21779         }, this);
21780         
21781         Roo.each(w, function(tag) {
21782             if (b.indexOf(tag) > -1) {
21783                 return;
21784             }
21785             if (this.white.indexOf(tag) > -1) {
21786                 return;
21787             }
21788             this.white.push(tag);
21789             
21790         }, this);
21791         
21792         
21793         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21794             if (w.indexOf(tag) > -1) {
21795                 return;
21796             }
21797             this.black.push(tag);
21798             
21799         }, this);
21800         
21801         Roo.each(b, function(tag) {
21802             if (w.indexOf(tag) > -1) {
21803                 return;
21804             }
21805             if (this.black.indexOf(tag) > -1) {
21806                 return;
21807             }
21808             this.black.push(tag);
21809             
21810         }, this);
21811         
21812         
21813         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21814         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21815         
21816         this.cwhite = [];
21817         this.cblack = [];
21818         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21819             if (b.indexOf(tag) > -1) {
21820                 return;
21821             }
21822             this.cwhite.push(tag);
21823             
21824         }, this);
21825         
21826         Roo.each(w, function(tag) {
21827             if (b.indexOf(tag) > -1) {
21828                 return;
21829             }
21830             if (this.cwhite.indexOf(tag) > -1) {
21831                 return;
21832             }
21833             this.cwhite.push(tag);
21834             
21835         }, this);
21836         
21837         
21838         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21839             if (w.indexOf(tag) > -1) {
21840                 return;
21841             }
21842             this.cblack.push(tag);
21843             
21844         }, this);
21845         
21846         Roo.each(b, function(tag) {
21847             if (w.indexOf(tag) > -1) {
21848                 return;
21849             }
21850             if (this.cblack.indexOf(tag) > -1) {
21851                 return;
21852             }
21853             this.cblack.push(tag);
21854             
21855         }, this);
21856     },
21857     
21858     setStylesheets : function(stylesheets)
21859     {
21860         if(typeof(stylesheets) == 'string'){
21861             Roo.get(this.iframe.contentDocument.head).createChild({
21862                 tag : 'link',
21863                 rel : 'stylesheet',
21864                 type : 'text/css',
21865                 href : stylesheets
21866             });
21867             
21868             return;
21869         }
21870         var _this = this;
21871      
21872         Roo.each(stylesheets, function(s) {
21873             if(!s.length){
21874                 return;
21875             }
21876             
21877             Roo.get(_this.iframe.contentDocument.head).createChild({
21878                 tag : 'link',
21879                 rel : 'stylesheet',
21880                 type : 'text/css',
21881                 href : s
21882             });
21883         });
21884
21885         
21886     },
21887     
21888     removeStylesheets : function()
21889     {
21890         var _this = this;
21891         
21892         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21893             s.remove();
21894         });
21895     }
21896     
21897     // hide stuff that is not compatible
21898     /**
21899      * @event blur
21900      * @hide
21901      */
21902     /**
21903      * @event change
21904      * @hide
21905      */
21906     /**
21907      * @event focus
21908      * @hide
21909      */
21910     /**
21911      * @event specialkey
21912      * @hide
21913      */
21914     /**
21915      * @cfg {String} fieldClass @hide
21916      */
21917     /**
21918      * @cfg {String} focusClass @hide
21919      */
21920     /**
21921      * @cfg {String} autoCreate @hide
21922      */
21923     /**
21924      * @cfg {String} inputType @hide
21925      */
21926     /**
21927      * @cfg {String} invalidClass @hide
21928      */
21929     /**
21930      * @cfg {String} invalidText @hide
21931      */
21932     /**
21933      * @cfg {String} msgFx @hide
21934      */
21935     /**
21936      * @cfg {String} validateOnBlur @hide
21937      */
21938 });
21939
21940 Roo.HtmlEditorCore.white = [
21941         'area', 'br', 'img', 'input', 'hr', 'wbr',
21942         
21943        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21944        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21945        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21946        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21947        'table',   'ul',         'xmp', 
21948        
21949        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21950       'thead',   'tr', 
21951      
21952       'dir', 'menu', 'ol', 'ul', 'dl',
21953        
21954       'embed',  'object'
21955 ];
21956
21957
21958 Roo.HtmlEditorCore.black = [
21959     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21960         'applet', // 
21961         'base',   'basefont', 'bgsound', 'blink',  'body', 
21962         'frame',  'frameset', 'head',    'html',   'ilayer', 
21963         'iframe', 'layer',  'link',     'meta',    'object',   
21964         'script', 'style' ,'title',  'xml' // clean later..
21965 ];
21966 Roo.HtmlEditorCore.clean = [
21967     'script', 'style', 'title', 'xml'
21968 ];
21969 Roo.HtmlEditorCore.remove = [
21970     'font'
21971 ];
21972 // attributes..
21973
21974 Roo.HtmlEditorCore.ablack = [
21975     'on'
21976 ];
21977     
21978 Roo.HtmlEditorCore.aclean = [ 
21979     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21980 ];
21981
21982 // protocols..
21983 Roo.HtmlEditorCore.pwhite= [
21984         'http',  'https',  'mailto'
21985 ];
21986
21987 // white listed style attributes.
21988 Roo.HtmlEditorCore.cwhite= [
21989       //  'text-align', /// default is to allow most things..
21990       
21991          
21992 //        'font-size'//??
21993 ];
21994
21995 // black listed style attributes.
21996 Roo.HtmlEditorCore.cblack= [
21997       //  'font-size' -- this can be set by the project 
21998 ];
21999
22000
22001 Roo.HtmlEditorCore.swapCodes   =[ 
22002     [    8211, "--" ], 
22003     [    8212, "--" ], 
22004     [    8216,  "'" ],  
22005     [    8217, "'" ],  
22006     [    8220, '"' ],  
22007     [    8221, '"' ],  
22008     [    8226, "*" ],  
22009     [    8230, "..." ]
22010 ]; 
22011
22012     //<script type="text/javascript">
22013
22014 /*
22015  * Ext JS Library 1.1.1
22016  * Copyright(c) 2006-2007, Ext JS, LLC.
22017  * Licence LGPL
22018  * 
22019  */
22020  
22021  
22022 Roo.form.HtmlEditor = function(config){
22023     
22024     
22025     
22026     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22027     
22028     if (!this.toolbars) {
22029         this.toolbars = [];
22030     }
22031     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22032     
22033     
22034 };
22035
22036 /**
22037  * @class Roo.form.HtmlEditor
22038  * @extends Roo.form.Field
22039  * Provides a lightweight HTML Editor component.
22040  *
22041  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22042  * 
22043  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22044  * supported by this editor.</b><br/><br/>
22045  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22046  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22047  */
22048 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22049     /**
22050      * @cfg {Boolean} clearUp
22051      */
22052     clearUp : true,
22053       /**
22054      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22055      */
22056     toolbars : false,
22057    
22058      /**
22059      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22060      *                        Roo.resizable.
22061      */
22062     resizable : false,
22063      /**
22064      * @cfg {Number} height (in pixels)
22065      */   
22066     height: 300,
22067    /**
22068      * @cfg {Number} width (in pixels)
22069      */   
22070     width: 500,
22071     
22072     /**
22073      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22074      * 
22075      */
22076     stylesheets: false,
22077     
22078     
22079      /**
22080      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22081      * 
22082      */
22083     cblack: false,
22084     /**
22085      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22086      * 
22087      */
22088     cwhite: false,
22089     
22090      /**
22091      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22092      * 
22093      */
22094     black: false,
22095     /**
22096      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22097      * 
22098      */
22099     white: false,
22100     
22101     // id of frame..
22102     frameId: false,
22103     
22104     // private properties
22105     validationEvent : false,
22106     deferHeight: true,
22107     initialized : false,
22108     activated : false,
22109     
22110     onFocus : Roo.emptyFn,
22111     iframePad:3,
22112     hideMode:'offsets',
22113     
22114     actionMode : 'container', // defaults to hiding it...
22115     
22116     defaultAutoCreate : { // modified by initCompnoent..
22117         tag: "textarea",
22118         style:"width:500px;height:300px;",
22119         autocomplete: "new-password"
22120     },
22121
22122     // private
22123     initComponent : function(){
22124         this.addEvents({
22125             /**
22126              * @event initialize
22127              * Fires when the editor is fully initialized (including the iframe)
22128              * @param {HtmlEditor} this
22129              */
22130             initialize: true,
22131             /**
22132              * @event activate
22133              * Fires when the editor is first receives the focus. Any insertion must wait
22134              * until after this event.
22135              * @param {HtmlEditor} this
22136              */
22137             activate: true,
22138              /**
22139              * @event beforesync
22140              * Fires before the textarea is updated with content from the editor iframe. Return false
22141              * to cancel the sync.
22142              * @param {HtmlEditor} this
22143              * @param {String} html
22144              */
22145             beforesync: true,
22146              /**
22147              * @event beforepush
22148              * Fires before the iframe editor is updated with content from the textarea. Return false
22149              * to cancel the push.
22150              * @param {HtmlEditor} this
22151              * @param {String} html
22152              */
22153             beforepush: true,
22154              /**
22155              * @event sync
22156              * Fires when the textarea is updated with content from the editor iframe.
22157              * @param {HtmlEditor} this
22158              * @param {String} html
22159              */
22160             sync: true,
22161              /**
22162              * @event push
22163              * Fires when the iframe editor is updated with content from the textarea.
22164              * @param {HtmlEditor} this
22165              * @param {String} html
22166              */
22167             push: true,
22168              /**
22169              * @event editmodechange
22170              * Fires when the editor switches edit modes
22171              * @param {HtmlEditor} this
22172              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22173              */
22174             editmodechange: true,
22175             /**
22176              * @event editorevent
22177              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22178              * @param {HtmlEditor} this
22179              */
22180             editorevent: true,
22181             /**
22182              * @event firstfocus
22183              * Fires when on first focus - needed by toolbars..
22184              * @param {HtmlEditor} this
22185              */
22186             firstfocus: true,
22187             /**
22188              * @event autosave
22189              * Auto save the htmlEditor value as a file into Events
22190              * @param {HtmlEditor} this
22191              */
22192             autosave: true,
22193             /**
22194              * @event savedpreview
22195              * preview the saved version of htmlEditor
22196              * @param {HtmlEditor} this
22197              */
22198             savedpreview: true,
22199             
22200             /**
22201             * @event stylesheetsclick
22202             * Fires when press the Sytlesheets button
22203             * @param {Roo.HtmlEditorCore} this
22204             */
22205             stylesheetsclick: true
22206         });
22207         this.defaultAutoCreate =  {
22208             tag: "textarea",
22209             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22210             autocomplete: "new-password"
22211         };
22212     },
22213
22214     /**
22215      * Protected method that will not generally be called directly. It
22216      * is called when the editor creates its toolbar. Override this method if you need to
22217      * add custom toolbar buttons.
22218      * @param {HtmlEditor} editor
22219      */
22220     createToolbar : function(editor){
22221         Roo.log("create toolbars");
22222         if (!editor.toolbars || !editor.toolbars.length) {
22223             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22224         }
22225         
22226         for (var i =0 ; i < editor.toolbars.length;i++) {
22227             editor.toolbars[i] = Roo.factory(
22228                     typeof(editor.toolbars[i]) == 'string' ?
22229                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22230                 Roo.form.HtmlEditor);
22231             editor.toolbars[i].init(editor);
22232         }
22233          
22234         
22235     },
22236
22237      
22238     // private
22239     onRender : function(ct, position)
22240     {
22241         var _t = this;
22242         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22243         
22244         this.wrap = this.el.wrap({
22245             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22246         });
22247         
22248         this.editorcore.onRender(ct, position);
22249          
22250         if (this.resizable) {
22251             this.resizeEl = new Roo.Resizable(this.wrap, {
22252                 pinned : true,
22253                 wrap: true,
22254                 dynamic : true,
22255                 minHeight : this.height,
22256                 height: this.height,
22257                 handles : this.resizable,
22258                 width: this.width,
22259                 listeners : {
22260                     resize : function(r, w, h) {
22261                         _t.onResize(w,h); // -something
22262                     }
22263                 }
22264             });
22265             
22266         }
22267         this.createToolbar(this);
22268        
22269         
22270         if(!this.width){
22271             this.setSize(this.wrap.getSize());
22272         }
22273         if (this.resizeEl) {
22274             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22275             // should trigger onReize..
22276         }
22277         
22278         this.keyNav = new Roo.KeyNav(this.el, {
22279             
22280             "tab" : function(e){
22281                 e.preventDefault();
22282                 
22283                 var value = this.getValue();
22284                 
22285                 var start = this.el.dom.selectionStart;
22286                 var end = this.el.dom.selectionEnd;
22287                 
22288                 if(!e.shiftKey){
22289                     
22290                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22291                     this.el.dom.setSelectionRange(end + 1, end + 1);
22292                     return;
22293                 }
22294                 
22295                 var f = value.substring(0, start).split("\t");
22296                 
22297                 if(f.pop().length != 0){
22298                     return;
22299                 }
22300                 
22301                 this.setValue(f.join("\t") + value.substring(end));
22302                 this.el.dom.setSelectionRange(start - 1, start - 1);
22303                 
22304             },
22305             
22306             "home" : function(e){
22307                 e.preventDefault();
22308                 
22309                 var curr = this.el.dom.selectionStart;
22310                 var lines = this.getValue().split("\n");
22311                 
22312                 if(!lines.length){
22313                     return;
22314                 }
22315                 
22316                 if(e.ctrlKey){
22317                     this.el.dom.setSelectionRange(0, 0);
22318                     return;
22319                 }
22320                 
22321                 var pos = 0;
22322                 
22323                 for (var i = 0; i < lines.length;i++) {
22324                     pos += lines[i].length;
22325                     
22326                     if(i != 0){
22327                         pos += 1;
22328                     }
22329                     
22330                     if(pos < curr){
22331                         continue;
22332                     }
22333                     
22334                     pos -= lines[i].length;
22335                     
22336                     break;
22337                 }
22338                 
22339                 if(!e.shiftKey){
22340                     this.el.dom.setSelectionRange(pos, pos);
22341                     return;
22342                 }
22343                 
22344                 this.el.dom.selectionStart = pos;
22345                 this.el.dom.selectionEnd = curr;
22346             },
22347             
22348             "end" : function(e){
22349                 e.preventDefault();
22350                 
22351                 var curr = this.el.dom.selectionStart;
22352                 var lines = this.getValue().split("\n");
22353                 
22354                 if(!lines.length){
22355                     return;
22356                 }
22357                 
22358                 if(e.ctrlKey){
22359                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22360                     return;
22361                 }
22362                 
22363                 var pos = 0;
22364                 
22365                 for (var i = 0; i < lines.length;i++) {
22366                     
22367                     pos += lines[i].length;
22368                     
22369                     if(i != 0){
22370                         pos += 1;
22371                     }
22372                     
22373                     if(pos < curr){
22374                         continue;
22375                     }
22376                     
22377                     break;
22378                 }
22379                 
22380                 if(!e.shiftKey){
22381                     this.el.dom.setSelectionRange(pos, pos);
22382                     return;
22383                 }
22384                 
22385                 this.el.dom.selectionStart = curr;
22386                 this.el.dom.selectionEnd = pos;
22387             },
22388
22389             scope : this,
22390
22391             doRelay : function(foo, bar, hname){
22392                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22393             },
22394
22395             forceKeyDown: true
22396         });
22397         
22398 //        if(this.autosave && this.w){
22399 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22400 //        }
22401     },
22402
22403     // private
22404     onResize : function(w, h)
22405     {
22406         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22407         var ew = false;
22408         var eh = false;
22409         
22410         if(this.el ){
22411             if(typeof w == 'number'){
22412                 var aw = w - this.wrap.getFrameWidth('lr');
22413                 this.el.setWidth(this.adjustWidth('textarea', aw));
22414                 ew = aw;
22415             }
22416             if(typeof h == 'number'){
22417                 var tbh = 0;
22418                 for (var i =0; i < this.toolbars.length;i++) {
22419                     // fixme - ask toolbars for heights?
22420                     tbh += this.toolbars[i].tb.el.getHeight();
22421                     if (this.toolbars[i].footer) {
22422                         tbh += this.toolbars[i].footer.el.getHeight();
22423                     }
22424                 }
22425                 
22426                 
22427                 
22428                 
22429                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22430                 ah -= 5; // knock a few pixes off for look..
22431 //                Roo.log(ah);
22432                 this.el.setHeight(this.adjustWidth('textarea', ah));
22433                 var eh = ah;
22434             }
22435         }
22436         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22437         this.editorcore.onResize(ew,eh);
22438         
22439     },
22440
22441     /**
22442      * Toggles the editor between standard and source edit mode.
22443      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22444      */
22445     toggleSourceEdit : function(sourceEditMode)
22446     {
22447         this.editorcore.toggleSourceEdit(sourceEditMode);
22448         
22449         if(this.editorcore.sourceEditMode){
22450             Roo.log('editor - showing textarea');
22451             
22452 //            Roo.log('in');
22453 //            Roo.log(this.syncValue());
22454             this.editorcore.syncValue();
22455             this.el.removeClass('x-hidden');
22456             this.el.dom.removeAttribute('tabIndex');
22457             this.el.focus();
22458             
22459             for (var i = 0; i < this.toolbars.length; i++) {
22460                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22461                     this.toolbars[i].tb.hide();
22462                     this.toolbars[i].footer.hide();
22463                 }
22464             }
22465             
22466         }else{
22467             Roo.log('editor - hiding textarea');
22468 //            Roo.log('out')
22469 //            Roo.log(this.pushValue()); 
22470             this.editorcore.pushValue();
22471             
22472             this.el.addClass('x-hidden');
22473             this.el.dom.setAttribute('tabIndex', -1);
22474             
22475             for (var i = 0; i < this.toolbars.length; i++) {
22476                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22477                     this.toolbars[i].tb.show();
22478                     this.toolbars[i].footer.show();
22479                 }
22480             }
22481             
22482             //this.deferFocus();
22483         }
22484         
22485         this.setSize(this.wrap.getSize());
22486         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22487         
22488         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22489     },
22490  
22491     // private (for BoxComponent)
22492     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22493
22494     // private (for BoxComponent)
22495     getResizeEl : function(){
22496         return this.wrap;
22497     },
22498
22499     // private (for BoxComponent)
22500     getPositionEl : function(){
22501         return this.wrap;
22502     },
22503
22504     // private
22505     initEvents : function(){
22506         this.originalValue = this.getValue();
22507     },
22508
22509     /**
22510      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22511      * @method
22512      */
22513     markInvalid : Roo.emptyFn,
22514     /**
22515      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22516      * @method
22517      */
22518     clearInvalid : Roo.emptyFn,
22519
22520     setValue : function(v){
22521         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22522         this.editorcore.pushValue();
22523     },
22524
22525      
22526     // private
22527     deferFocus : function(){
22528         this.focus.defer(10, this);
22529     },
22530
22531     // doc'ed in Field
22532     focus : function(){
22533         this.editorcore.focus();
22534         
22535     },
22536       
22537
22538     // private
22539     onDestroy : function(){
22540         
22541         
22542         
22543         if(this.rendered){
22544             
22545             for (var i =0; i < this.toolbars.length;i++) {
22546                 // fixme - ask toolbars for heights?
22547                 this.toolbars[i].onDestroy();
22548             }
22549             
22550             this.wrap.dom.innerHTML = '';
22551             this.wrap.remove();
22552         }
22553     },
22554
22555     // private
22556     onFirstFocus : function(){
22557         //Roo.log("onFirstFocus");
22558         this.editorcore.onFirstFocus();
22559          for (var i =0; i < this.toolbars.length;i++) {
22560             this.toolbars[i].onFirstFocus();
22561         }
22562         
22563     },
22564     
22565     // private
22566     syncValue : function()
22567     {
22568         this.editorcore.syncValue();
22569     },
22570     
22571     pushValue : function()
22572     {
22573         this.editorcore.pushValue();
22574     },
22575     
22576     setStylesheets : function(stylesheets)
22577     {
22578         this.editorcore.setStylesheets(stylesheets);
22579     },
22580     
22581     removeStylesheets : function()
22582     {
22583         this.editorcore.removeStylesheets();
22584     }
22585      
22586     
22587     // hide stuff that is not compatible
22588     /**
22589      * @event blur
22590      * @hide
22591      */
22592     /**
22593      * @event change
22594      * @hide
22595      */
22596     /**
22597      * @event focus
22598      * @hide
22599      */
22600     /**
22601      * @event specialkey
22602      * @hide
22603      */
22604     /**
22605      * @cfg {String} fieldClass @hide
22606      */
22607     /**
22608      * @cfg {String} focusClass @hide
22609      */
22610     /**
22611      * @cfg {String} autoCreate @hide
22612      */
22613     /**
22614      * @cfg {String} inputType @hide
22615      */
22616     /**
22617      * @cfg {String} invalidClass @hide
22618      */
22619     /**
22620      * @cfg {String} invalidText @hide
22621      */
22622     /**
22623      * @cfg {String} msgFx @hide
22624      */
22625     /**
22626      * @cfg {String} validateOnBlur @hide
22627      */
22628 });
22629  
22630     // <script type="text/javascript">
22631 /*
22632  * Based on
22633  * Ext JS Library 1.1.1
22634  * Copyright(c) 2006-2007, Ext JS, LLC.
22635  *  
22636  
22637  */
22638
22639 /**
22640  * @class Roo.form.HtmlEditorToolbar1
22641  * Basic Toolbar
22642  * 
22643  * Usage:
22644  *
22645  new Roo.form.HtmlEditor({
22646     ....
22647     toolbars : [
22648         new Roo.form.HtmlEditorToolbar1({
22649             disable : { fonts: 1 , format: 1, ..., ... , ...],
22650             btns : [ .... ]
22651         })
22652     }
22653      
22654  * 
22655  * @cfg {Object} disable List of elements to disable..
22656  * @cfg {Array} btns List of additional buttons.
22657  * 
22658  * 
22659  * NEEDS Extra CSS? 
22660  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22661  */
22662  
22663 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22664 {
22665     
22666     Roo.apply(this, config);
22667     
22668     // default disabled, based on 'good practice'..
22669     this.disable = this.disable || {};
22670     Roo.applyIf(this.disable, {
22671         fontSize : true,
22672         colors : true,
22673         specialElements : true
22674     });
22675     
22676     
22677     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22678     // dont call parent... till later.
22679 }
22680
22681 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22682     
22683     tb: false,
22684     
22685     rendered: false,
22686     
22687     editor : false,
22688     editorcore : false,
22689     /**
22690      * @cfg {Object} disable  List of toolbar elements to disable
22691          
22692      */
22693     disable : false,
22694     
22695     
22696      /**
22697      * @cfg {String} createLinkText The default text for the create link prompt
22698      */
22699     createLinkText : 'Please enter the URL for the link:',
22700     /**
22701      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22702      */
22703     defaultLinkValue : 'http:/'+'/',
22704    
22705     
22706       /**
22707      * @cfg {Array} fontFamilies An array of available font families
22708      */
22709     fontFamilies : [
22710         'Arial',
22711         'Courier New',
22712         'Tahoma',
22713         'Times New Roman',
22714         'Verdana'
22715     ],
22716     
22717     specialChars : [
22718            "&#169;",
22719           "&#174;",     
22720           "&#8482;",    
22721           "&#163;" ,    
22722          // "&#8212;",    
22723           "&#8230;",    
22724           "&#247;" ,    
22725         //  "&#225;" ,     ?? a acute?
22726            "&#8364;"    , //Euro
22727        //   "&#8220;"    ,
22728         //  "&#8221;"    ,
22729         //  "&#8226;"    ,
22730           "&#176;"  //   , // degrees
22731
22732          // "&#233;"     , // e ecute
22733          // "&#250;"     , // u ecute?
22734     ],
22735     
22736     specialElements : [
22737         {
22738             text: "Insert Table",
22739             xtype: 'MenuItem',
22740             xns : Roo.Menu,
22741             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22742                 
22743         },
22744         {    
22745             text: "Insert Image",
22746             xtype: 'MenuItem',
22747             xns : Roo.Menu,
22748             ihtml : '<img src="about:blank"/>'
22749             
22750         }
22751         
22752          
22753     ],
22754     
22755     
22756     inputElements : [ 
22757             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22758             "input:submit", "input:button", "select", "textarea", "label" ],
22759     formats : [
22760         ["p"] ,  
22761         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22762         ["pre"],[ "code"], 
22763         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22764         ['div'],['span']
22765     ],
22766     
22767     cleanStyles : [
22768         "font-size"
22769     ],
22770      /**
22771      * @cfg {String} defaultFont default font to use.
22772      */
22773     defaultFont: 'tahoma',
22774    
22775     fontSelect : false,
22776     
22777     
22778     formatCombo : false,
22779     
22780     init : function(editor)
22781     {
22782         this.editor = editor;
22783         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22784         var editorcore = this.editorcore;
22785         
22786         var _t = this;
22787         
22788         var fid = editorcore.frameId;
22789         var etb = this;
22790         function btn(id, toggle, handler){
22791             var xid = fid + '-'+ id ;
22792             return {
22793                 id : xid,
22794                 cmd : id,
22795                 cls : 'x-btn-icon x-edit-'+id,
22796                 enableToggle:toggle !== false,
22797                 scope: _t, // was editor...
22798                 handler:handler||_t.relayBtnCmd,
22799                 clickEvent:'mousedown',
22800                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22801                 tabIndex:-1
22802             };
22803         }
22804         
22805         
22806         
22807         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22808         this.tb = tb;
22809          // stop form submits
22810         tb.el.on('click', function(e){
22811             e.preventDefault(); // what does this do?
22812         });
22813
22814         if(!this.disable.font) { // && !Roo.isSafari){
22815             /* why no safari for fonts 
22816             editor.fontSelect = tb.el.createChild({
22817                 tag:'select',
22818                 tabIndex: -1,
22819                 cls:'x-font-select',
22820                 html: this.createFontOptions()
22821             });
22822             
22823             editor.fontSelect.on('change', function(){
22824                 var font = editor.fontSelect.dom.value;
22825                 editor.relayCmd('fontname', font);
22826                 editor.deferFocus();
22827             }, editor);
22828             
22829             tb.add(
22830                 editor.fontSelect.dom,
22831                 '-'
22832             );
22833             */
22834             
22835         };
22836         if(!this.disable.formats){
22837             this.formatCombo = new Roo.form.ComboBox({
22838                 store: new Roo.data.SimpleStore({
22839                     id : 'tag',
22840                     fields: ['tag'],
22841                     data : this.formats // from states.js
22842                 }),
22843                 blockFocus : true,
22844                 name : '',
22845                 //autoCreate : {tag: "div",  size: "20"},
22846                 displayField:'tag',
22847                 typeAhead: false,
22848                 mode: 'local',
22849                 editable : false,
22850                 triggerAction: 'all',
22851                 emptyText:'Add tag',
22852                 selectOnFocus:true,
22853                 width:135,
22854                 listeners : {
22855                     'select': function(c, r, i) {
22856                         editorcore.insertTag(r.get('tag'));
22857                         editor.focus();
22858                     }
22859                 }
22860
22861             });
22862             tb.addField(this.formatCombo);
22863             
22864         }
22865         
22866         if(!this.disable.format){
22867             tb.add(
22868                 btn('bold'),
22869                 btn('italic'),
22870                 btn('underline'),
22871                 btn('strikethrough')
22872             );
22873         };
22874         if(!this.disable.fontSize){
22875             tb.add(
22876                 '-',
22877                 
22878                 
22879                 btn('increasefontsize', false, editorcore.adjustFont),
22880                 btn('decreasefontsize', false, editorcore.adjustFont)
22881             );
22882         };
22883         
22884         
22885         if(!this.disable.colors){
22886             tb.add(
22887                 '-', {
22888                     id:editorcore.frameId +'-forecolor',
22889                     cls:'x-btn-icon x-edit-forecolor',
22890                     clickEvent:'mousedown',
22891                     tooltip: this.buttonTips['forecolor'] || undefined,
22892                     tabIndex:-1,
22893                     menu : new Roo.menu.ColorMenu({
22894                         allowReselect: true,
22895                         focus: Roo.emptyFn,
22896                         value:'000000',
22897                         plain:true,
22898                         selectHandler: function(cp, color){
22899                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22900                             editor.deferFocus();
22901                         },
22902                         scope: editorcore,
22903                         clickEvent:'mousedown'
22904                     })
22905                 }, {
22906                     id:editorcore.frameId +'backcolor',
22907                     cls:'x-btn-icon x-edit-backcolor',
22908                     clickEvent:'mousedown',
22909                     tooltip: this.buttonTips['backcolor'] || undefined,
22910                     tabIndex:-1,
22911                     menu : new Roo.menu.ColorMenu({
22912                         focus: Roo.emptyFn,
22913                         value:'FFFFFF',
22914                         plain:true,
22915                         allowReselect: true,
22916                         selectHandler: function(cp, color){
22917                             if(Roo.isGecko){
22918                                 editorcore.execCmd('useCSS', false);
22919                                 editorcore.execCmd('hilitecolor', color);
22920                                 editorcore.execCmd('useCSS', true);
22921                                 editor.deferFocus();
22922                             }else{
22923                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22924                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22925                                 editor.deferFocus();
22926                             }
22927                         },
22928                         scope:editorcore,
22929                         clickEvent:'mousedown'
22930                     })
22931                 }
22932             );
22933         };
22934         // now add all the items...
22935         
22936
22937         if(!this.disable.alignments){
22938             tb.add(
22939                 '-',
22940                 btn('justifyleft'),
22941                 btn('justifycenter'),
22942                 btn('justifyright')
22943             );
22944         };
22945
22946         //if(!Roo.isSafari){
22947             if(!this.disable.links){
22948                 tb.add(
22949                     '-',
22950                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22951                 );
22952             };
22953
22954             if(!this.disable.lists){
22955                 tb.add(
22956                     '-',
22957                     btn('insertorderedlist'),
22958                     btn('insertunorderedlist')
22959                 );
22960             }
22961             if(!this.disable.sourceEdit){
22962                 tb.add(
22963                     '-',
22964                     btn('sourceedit', true, function(btn){
22965                         this.toggleSourceEdit(btn.pressed);
22966                     })
22967                 );
22968             }
22969         //}
22970         
22971         var smenu = { };
22972         // special menu.. - needs to be tidied up..
22973         if (!this.disable.special) {
22974             smenu = {
22975                 text: "&#169;",
22976                 cls: 'x-edit-none',
22977                 
22978                 menu : {
22979                     items : []
22980                 }
22981             };
22982             for (var i =0; i < this.specialChars.length; i++) {
22983                 smenu.menu.items.push({
22984                     
22985                     html: this.specialChars[i],
22986                     handler: function(a,b) {
22987                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
22988                         //editor.insertAtCursor(a.html);
22989                         
22990                     },
22991                     tabIndex:-1
22992                 });
22993             }
22994             
22995             
22996             tb.add(smenu);
22997             
22998             
22999         }
23000         
23001         var cmenu = { };
23002         if (!this.disable.cleanStyles) {
23003             cmenu = {
23004                 cls: 'x-btn-icon x-btn-clear',
23005                 
23006                 menu : {
23007                     items : []
23008                 }
23009             };
23010             for (var i =0; i < this.cleanStyles.length; i++) {
23011                 cmenu.menu.items.push({
23012                     actiontype : this.cleanStyles[i],
23013                     html: 'Remove ' + this.cleanStyles[i],
23014                     handler: function(a,b) {
23015 //                        Roo.log(a);
23016 //                        Roo.log(b);
23017                         var c = Roo.get(editorcore.doc.body);
23018                         c.select('[style]').each(function(s) {
23019                             s.dom.style.removeProperty(a.actiontype);
23020                         });
23021                         editorcore.syncValue();
23022                     },
23023                     tabIndex:-1
23024                 });
23025             }
23026              cmenu.menu.items.push({
23027                 actiontype : 'tablewidths',
23028                 html: 'Remove Table Widths',
23029                 handler: function(a,b) {
23030                     editorcore.cleanTableWidths();
23031                     editorcore.syncValue();
23032                 },
23033                 tabIndex:-1
23034             });
23035             cmenu.menu.items.push({
23036                 actiontype : 'word',
23037                 html: 'Remove MS Word Formating',
23038                 handler: function(a,b) {
23039                     editorcore.cleanWord();
23040                     editorcore.syncValue();
23041                 },
23042                 tabIndex:-1
23043             });
23044             
23045             cmenu.menu.items.push({
23046                 actiontype : 'all',
23047                 html: 'Remove All Styles',
23048                 handler: function(a,b) {
23049                     
23050                     var c = Roo.get(editorcore.doc.body);
23051                     c.select('[style]').each(function(s) {
23052                         s.dom.removeAttribute('style');
23053                     });
23054                     editorcore.syncValue();
23055                 },
23056                 tabIndex:-1
23057             });
23058             
23059             cmenu.menu.items.push({
23060                 actiontype : 'all',
23061                 html: 'Remove All CSS Classes',
23062                 handler: function(a,b) {
23063                     
23064                     var c = Roo.get(editorcore.doc.body);
23065                     c.select('[class]').each(function(s) {
23066                         s.dom.className = '';
23067                     });
23068                     editorcore.syncValue();
23069                 },
23070                 tabIndex:-1
23071             });
23072             
23073              cmenu.menu.items.push({
23074                 actiontype : 'tidy',
23075                 html: 'Tidy HTML Source',
23076                 handler: function(a,b) {
23077                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23078                     editorcore.syncValue();
23079                 },
23080                 tabIndex:-1
23081             });
23082             
23083             
23084             tb.add(cmenu);
23085         }
23086          
23087         if (!this.disable.specialElements) {
23088             var semenu = {
23089                 text: "Other;",
23090                 cls: 'x-edit-none',
23091                 menu : {
23092                     items : []
23093                 }
23094             };
23095             for (var i =0; i < this.specialElements.length; i++) {
23096                 semenu.menu.items.push(
23097                     Roo.apply({ 
23098                         handler: function(a,b) {
23099                             editor.insertAtCursor(this.ihtml);
23100                         }
23101                     }, this.specialElements[i])
23102                 );
23103                     
23104             }
23105             
23106             tb.add(semenu);
23107             
23108             
23109         }
23110          
23111         
23112         if (this.btns) {
23113             for(var i =0; i< this.btns.length;i++) {
23114                 var b = Roo.factory(this.btns[i],Roo.form);
23115                 b.cls =  'x-edit-none';
23116                 
23117                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23118                     b.cls += ' x-init-enable';
23119                 }
23120                 
23121                 b.scope = editorcore;
23122                 tb.add(b);
23123             }
23124         
23125         }
23126         
23127         
23128         
23129         // disable everything...
23130         
23131         this.tb.items.each(function(item){
23132             
23133            if(
23134                 item.id != editorcore.frameId+ '-sourceedit' && 
23135                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23136             ){
23137                 
23138                 item.disable();
23139             }
23140         });
23141         this.rendered = true;
23142         
23143         // the all the btns;
23144         editor.on('editorevent', this.updateToolbar, this);
23145         // other toolbars need to implement this..
23146         //editor.on('editmodechange', this.updateToolbar, this);
23147     },
23148     
23149     
23150     relayBtnCmd : function(btn) {
23151         this.editorcore.relayCmd(btn.cmd);
23152     },
23153     // private used internally
23154     createLink : function(){
23155         Roo.log("create link?");
23156         var url = prompt(this.createLinkText, this.defaultLinkValue);
23157         if(url && url != 'http:/'+'/'){
23158             this.editorcore.relayCmd('createlink', url);
23159         }
23160     },
23161
23162     
23163     /**
23164      * Protected method that will not generally be called directly. It triggers
23165      * a toolbar update by reading the markup state of the current selection in the editor.
23166      */
23167     updateToolbar: function(){
23168
23169         if(!this.editorcore.activated){
23170             this.editor.onFirstFocus();
23171             return;
23172         }
23173
23174         var btns = this.tb.items.map, 
23175             doc = this.editorcore.doc,
23176             frameId = this.editorcore.frameId;
23177
23178         if(!this.disable.font && !Roo.isSafari){
23179             /*
23180             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23181             if(name != this.fontSelect.dom.value){
23182                 this.fontSelect.dom.value = name;
23183             }
23184             */
23185         }
23186         if(!this.disable.format){
23187             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23188             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23189             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23190             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23191         }
23192         if(!this.disable.alignments){
23193             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23194             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23195             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23196         }
23197         if(!Roo.isSafari && !this.disable.lists){
23198             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23199             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23200         }
23201         
23202         var ans = this.editorcore.getAllAncestors();
23203         if (this.formatCombo) {
23204             
23205             
23206             var store = this.formatCombo.store;
23207             this.formatCombo.setValue("");
23208             for (var i =0; i < ans.length;i++) {
23209                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23210                     // select it..
23211                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23212                     break;
23213                 }
23214             }
23215         }
23216         
23217         
23218         
23219         // hides menus... - so this cant be on a menu...
23220         Roo.menu.MenuMgr.hideAll();
23221
23222         //this.editorsyncValue();
23223     },
23224    
23225     
23226     createFontOptions : function(){
23227         var buf = [], fs = this.fontFamilies, ff, lc;
23228         
23229         
23230         
23231         for(var i = 0, len = fs.length; i< len; i++){
23232             ff = fs[i];
23233             lc = ff.toLowerCase();
23234             buf.push(
23235                 '<option value="',lc,'" style="font-family:',ff,';"',
23236                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23237                     ff,
23238                 '</option>'
23239             );
23240         }
23241         return buf.join('');
23242     },
23243     
23244     toggleSourceEdit : function(sourceEditMode){
23245         
23246         Roo.log("toolbar toogle");
23247         if(sourceEditMode === undefined){
23248             sourceEditMode = !this.sourceEditMode;
23249         }
23250         this.sourceEditMode = sourceEditMode === true;
23251         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23252         // just toggle the button?
23253         if(btn.pressed !== this.sourceEditMode){
23254             btn.toggle(this.sourceEditMode);
23255             return;
23256         }
23257         
23258         if(sourceEditMode){
23259             Roo.log("disabling buttons");
23260             this.tb.items.each(function(item){
23261                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23262                     item.disable();
23263                 }
23264             });
23265           
23266         }else{
23267             Roo.log("enabling buttons");
23268             if(this.editorcore.initialized){
23269                 this.tb.items.each(function(item){
23270                     item.enable();
23271                 });
23272             }
23273             
23274         }
23275         Roo.log("calling toggole on editor");
23276         // tell the editor that it's been pressed..
23277         this.editor.toggleSourceEdit(sourceEditMode);
23278        
23279     },
23280      /**
23281      * Object collection of toolbar tooltips for the buttons in the editor. The key
23282      * is the command id associated with that button and the value is a valid QuickTips object.
23283      * For example:
23284 <pre><code>
23285 {
23286     bold : {
23287         title: 'Bold (Ctrl+B)',
23288         text: 'Make the selected text bold.',
23289         cls: 'x-html-editor-tip'
23290     },
23291     italic : {
23292         title: 'Italic (Ctrl+I)',
23293         text: 'Make the selected text italic.',
23294         cls: 'x-html-editor-tip'
23295     },
23296     ...
23297 </code></pre>
23298     * @type Object
23299      */
23300     buttonTips : {
23301         bold : {
23302             title: 'Bold (Ctrl+B)',
23303             text: 'Make the selected text bold.',
23304             cls: 'x-html-editor-tip'
23305         },
23306         italic : {
23307             title: 'Italic (Ctrl+I)',
23308             text: 'Make the selected text italic.',
23309             cls: 'x-html-editor-tip'
23310         },
23311         underline : {
23312             title: 'Underline (Ctrl+U)',
23313             text: 'Underline the selected text.',
23314             cls: 'x-html-editor-tip'
23315         },
23316         strikethrough : {
23317             title: 'Strikethrough',
23318             text: 'Strikethrough the selected text.',
23319             cls: 'x-html-editor-tip'
23320         },
23321         increasefontsize : {
23322             title: 'Grow Text',
23323             text: 'Increase the font size.',
23324             cls: 'x-html-editor-tip'
23325         },
23326         decreasefontsize : {
23327             title: 'Shrink Text',
23328             text: 'Decrease the font size.',
23329             cls: 'x-html-editor-tip'
23330         },
23331         backcolor : {
23332             title: 'Text Highlight Color',
23333             text: 'Change the background color of the selected text.',
23334             cls: 'x-html-editor-tip'
23335         },
23336         forecolor : {
23337             title: 'Font Color',
23338             text: 'Change the color of the selected text.',
23339             cls: 'x-html-editor-tip'
23340         },
23341         justifyleft : {
23342             title: 'Align Text Left',
23343             text: 'Align text to the left.',
23344             cls: 'x-html-editor-tip'
23345         },
23346         justifycenter : {
23347             title: 'Center Text',
23348             text: 'Center text in the editor.',
23349             cls: 'x-html-editor-tip'
23350         },
23351         justifyright : {
23352             title: 'Align Text Right',
23353             text: 'Align text to the right.',
23354             cls: 'x-html-editor-tip'
23355         },
23356         insertunorderedlist : {
23357             title: 'Bullet List',
23358             text: 'Start a bulleted list.',
23359             cls: 'x-html-editor-tip'
23360         },
23361         insertorderedlist : {
23362             title: 'Numbered List',
23363             text: 'Start a numbered list.',
23364             cls: 'x-html-editor-tip'
23365         },
23366         createlink : {
23367             title: 'Hyperlink',
23368             text: 'Make the selected text a hyperlink.',
23369             cls: 'x-html-editor-tip'
23370         },
23371         sourceedit : {
23372             title: 'Source Edit',
23373             text: 'Switch to source editing mode.',
23374             cls: 'x-html-editor-tip'
23375         }
23376     },
23377     // private
23378     onDestroy : function(){
23379         if(this.rendered){
23380             
23381             this.tb.items.each(function(item){
23382                 if(item.menu){
23383                     item.menu.removeAll();
23384                     if(item.menu.el){
23385                         item.menu.el.destroy();
23386                     }
23387                 }
23388                 item.destroy();
23389             });
23390              
23391         }
23392     },
23393     onFirstFocus: function() {
23394         this.tb.items.each(function(item){
23395            item.enable();
23396         });
23397     }
23398 });
23399
23400
23401
23402
23403 // <script type="text/javascript">
23404 /*
23405  * Based on
23406  * Ext JS Library 1.1.1
23407  * Copyright(c) 2006-2007, Ext JS, LLC.
23408  *  
23409  
23410  */
23411
23412  
23413 /**
23414  * @class Roo.form.HtmlEditor.ToolbarContext
23415  * Context Toolbar
23416  * 
23417  * Usage:
23418  *
23419  new Roo.form.HtmlEditor({
23420     ....
23421     toolbars : [
23422         { xtype: 'ToolbarStandard', styles : {} }
23423         { xtype: 'ToolbarContext', disable : {} }
23424     ]
23425 })
23426
23427      
23428  * 
23429  * @config : {Object} disable List of elements to disable.. (not done yet.)
23430  * @config : {Object} styles  Map of styles available.
23431  * 
23432  */
23433
23434 Roo.form.HtmlEditor.ToolbarContext = function(config)
23435 {
23436     
23437     Roo.apply(this, config);
23438     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23439     // dont call parent... till later.
23440     this.styles = this.styles || {};
23441 }
23442
23443  
23444
23445 Roo.form.HtmlEditor.ToolbarContext.types = {
23446     'IMG' : {
23447         width : {
23448             title: "Width",
23449             width: 40
23450         },
23451         height:  {
23452             title: "Height",
23453             width: 40
23454         },
23455         align: {
23456             title: "Align",
23457             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23458             width : 80
23459             
23460         },
23461         border: {
23462             title: "Border",
23463             width: 40
23464         },
23465         alt: {
23466             title: "Alt",
23467             width: 120
23468         },
23469         src : {
23470             title: "Src",
23471             width: 220
23472         }
23473         
23474     },
23475     'A' : {
23476         name : {
23477             title: "Name",
23478             width: 50
23479         },
23480         target:  {
23481             title: "Target",
23482             width: 120
23483         },
23484         href:  {
23485             title: "Href",
23486             width: 220
23487         } // border?
23488         
23489     },
23490     'TABLE' : {
23491         rows : {
23492             title: "Rows",
23493             width: 20
23494         },
23495         cols : {
23496             title: "Cols",
23497             width: 20
23498         },
23499         width : {
23500             title: "Width",
23501             width: 40
23502         },
23503         height : {
23504             title: "Height",
23505             width: 40
23506         },
23507         border : {
23508             title: "Border",
23509             width: 20
23510         }
23511     },
23512     'TD' : {
23513         width : {
23514             title: "Width",
23515             width: 40
23516         },
23517         height : {
23518             title: "Height",
23519             width: 40
23520         },   
23521         align: {
23522             title: "Align",
23523             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23524             width: 80
23525         },
23526         valign: {
23527             title: "Valign",
23528             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23529             width: 80
23530         },
23531         colspan: {
23532             title: "Colspan",
23533             width: 20
23534             
23535         },
23536          'font-family'  : {
23537             title : "Font",
23538             style : 'fontFamily',
23539             displayField: 'display',
23540             optname : 'font-family',
23541             width: 140
23542         }
23543     },
23544     'INPUT' : {
23545         name : {
23546             title: "name",
23547             width: 120
23548         },
23549         value : {
23550             title: "Value",
23551             width: 120
23552         },
23553         width : {
23554             title: "Width",
23555             width: 40
23556         }
23557     },
23558     'LABEL' : {
23559         'for' : {
23560             title: "For",
23561             width: 120
23562         }
23563     },
23564     'TEXTAREA' : {
23565           name : {
23566             title: "name",
23567             width: 120
23568         },
23569         rows : {
23570             title: "Rows",
23571             width: 20
23572         },
23573         cols : {
23574             title: "Cols",
23575             width: 20
23576         }
23577     },
23578     'SELECT' : {
23579         name : {
23580             title: "name",
23581             width: 120
23582         },
23583         selectoptions : {
23584             title: "Options",
23585             width: 200
23586         }
23587     },
23588     
23589     // should we really allow this??
23590     // should this just be 
23591     'BODY' : {
23592         title : {
23593             title: "Title",
23594             width: 200,
23595             disabled : true
23596         }
23597     },
23598     'SPAN' : {
23599         'font-family'  : {
23600             title : "Font",
23601             style : 'fontFamily',
23602             displayField: 'display',
23603             optname : 'font-family',
23604             width: 140
23605         }
23606     },
23607     'DIV' : {
23608         'font-family'  : {
23609             title : "Font",
23610             style : 'fontFamily',
23611             displayField: 'display',
23612             optname : 'font-family',
23613             width: 140
23614         }
23615     },
23616      'P' : {
23617         'font-family'  : {
23618             title : "Font",
23619             style : 'fontFamily',
23620             displayField: 'display',
23621             optname : 'font-family',
23622             width: 140
23623         }
23624     },
23625     
23626     '*' : {
23627         // empty..
23628     }
23629
23630 };
23631
23632 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23633 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23634
23635 Roo.form.HtmlEditor.ToolbarContext.options = {
23636         'font-family'  : [ 
23637                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23638                 [ 'Courier New', 'Courier New'],
23639                 [ 'Tahoma', 'Tahoma'],
23640                 [ 'Times New Roman,serif', 'Times'],
23641                 [ 'Verdana','Verdana' ]
23642         ]
23643 };
23644
23645 // fixme - these need to be configurable..
23646  
23647
23648 //Roo.form.HtmlEditor.ToolbarContext.types
23649
23650
23651 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23652     
23653     tb: false,
23654     
23655     rendered: false,
23656     
23657     editor : false,
23658     editorcore : false,
23659     /**
23660      * @cfg {Object} disable  List of toolbar elements to disable
23661          
23662      */
23663     disable : false,
23664     /**
23665      * @cfg {Object} styles List of styles 
23666      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23667      *
23668      * These must be defined in the page, so they get rendered correctly..
23669      * .headline { }
23670      * TD.underline { }
23671      * 
23672      */
23673     styles : false,
23674     
23675     options: false,
23676     
23677     toolbars : false,
23678     
23679     init : function(editor)
23680     {
23681         this.editor = editor;
23682         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23683         var editorcore = this.editorcore;
23684         
23685         var fid = editorcore.frameId;
23686         var etb = this;
23687         function btn(id, toggle, handler){
23688             var xid = fid + '-'+ id ;
23689             return {
23690                 id : xid,
23691                 cmd : id,
23692                 cls : 'x-btn-icon x-edit-'+id,
23693                 enableToggle:toggle !== false,
23694                 scope: editorcore, // was editor...
23695                 handler:handler||editorcore.relayBtnCmd,
23696                 clickEvent:'mousedown',
23697                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23698                 tabIndex:-1
23699             };
23700         }
23701         // create a new element.
23702         var wdiv = editor.wrap.createChild({
23703                 tag: 'div'
23704             }, editor.wrap.dom.firstChild.nextSibling, true);
23705         
23706         // can we do this more than once??
23707         
23708          // stop form submits
23709       
23710  
23711         // disable everything...
23712         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23713         this.toolbars = {};
23714            
23715         for (var i in  ty) {
23716           
23717             this.toolbars[i] = this.buildToolbar(ty[i],i);
23718         }
23719         this.tb = this.toolbars.BODY;
23720         this.tb.el.show();
23721         this.buildFooter();
23722         this.footer.show();
23723         editor.on('hide', function( ) { this.footer.hide() }, this);
23724         editor.on('show', function( ) { this.footer.show() }, this);
23725         
23726          
23727         this.rendered = true;
23728         
23729         // the all the btns;
23730         editor.on('editorevent', this.updateToolbar, this);
23731         // other toolbars need to implement this..
23732         //editor.on('editmodechange', this.updateToolbar, this);
23733     },
23734     
23735     
23736     
23737     /**
23738      * Protected method that will not generally be called directly. It triggers
23739      * a toolbar update by reading the markup state of the current selection in the editor.
23740      *
23741      * Note you can force an update by calling on('editorevent', scope, false)
23742      */
23743     updateToolbar: function(editor,ev,sel){
23744
23745         //Roo.log(ev);
23746         // capture mouse up - this is handy for selecting images..
23747         // perhaps should go somewhere else...
23748         if(!this.editorcore.activated){
23749              this.editor.onFirstFocus();
23750             return;
23751         }
23752         
23753         
23754         
23755         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23756         // selectNode - might want to handle IE?
23757         if (ev &&
23758             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23759             ev.target && ev.target.tagName == 'IMG') {
23760             // they have click on an image...
23761             // let's see if we can change the selection...
23762             sel = ev.target;
23763          
23764               var nodeRange = sel.ownerDocument.createRange();
23765             try {
23766                 nodeRange.selectNode(sel);
23767             } catch (e) {
23768                 nodeRange.selectNodeContents(sel);
23769             }
23770             //nodeRange.collapse(true);
23771             var s = this.editorcore.win.getSelection();
23772             s.removeAllRanges();
23773             s.addRange(nodeRange);
23774         }  
23775         
23776       
23777         var updateFooter = sel ? false : true;
23778         
23779         
23780         var ans = this.editorcore.getAllAncestors();
23781         
23782         // pick
23783         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23784         
23785         if (!sel) { 
23786             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23787             sel = sel ? sel : this.editorcore.doc.body;
23788             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23789             
23790         }
23791         // pick a menu that exists..
23792         var tn = sel.tagName.toUpperCase();
23793         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23794         
23795         tn = sel.tagName.toUpperCase();
23796         
23797         var lastSel = this.tb.selectedNode;
23798         
23799         this.tb.selectedNode = sel;
23800         
23801         // if current menu does not match..
23802         
23803         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23804                 
23805             this.tb.el.hide();
23806             ///console.log("show: " + tn);
23807             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23808             this.tb.el.show();
23809             // update name
23810             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23811             
23812             
23813             // update attributes
23814             if (this.tb.fields) {
23815                 this.tb.fields.each(function(e) {
23816                     if (e.stylename) {
23817                         e.setValue(sel.style[e.stylename]);
23818                         return;
23819                     } 
23820                    e.setValue(sel.getAttribute(e.attrname));
23821                 });
23822             }
23823             
23824             var hasStyles = false;
23825             for(var i in this.styles) {
23826                 hasStyles = true;
23827                 break;
23828             }
23829             
23830             // update styles
23831             if (hasStyles) { 
23832                 var st = this.tb.fields.item(0);
23833                 
23834                 st.store.removeAll();
23835                
23836                 
23837                 var cn = sel.className.split(/\s+/);
23838                 
23839                 var avs = [];
23840                 if (this.styles['*']) {
23841                     
23842                     Roo.each(this.styles['*'], function(v) {
23843                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23844                     });
23845                 }
23846                 if (this.styles[tn]) { 
23847                     Roo.each(this.styles[tn], function(v) {
23848                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23849                     });
23850                 }
23851                 
23852                 st.store.loadData(avs);
23853                 st.collapse();
23854                 st.setValue(cn);
23855             }
23856             // flag our selected Node.
23857             this.tb.selectedNode = sel;
23858            
23859            
23860             Roo.menu.MenuMgr.hideAll();
23861
23862         }
23863         
23864         if (!updateFooter) {
23865             //this.footDisp.dom.innerHTML = ''; 
23866             return;
23867         }
23868         // update the footer
23869         //
23870         var html = '';
23871         
23872         this.footerEls = ans.reverse();
23873         Roo.each(this.footerEls, function(a,i) {
23874             if (!a) { return; }
23875             html += html.length ? ' &gt; '  :  '';
23876             
23877             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23878             
23879         });
23880        
23881         // 
23882         var sz = this.footDisp.up('td').getSize();
23883         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23884         this.footDisp.dom.style.marginLeft = '5px';
23885         
23886         this.footDisp.dom.style.overflow = 'hidden';
23887         
23888         this.footDisp.dom.innerHTML = html;
23889             
23890         //this.editorsyncValue();
23891     },
23892      
23893     
23894    
23895        
23896     // private
23897     onDestroy : function(){
23898         if(this.rendered){
23899             
23900             this.tb.items.each(function(item){
23901                 if(item.menu){
23902                     item.menu.removeAll();
23903                     if(item.menu.el){
23904                         item.menu.el.destroy();
23905                     }
23906                 }
23907                 item.destroy();
23908             });
23909              
23910         }
23911     },
23912     onFirstFocus: function() {
23913         // need to do this for all the toolbars..
23914         this.tb.items.each(function(item){
23915            item.enable();
23916         });
23917     },
23918     buildToolbar: function(tlist, nm)
23919     {
23920         var editor = this.editor;
23921         var editorcore = this.editorcore;
23922          // create a new element.
23923         var wdiv = editor.wrap.createChild({
23924                 tag: 'div'
23925             }, editor.wrap.dom.firstChild.nextSibling, true);
23926         
23927        
23928         var tb = new Roo.Toolbar(wdiv);
23929         // add the name..
23930         
23931         tb.add(nm+ ":&nbsp;");
23932         
23933         var styles = [];
23934         for(var i in this.styles) {
23935             styles.push(i);
23936         }
23937         
23938         // styles...
23939         if (styles && styles.length) {
23940             
23941             // this needs a multi-select checkbox...
23942             tb.addField( new Roo.form.ComboBox({
23943                 store: new Roo.data.SimpleStore({
23944                     id : 'val',
23945                     fields: ['val', 'selected'],
23946                     data : [] 
23947                 }),
23948                 name : '-roo-edit-className',
23949                 attrname : 'className',
23950                 displayField: 'val',
23951                 typeAhead: false,
23952                 mode: 'local',
23953                 editable : false,
23954                 triggerAction: 'all',
23955                 emptyText:'Select Style',
23956                 selectOnFocus:true,
23957                 width: 130,
23958                 listeners : {
23959                     'select': function(c, r, i) {
23960                         // initial support only for on class per el..
23961                         tb.selectedNode.className =  r ? r.get('val') : '';
23962                         editorcore.syncValue();
23963                     }
23964                 }
23965     
23966             }));
23967         }
23968         
23969         var tbc = Roo.form.HtmlEditor.ToolbarContext;
23970         var tbops = tbc.options;
23971         
23972         for (var i in tlist) {
23973             
23974             var item = tlist[i];
23975             tb.add(item.title + ":&nbsp;");
23976             
23977             
23978             //optname == used so you can configure the options available..
23979             var opts = item.opts ? item.opts : false;
23980             if (item.optname) {
23981                 opts = tbops[item.optname];
23982            
23983             }
23984             
23985             if (opts) {
23986                 // opts == pulldown..
23987                 tb.addField( new Roo.form.ComboBox({
23988                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
23989                         id : 'val',
23990                         fields: ['val', 'display'],
23991                         data : opts  
23992                     }),
23993                     name : '-roo-edit-' + i,
23994                     attrname : i,
23995                     stylename : item.style ? item.style : false,
23996                     displayField: item.displayField ? item.displayField : 'val',
23997                     valueField :  'val',
23998                     typeAhead: false,
23999                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24000                     editable : false,
24001                     triggerAction: 'all',
24002                     emptyText:'Select',
24003                     selectOnFocus:true,
24004                     width: item.width ? item.width  : 130,
24005                     listeners : {
24006                         'select': function(c, r, i) {
24007                             if (c.stylename) {
24008                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24009                                 return;
24010                             }
24011                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24012                         }
24013                     }
24014
24015                 }));
24016                 continue;
24017                     
24018                  
24019                 
24020                 tb.addField( new Roo.form.TextField({
24021                     name: i,
24022                     width: 100,
24023                     //allowBlank:false,
24024                     value: ''
24025                 }));
24026                 continue;
24027             }
24028             tb.addField( new Roo.form.TextField({
24029                 name: '-roo-edit-' + i,
24030                 attrname : i,
24031                 
24032                 width: item.width,
24033                 //allowBlank:true,
24034                 value: '',
24035                 listeners: {
24036                     'change' : function(f, nv, ov) {
24037                         tb.selectedNode.setAttribute(f.attrname, nv);
24038                         editorcore.syncValue();
24039                     }
24040                 }
24041             }));
24042              
24043         }
24044         
24045         var _this = this;
24046         
24047         if(nm == 'BODY'){
24048             tb.addSeparator();
24049         
24050             tb.addButton( {
24051                 text: 'Stylesheets',
24052
24053                 listeners : {
24054                     click : function ()
24055                     {
24056                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24057                     }
24058                 }
24059             });
24060         }
24061         
24062         tb.addFill();
24063         tb.addButton( {
24064             text: 'Remove Tag',
24065     
24066             listeners : {
24067                 click : function ()
24068                 {
24069                     // remove
24070                     // undo does not work.
24071                      
24072                     var sn = tb.selectedNode;
24073                     
24074                     var pn = sn.parentNode;
24075                     
24076                     var stn =  sn.childNodes[0];
24077                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24078                     while (sn.childNodes.length) {
24079                         var node = sn.childNodes[0];
24080                         sn.removeChild(node);
24081                         //Roo.log(node);
24082                         pn.insertBefore(node, sn);
24083                         
24084                     }
24085                     pn.removeChild(sn);
24086                     var range = editorcore.createRange();
24087         
24088                     range.setStart(stn,0);
24089                     range.setEnd(en,0); //????
24090                     //range.selectNode(sel);
24091                     
24092                     
24093                     var selection = editorcore.getSelection();
24094                     selection.removeAllRanges();
24095                     selection.addRange(range);
24096                     
24097                     
24098                     
24099                     //_this.updateToolbar(null, null, pn);
24100                     _this.updateToolbar(null, null, null);
24101                     _this.footDisp.dom.innerHTML = ''; 
24102                 }
24103             }
24104             
24105                     
24106                 
24107             
24108         });
24109         
24110         
24111         tb.el.on('click', function(e){
24112             e.preventDefault(); // what does this do?
24113         });
24114         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24115         tb.el.hide();
24116         tb.name = nm;
24117         // dont need to disable them... as they will get hidden
24118         return tb;
24119          
24120         
24121     },
24122     buildFooter : function()
24123     {
24124         
24125         var fel = this.editor.wrap.createChild();
24126         this.footer = new Roo.Toolbar(fel);
24127         // toolbar has scrolly on left / right?
24128         var footDisp= new Roo.Toolbar.Fill();
24129         var _t = this;
24130         this.footer.add(
24131             {
24132                 text : '&lt;',
24133                 xtype: 'Button',
24134                 handler : function() {
24135                     _t.footDisp.scrollTo('left',0,true)
24136                 }
24137             }
24138         );
24139         this.footer.add( footDisp );
24140         this.footer.add( 
24141             {
24142                 text : '&gt;',
24143                 xtype: 'Button',
24144                 handler : function() {
24145                     // no animation..
24146                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24147                 }
24148             }
24149         );
24150         var fel = Roo.get(footDisp.el);
24151         fel.addClass('x-editor-context');
24152         this.footDispWrap = fel; 
24153         this.footDispWrap.overflow  = 'hidden';
24154         
24155         this.footDisp = fel.createChild();
24156         this.footDispWrap.on('click', this.onContextClick, this)
24157         
24158         
24159     },
24160     onContextClick : function (ev,dom)
24161     {
24162         ev.preventDefault();
24163         var  cn = dom.className;
24164         //Roo.log(cn);
24165         if (!cn.match(/x-ed-loc-/)) {
24166             return;
24167         }
24168         var n = cn.split('-').pop();
24169         var ans = this.footerEls;
24170         var sel = ans[n];
24171         
24172          // pick
24173         var range = this.editorcore.createRange();
24174         
24175         range.selectNodeContents(sel);
24176         //range.selectNode(sel);
24177         
24178         
24179         var selection = this.editorcore.getSelection();
24180         selection.removeAllRanges();
24181         selection.addRange(range);
24182         
24183         
24184         
24185         this.updateToolbar(null, null, sel);
24186         
24187         
24188     }
24189     
24190     
24191     
24192     
24193     
24194 });
24195
24196
24197
24198
24199
24200 /*
24201  * Based on:
24202  * Ext JS Library 1.1.1
24203  * Copyright(c) 2006-2007, Ext JS, LLC.
24204  *
24205  * Originally Released Under LGPL - original licence link has changed is not relivant.
24206  *
24207  * Fork - LGPL
24208  * <script type="text/javascript">
24209  */
24210  
24211 /**
24212  * @class Roo.form.BasicForm
24213  * @extends Roo.util.Observable
24214  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24215  * @constructor
24216  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24217  * @param {Object} config Configuration options
24218  */
24219 Roo.form.BasicForm = function(el, config){
24220     this.allItems = [];
24221     this.childForms = [];
24222     Roo.apply(this, config);
24223     /*
24224      * The Roo.form.Field items in this form.
24225      * @type MixedCollection
24226      */
24227      
24228      
24229     this.items = new Roo.util.MixedCollection(false, function(o){
24230         return o.id || (o.id = Roo.id());
24231     });
24232     this.addEvents({
24233         /**
24234          * @event beforeaction
24235          * Fires before any action is performed. Return false to cancel the action.
24236          * @param {Form} this
24237          * @param {Action} action The action to be performed
24238          */
24239         beforeaction: true,
24240         /**
24241          * @event actionfailed
24242          * Fires when an action fails.
24243          * @param {Form} this
24244          * @param {Action} action The action that failed
24245          */
24246         actionfailed : true,
24247         /**
24248          * @event actioncomplete
24249          * Fires when an action is completed.
24250          * @param {Form} this
24251          * @param {Action} action The action that completed
24252          */
24253         actioncomplete : true
24254     });
24255     if(el){
24256         this.initEl(el);
24257     }
24258     Roo.form.BasicForm.superclass.constructor.call(this);
24259 };
24260
24261 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24262     /**
24263      * @cfg {String} method
24264      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24265      */
24266     /**
24267      * @cfg {DataReader} reader
24268      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24269      * This is optional as there is built-in support for processing JSON.
24270      */
24271     /**
24272      * @cfg {DataReader} errorReader
24273      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24274      * This is completely optional as there is built-in support for processing JSON.
24275      */
24276     /**
24277      * @cfg {String} url
24278      * The URL to use for form actions if one isn't supplied in the action options.
24279      */
24280     /**
24281      * @cfg {Boolean} fileUpload
24282      * Set to true if this form is a file upload.
24283      */
24284      
24285     /**
24286      * @cfg {Object} baseParams
24287      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24288      */
24289      /**
24290      
24291     /**
24292      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24293      */
24294     timeout: 30,
24295
24296     // private
24297     activeAction : null,
24298
24299     /**
24300      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24301      * or setValues() data instead of when the form was first created.
24302      */
24303     trackResetOnLoad : false,
24304     
24305     
24306     /**
24307      * childForms - used for multi-tab forms
24308      * @type {Array}
24309      */
24310     childForms : false,
24311     
24312     /**
24313      * allItems - full list of fields.
24314      * @type {Array}
24315      */
24316     allItems : false,
24317     
24318     /**
24319      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24320      * element by passing it or its id or mask the form itself by passing in true.
24321      * @type Mixed
24322      */
24323     waitMsgTarget : false,
24324
24325     // private
24326     initEl : function(el){
24327         this.el = Roo.get(el);
24328         this.id = this.el.id || Roo.id();
24329         this.el.on('submit', this.onSubmit, this);
24330         this.el.addClass('x-form');
24331     },
24332
24333     // private
24334     onSubmit : function(e){
24335         e.stopEvent();
24336     },
24337
24338     /**
24339      * Returns true if client-side validation on the form is successful.
24340      * @return Boolean
24341      */
24342     isValid : function(){
24343         var valid = true;
24344         this.items.each(function(f){
24345            if(!f.validate()){
24346                valid = false;
24347            }
24348         });
24349         return valid;
24350     },
24351
24352     /**
24353      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24354      * @return Boolean
24355      */
24356     isDirty : function(){
24357         var dirty = false;
24358         this.items.each(function(f){
24359            if(f.isDirty()){
24360                dirty = true;
24361                return false;
24362            }
24363         });
24364         return dirty;
24365     },
24366     
24367     /**
24368      * Returns true if any fields in this form have changed since their original load. (New version)
24369      * @return Boolean
24370      */
24371     
24372     hasChanged : function()
24373     {
24374         var dirty = false;
24375         this.items.each(function(f){
24376            if(f.hasChanged()){
24377                dirty = true;
24378                return false;
24379            }
24380         });
24381         return dirty;
24382         
24383     },
24384     /**
24385      * Resets all hasChanged to 'false' -
24386      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24387      * So hasChanged storage is only to be used for this purpose
24388      * @return Boolean
24389      */
24390     resetHasChanged : function()
24391     {
24392         this.items.each(function(f){
24393            f.resetHasChanged();
24394         });
24395         
24396     },
24397     
24398     
24399     /**
24400      * Performs a predefined action (submit or load) or custom actions you define on this form.
24401      * @param {String} actionName The name of the action type
24402      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24403      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24404      * accept other config options):
24405      * <pre>
24406 Property          Type             Description
24407 ----------------  ---------------  ----------------------------------------------------------------------------------
24408 url               String           The url for the action (defaults to the form's url)
24409 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24410 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24411 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24412                                    validate the form on the client (defaults to false)
24413      * </pre>
24414      * @return {BasicForm} this
24415      */
24416     doAction : function(action, options){
24417         if(typeof action == 'string'){
24418             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24419         }
24420         if(this.fireEvent('beforeaction', this, action) !== false){
24421             this.beforeAction(action);
24422             action.run.defer(100, action);
24423         }
24424         return this;
24425     },
24426
24427     /**
24428      * Shortcut to do a submit action.
24429      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24430      * @return {BasicForm} this
24431      */
24432     submit : function(options){
24433         this.doAction('submit', options);
24434         return this;
24435     },
24436
24437     /**
24438      * Shortcut to do a load action.
24439      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24440      * @return {BasicForm} this
24441      */
24442     load : function(options){
24443         this.doAction('load', options);
24444         return this;
24445     },
24446
24447     /**
24448      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24449      * @param {Record} record The record to edit
24450      * @return {BasicForm} this
24451      */
24452     updateRecord : function(record){
24453         record.beginEdit();
24454         var fs = record.fields;
24455         fs.each(function(f){
24456             var field = this.findField(f.name);
24457             if(field){
24458                 record.set(f.name, field.getValue());
24459             }
24460         }, this);
24461         record.endEdit();
24462         return this;
24463     },
24464
24465     /**
24466      * Loads an Roo.data.Record into this form.
24467      * @param {Record} record The record to load
24468      * @return {BasicForm} this
24469      */
24470     loadRecord : function(record){
24471         this.setValues(record.data);
24472         return this;
24473     },
24474
24475     // private
24476     beforeAction : function(action){
24477         var o = action.options;
24478         
24479        
24480         if(this.waitMsgTarget === true){
24481             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24482         }else if(this.waitMsgTarget){
24483             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24484             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24485         }else {
24486             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24487         }
24488          
24489     },
24490
24491     // private
24492     afterAction : function(action, success){
24493         this.activeAction = null;
24494         var o = action.options;
24495         
24496         if(this.waitMsgTarget === true){
24497             this.el.unmask();
24498         }else if(this.waitMsgTarget){
24499             this.waitMsgTarget.unmask();
24500         }else{
24501             Roo.MessageBox.updateProgress(1);
24502             Roo.MessageBox.hide();
24503         }
24504          
24505         if(success){
24506             if(o.reset){
24507                 this.reset();
24508             }
24509             Roo.callback(o.success, o.scope, [this, action]);
24510             this.fireEvent('actioncomplete', this, action);
24511             
24512         }else{
24513             
24514             // failure condition..
24515             // we have a scenario where updates need confirming.
24516             // eg. if a locking scenario exists..
24517             // we look for { errors : { needs_confirm : true }} in the response.
24518             if (
24519                 (typeof(action.result) != 'undefined')  &&
24520                 (typeof(action.result.errors) != 'undefined')  &&
24521                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24522            ){
24523                 var _t = this;
24524                 Roo.MessageBox.confirm(
24525                     "Change requires confirmation",
24526                     action.result.errorMsg,
24527                     function(r) {
24528                         if (r != 'yes') {
24529                             return;
24530                         }
24531                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24532                     }
24533                     
24534                 );
24535                 
24536                 
24537                 
24538                 return;
24539             }
24540             
24541             Roo.callback(o.failure, o.scope, [this, action]);
24542             // show an error message if no failed handler is set..
24543             if (!this.hasListener('actionfailed')) {
24544                 Roo.MessageBox.alert("Error",
24545                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24546                         action.result.errorMsg :
24547                         "Saving Failed, please check your entries or try again"
24548                 );
24549             }
24550             
24551             this.fireEvent('actionfailed', this, action);
24552         }
24553         
24554     },
24555
24556     /**
24557      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24558      * @param {String} id The value to search for
24559      * @return Field
24560      */
24561     findField : function(id){
24562         var field = this.items.get(id);
24563         if(!field){
24564             this.items.each(function(f){
24565                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24566                     field = f;
24567                     return false;
24568                 }
24569             });
24570         }
24571         return field || null;
24572     },
24573
24574     /**
24575      * Add a secondary form to this one, 
24576      * Used to provide tabbed forms. One form is primary, with hidden values 
24577      * which mirror the elements from the other forms.
24578      * 
24579      * @param {Roo.form.Form} form to add.
24580      * 
24581      */
24582     addForm : function(form)
24583     {
24584        
24585         if (this.childForms.indexOf(form) > -1) {
24586             // already added..
24587             return;
24588         }
24589         this.childForms.push(form);
24590         var n = '';
24591         Roo.each(form.allItems, function (fe) {
24592             
24593             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24594             if (this.findField(n)) { // already added..
24595                 return;
24596             }
24597             var add = new Roo.form.Hidden({
24598                 name : n
24599             });
24600             add.render(this.el);
24601             
24602             this.add( add );
24603         }, this);
24604         
24605     },
24606     /**
24607      * Mark fields in this form invalid in bulk.
24608      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24609      * @return {BasicForm} this
24610      */
24611     markInvalid : function(errors){
24612         if(errors instanceof Array){
24613             for(var i = 0, len = errors.length; i < len; i++){
24614                 var fieldError = errors[i];
24615                 var f = this.findField(fieldError.id);
24616                 if(f){
24617                     f.markInvalid(fieldError.msg);
24618                 }
24619             }
24620         }else{
24621             var field, id;
24622             for(id in errors){
24623                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24624                     field.markInvalid(errors[id]);
24625                 }
24626             }
24627         }
24628         Roo.each(this.childForms || [], function (f) {
24629             f.markInvalid(errors);
24630         });
24631         
24632         return this;
24633     },
24634
24635     /**
24636      * Set values for fields in this form in bulk.
24637      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24638      * @return {BasicForm} this
24639      */
24640     setValues : function(values){
24641         if(values instanceof Array){ // array of objects
24642             for(var i = 0, len = values.length; i < len; i++){
24643                 var v = values[i];
24644                 var f = this.findField(v.id);
24645                 if(f){
24646                     f.setValue(v.value);
24647                     if(this.trackResetOnLoad){
24648                         f.originalValue = f.getValue();
24649                     }
24650                 }
24651             }
24652         }else{ // object hash
24653             var field, id;
24654             for(id in values){
24655                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24656                     
24657                     if (field.setFromData && 
24658                         field.valueField && 
24659                         field.displayField &&
24660                         // combos' with local stores can 
24661                         // be queried via setValue()
24662                         // to set their value..
24663                         (field.store && !field.store.isLocal)
24664                         ) {
24665                         // it's a combo
24666                         var sd = { };
24667                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24668                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24669                         field.setFromData(sd);
24670                         
24671                     } else {
24672                         field.setValue(values[id]);
24673                     }
24674                     
24675                     
24676                     if(this.trackResetOnLoad){
24677                         field.originalValue = field.getValue();
24678                     }
24679                 }
24680             }
24681         }
24682         this.resetHasChanged();
24683         
24684         
24685         Roo.each(this.childForms || [], function (f) {
24686             f.setValues(values);
24687             f.resetHasChanged();
24688         });
24689                 
24690         return this;
24691     },
24692
24693     /**
24694      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24695      * they are returned as an array.
24696      * @param {Boolean} asString
24697      * @return {Object}
24698      */
24699     getValues : function(asString){
24700         if (this.childForms) {
24701             // copy values from the child forms
24702             Roo.each(this.childForms, function (f) {
24703                 this.setValues(f.getValues());
24704             }, this);
24705         }
24706         
24707         
24708         
24709         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24710         if(asString === true){
24711             return fs;
24712         }
24713         return Roo.urlDecode(fs);
24714     },
24715     
24716     /**
24717      * Returns the fields in this form as an object with key/value pairs. 
24718      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24719      * @return {Object}
24720      */
24721     getFieldValues : function(with_hidden)
24722     {
24723         if (this.childForms) {
24724             // copy values from the child forms
24725             // should this call getFieldValues - probably not as we do not currently copy
24726             // hidden fields when we generate..
24727             Roo.each(this.childForms, function (f) {
24728                 this.setValues(f.getValues());
24729             }, this);
24730         }
24731         
24732         var ret = {};
24733         this.items.each(function(f){
24734             if (!f.getName()) {
24735                 return;
24736             }
24737             var v = f.getValue();
24738             if (f.inputType =='radio') {
24739                 if (typeof(ret[f.getName()]) == 'undefined') {
24740                     ret[f.getName()] = ''; // empty..
24741                 }
24742                 
24743                 if (!f.el.dom.checked) {
24744                     return;
24745                     
24746                 }
24747                 v = f.el.dom.value;
24748                 
24749             }
24750             
24751             // not sure if this supported any more..
24752             if ((typeof(v) == 'object') && f.getRawValue) {
24753                 v = f.getRawValue() ; // dates..
24754             }
24755             // combo boxes where name != hiddenName...
24756             if (f.name != f.getName()) {
24757                 ret[f.name] = f.getRawValue();
24758             }
24759             ret[f.getName()] = v;
24760         });
24761         
24762         return ret;
24763     },
24764
24765     /**
24766      * Clears all invalid messages in this form.
24767      * @return {BasicForm} this
24768      */
24769     clearInvalid : function(){
24770         this.items.each(function(f){
24771            f.clearInvalid();
24772         });
24773         
24774         Roo.each(this.childForms || [], function (f) {
24775             f.clearInvalid();
24776         });
24777         
24778         
24779         return this;
24780     },
24781
24782     /**
24783      * Resets this form.
24784      * @return {BasicForm} this
24785      */
24786     reset : function(){
24787         this.items.each(function(f){
24788             f.reset();
24789         });
24790         
24791         Roo.each(this.childForms || [], function (f) {
24792             f.reset();
24793         });
24794         this.resetHasChanged();
24795         
24796         return this;
24797     },
24798
24799     /**
24800      * Add Roo.form components to this form.
24801      * @param {Field} field1
24802      * @param {Field} field2 (optional)
24803      * @param {Field} etc (optional)
24804      * @return {BasicForm} this
24805      */
24806     add : function(){
24807         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24808         return this;
24809     },
24810
24811
24812     /**
24813      * Removes a field from the items collection (does NOT remove its markup).
24814      * @param {Field} field
24815      * @return {BasicForm} this
24816      */
24817     remove : function(field){
24818         this.items.remove(field);
24819         return this;
24820     },
24821
24822     /**
24823      * Looks at the fields in this form, checks them for an id attribute,
24824      * and calls applyTo on the existing dom element with that id.
24825      * @return {BasicForm} this
24826      */
24827     render : function(){
24828         this.items.each(function(f){
24829             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24830                 f.applyTo(f.id);
24831             }
24832         });
24833         return this;
24834     },
24835
24836     /**
24837      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24838      * @param {Object} values
24839      * @return {BasicForm} this
24840      */
24841     applyToFields : function(o){
24842         this.items.each(function(f){
24843            Roo.apply(f, o);
24844         });
24845         return this;
24846     },
24847
24848     /**
24849      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24850      * @param {Object} values
24851      * @return {BasicForm} this
24852      */
24853     applyIfToFields : function(o){
24854         this.items.each(function(f){
24855            Roo.applyIf(f, o);
24856         });
24857         return this;
24858     }
24859 });
24860
24861 // back compat
24862 Roo.BasicForm = Roo.form.BasicForm;/*
24863  * Based on:
24864  * Ext JS Library 1.1.1
24865  * Copyright(c) 2006-2007, Ext JS, LLC.
24866  *
24867  * Originally Released Under LGPL - original licence link has changed is not relivant.
24868  *
24869  * Fork - LGPL
24870  * <script type="text/javascript">
24871  */
24872
24873 /**
24874  * @class Roo.form.Form
24875  * @extends Roo.form.BasicForm
24876  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24877  * @constructor
24878  * @param {Object} config Configuration options
24879  */
24880 Roo.form.Form = function(config){
24881     var xitems =  [];
24882     if (config.items) {
24883         xitems = config.items;
24884         delete config.items;
24885     }
24886    
24887     
24888     Roo.form.Form.superclass.constructor.call(this, null, config);
24889     this.url = this.url || this.action;
24890     if(!this.root){
24891         this.root = new Roo.form.Layout(Roo.applyIf({
24892             id: Roo.id()
24893         }, config));
24894     }
24895     this.active = this.root;
24896     /**
24897      * Array of all the buttons that have been added to this form via {@link addButton}
24898      * @type Array
24899      */
24900     this.buttons = [];
24901     this.allItems = [];
24902     this.addEvents({
24903         /**
24904          * @event clientvalidation
24905          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24906          * @param {Form} this
24907          * @param {Boolean} valid true if the form has passed client-side validation
24908          */
24909         clientvalidation: true,
24910         /**
24911          * @event rendered
24912          * Fires when the form is rendered
24913          * @param {Roo.form.Form} form
24914          */
24915         rendered : true
24916     });
24917     
24918     if (this.progressUrl) {
24919             // push a hidden field onto the list of fields..
24920             this.addxtype( {
24921                     xns: Roo.form, 
24922                     xtype : 'Hidden', 
24923                     name : 'UPLOAD_IDENTIFIER' 
24924             });
24925         }
24926         
24927     
24928     Roo.each(xitems, this.addxtype, this);
24929     
24930     
24931     
24932 };
24933
24934 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24935     /**
24936      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24937      */
24938     /**
24939      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24940      */
24941     /**
24942      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24943      */
24944     buttonAlign:'center',
24945
24946     /**
24947      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24948      */
24949     minButtonWidth:75,
24950
24951     /**
24952      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24953      * This property cascades to child containers if not set.
24954      */
24955     labelAlign:'left',
24956
24957     /**
24958      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24959      * fires a looping event with that state. This is required to bind buttons to the valid
24960      * state using the config value formBind:true on the button.
24961      */
24962     monitorValid : false,
24963
24964     /**
24965      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24966      */
24967     monitorPoll : 200,
24968     
24969     /**
24970      * @cfg {String} progressUrl - Url to return progress data 
24971      */
24972     
24973     progressUrl : false,
24974   
24975     /**
24976      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
24977      * fields are added and the column is closed. If no fields are passed the column remains open
24978      * until end() is called.
24979      * @param {Object} config The config to pass to the column
24980      * @param {Field} field1 (optional)
24981      * @param {Field} field2 (optional)
24982      * @param {Field} etc (optional)
24983      * @return Column The column container object
24984      */
24985     column : function(c){
24986         var col = new Roo.form.Column(c);
24987         this.start(col);
24988         if(arguments.length > 1){ // duplicate code required because of Opera
24989             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
24990             this.end();
24991         }
24992         return col;
24993     },
24994
24995     /**
24996      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
24997      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
24998      * until end() is called.
24999      * @param {Object} config The config to pass to the fieldset
25000      * @param {Field} field1 (optional)
25001      * @param {Field} field2 (optional)
25002      * @param {Field} etc (optional)
25003      * @return FieldSet The fieldset container object
25004      */
25005     fieldset : function(c){
25006         var fs = new Roo.form.FieldSet(c);
25007         this.start(fs);
25008         if(arguments.length > 1){ // duplicate code required because of Opera
25009             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25010             this.end();
25011         }
25012         return fs;
25013     },
25014
25015     /**
25016      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25017      * fields are added and the container is closed. If no fields are passed the container remains open
25018      * until end() is called.
25019      * @param {Object} config The config to pass to the Layout
25020      * @param {Field} field1 (optional)
25021      * @param {Field} field2 (optional)
25022      * @param {Field} etc (optional)
25023      * @return Layout The container object
25024      */
25025     container : function(c){
25026         var l = new Roo.form.Layout(c);
25027         this.start(l);
25028         if(arguments.length > 1){ // duplicate code required because of Opera
25029             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25030             this.end();
25031         }
25032         return l;
25033     },
25034
25035     /**
25036      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25037      * @param {Object} container A Roo.form.Layout or subclass of Layout
25038      * @return {Form} this
25039      */
25040     start : function(c){
25041         // cascade label info
25042         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25043         this.active.stack.push(c);
25044         c.ownerCt = this.active;
25045         this.active = c;
25046         return this;
25047     },
25048
25049     /**
25050      * Closes the current open container
25051      * @return {Form} this
25052      */
25053     end : function(){
25054         if(this.active == this.root){
25055             return this;
25056         }
25057         this.active = this.active.ownerCt;
25058         return this;
25059     },
25060
25061     /**
25062      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25063      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25064      * as the label of the field.
25065      * @param {Field} field1
25066      * @param {Field} field2 (optional)
25067      * @param {Field} etc. (optional)
25068      * @return {Form} this
25069      */
25070     add : function(){
25071         this.active.stack.push.apply(this.active.stack, arguments);
25072         this.allItems.push.apply(this.allItems,arguments);
25073         var r = [];
25074         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25075             if(a[i].isFormField){
25076                 r.push(a[i]);
25077             }
25078         }
25079         if(r.length > 0){
25080             Roo.form.Form.superclass.add.apply(this, r);
25081         }
25082         return this;
25083     },
25084     
25085
25086     
25087     
25088     
25089      /**
25090      * Find any element that has been added to a form, using it's ID or name
25091      * This can include framesets, columns etc. along with regular fields..
25092      * @param {String} id - id or name to find.
25093      
25094      * @return {Element} e - or false if nothing found.
25095      */
25096     findbyId : function(id)
25097     {
25098         var ret = false;
25099         if (!id) {
25100             return ret;
25101         }
25102         Roo.each(this.allItems, function(f){
25103             if (f.id == id || f.name == id ){
25104                 ret = f;
25105                 return false;
25106             }
25107         });
25108         return ret;
25109     },
25110
25111     
25112     
25113     /**
25114      * Render this form into the passed container. This should only be called once!
25115      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25116      * @return {Form} this
25117      */
25118     render : function(ct)
25119     {
25120         
25121         
25122         
25123         ct = Roo.get(ct);
25124         var o = this.autoCreate || {
25125             tag: 'form',
25126             method : this.method || 'POST',
25127             id : this.id || Roo.id()
25128         };
25129         this.initEl(ct.createChild(o));
25130
25131         this.root.render(this.el);
25132         
25133        
25134              
25135         this.items.each(function(f){
25136             f.render('x-form-el-'+f.id);
25137         });
25138
25139         if(this.buttons.length > 0){
25140             // tables are required to maintain order and for correct IE layout
25141             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25142                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25143                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25144             }}, null, true);
25145             var tr = tb.getElementsByTagName('tr')[0];
25146             for(var i = 0, len = this.buttons.length; i < len; i++) {
25147                 var b = this.buttons[i];
25148                 var td = document.createElement('td');
25149                 td.className = 'x-form-btn-td';
25150                 b.render(tr.appendChild(td));
25151             }
25152         }
25153         if(this.monitorValid){ // initialize after render
25154             this.startMonitoring();
25155         }
25156         this.fireEvent('rendered', this);
25157         return this;
25158     },
25159
25160     /**
25161      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25162      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25163      * object or a valid Roo.DomHelper element config
25164      * @param {Function} handler The function called when the button is clicked
25165      * @param {Object} scope (optional) The scope of the handler function
25166      * @return {Roo.Button}
25167      */
25168     addButton : function(config, handler, scope){
25169         var bc = {
25170             handler: handler,
25171             scope: scope,
25172             minWidth: this.minButtonWidth,
25173             hideParent:true
25174         };
25175         if(typeof config == "string"){
25176             bc.text = config;
25177         }else{
25178             Roo.apply(bc, config);
25179         }
25180         var btn = new Roo.Button(null, bc);
25181         this.buttons.push(btn);
25182         return btn;
25183     },
25184
25185      /**
25186      * Adds a series of form elements (using the xtype property as the factory method.
25187      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25188      * @param {Object} config 
25189      */
25190     
25191     addxtype : function()
25192     {
25193         var ar = Array.prototype.slice.call(arguments, 0);
25194         var ret = false;
25195         for(var i = 0; i < ar.length; i++) {
25196             if (!ar[i]) {
25197                 continue; // skip -- if this happends something invalid got sent, we 
25198                 // should ignore it, as basically that interface element will not show up
25199                 // and that should be pretty obvious!!
25200             }
25201             
25202             if (Roo.form[ar[i].xtype]) {
25203                 ar[i].form = this;
25204                 var fe = Roo.factory(ar[i], Roo.form);
25205                 if (!ret) {
25206                     ret = fe;
25207                 }
25208                 fe.form = this;
25209                 if (fe.store) {
25210                     fe.store.form = this;
25211                 }
25212                 if (fe.isLayout) {  
25213                          
25214                     this.start(fe);
25215                     this.allItems.push(fe);
25216                     if (fe.items && fe.addxtype) {
25217                         fe.addxtype.apply(fe, fe.items);
25218                         delete fe.items;
25219                     }
25220                      this.end();
25221                     continue;
25222                 }
25223                 
25224                 
25225                  
25226                 this.add(fe);
25227               //  console.log('adding ' + ar[i].xtype);
25228             }
25229             if (ar[i].xtype == 'Button') {  
25230                 //console.log('adding button');
25231                 //console.log(ar[i]);
25232                 this.addButton(ar[i]);
25233                 this.allItems.push(fe);
25234                 continue;
25235             }
25236             
25237             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25238                 alert('end is not supported on xtype any more, use items');
25239             //    this.end();
25240             //    //console.log('adding end');
25241             }
25242             
25243         }
25244         return ret;
25245     },
25246     
25247     /**
25248      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25249      * option "monitorValid"
25250      */
25251     startMonitoring : function(){
25252         if(!this.bound){
25253             this.bound = true;
25254             Roo.TaskMgr.start({
25255                 run : this.bindHandler,
25256                 interval : this.monitorPoll || 200,
25257                 scope: this
25258             });
25259         }
25260     },
25261
25262     /**
25263      * Stops monitoring of the valid state of this form
25264      */
25265     stopMonitoring : function(){
25266         this.bound = false;
25267     },
25268
25269     // private
25270     bindHandler : function(){
25271         if(!this.bound){
25272             return false; // stops binding
25273         }
25274         var valid = true;
25275         this.items.each(function(f){
25276             if(!f.isValid(true)){
25277                 valid = false;
25278                 return false;
25279             }
25280         });
25281         for(var i = 0, len = this.buttons.length; i < len; i++){
25282             var btn = this.buttons[i];
25283             if(btn.formBind === true && btn.disabled === valid){
25284                 btn.setDisabled(!valid);
25285             }
25286         }
25287         this.fireEvent('clientvalidation', this, valid);
25288     }
25289     
25290     
25291     
25292     
25293     
25294     
25295     
25296     
25297 });
25298
25299
25300 // back compat
25301 Roo.Form = Roo.form.Form;
25302 /*
25303  * Based on:
25304  * Ext JS Library 1.1.1
25305  * Copyright(c) 2006-2007, Ext JS, LLC.
25306  *
25307  * Originally Released Under LGPL - original licence link has changed is not relivant.
25308  *
25309  * Fork - LGPL
25310  * <script type="text/javascript">
25311  */
25312
25313 // as we use this in bootstrap.
25314 Roo.namespace('Roo.form');
25315  /**
25316  * @class Roo.form.Action
25317  * Internal Class used to handle form actions
25318  * @constructor
25319  * @param {Roo.form.BasicForm} el The form element or its id
25320  * @param {Object} config Configuration options
25321  */
25322
25323  
25324  
25325 // define the action interface
25326 Roo.form.Action = function(form, options){
25327     this.form = form;
25328     this.options = options || {};
25329 };
25330 /**
25331  * Client Validation Failed
25332  * @const 
25333  */
25334 Roo.form.Action.CLIENT_INVALID = 'client';
25335 /**
25336  * Server Validation Failed
25337  * @const 
25338  */
25339 Roo.form.Action.SERVER_INVALID = 'server';
25340  /**
25341  * Connect to Server Failed
25342  * @const 
25343  */
25344 Roo.form.Action.CONNECT_FAILURE = 'connect';
25345 /**
25346  * Reading Data from Server Failed
25347  * @const 
25348  */
25349 Roo.form.Action.LOAD_FAILURE = 'load';
25350
25351 Roo.form.Action.prototype = {
25352     type : 'default',
25353     failureType : undefined,
25354     response : undefined,
25355     result : undefined,
25356
25357     // interface method
25358     run : function(options){
25359
25360     },
25361
25362     // interface method
25363     success : function(response){
25364
25365     },
25366
25367     // interface method
25368     handleResponse : function(response){
25369
25370     },
25371
25372     // default connection failure
25373     failure : function(response){
25374         
25375         this.response = response;
25376         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25377         this.form.afterAction(this, false);
25378     },
25379
25380     processResponse : function(response){
25381         this.response = response;
25382         if(!response.responseText){
25383             return true;
25384         }
25385         this.result = this.handleResponse(response);
25386         return this.result;
25387     },
25388
25389     // utility functions used internally
25390     getUrl : function(appendParams){
25391         var url = this.options.url || this.form.url || this.form.el.dom.action;
25392         if(appendParams){
25393             var p = this.getParams();
25394             if(p){
25395                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25396             }
25397         }
25398         return url;
25399     },
25400
25401     getMethod : function(){
25402         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25403     },
25404
25405     getParams : function(){
25406         var bp = this.form.baseParams;
25407         var p = this.options.params;
25408         if(p){
25409             if(typeof p == "object"){
25410                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25411             }else if(typeof p == 'string' && bp){
25412                 p += '&' + Roo.urlEncode(bp);
25413             }
25414         }else if(bp){
25415             p = Roo.urlEncode(bp);
25416         }
25417         return p;
25418     },
25419
25420     createCallback : function(){
25421         return {
25422             success: this.success,
25423             failure: this.failure,
25424             scope: this,
25425             timeout: (this.form.timeout*1000),
25426             upload: this.form.fileUpload ? this.success : undefined
25427         };
25428     }
25429 };
25430
25431 Roo.form.Action.Submit = function(form, options){
25432     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25433 };
25434
25435 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25436     type : 'submit',
25437
25438     haveProgress : false,
25439     uploadComplete : false,
25440     
25441     // uploadProgress indicator.
25442     uploadProgress : function()
25443     {
25444         if (!this.form.progressUrl) {
25445             return;
25446         }
25447         
25448         if (!this.haveProgress) {
25449             Roo.MessageBox.progress("Uploading", "Uploading");
25450         }
25451         if (this.uploadComplete) {
25452            Roo.MessageBox.hide();
25453            return;
25454         }
25455         
25456         this.haveProgress = true;
25457    
25458         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25459         
25460         var c = new Roo.data.Connection();
25461         c.request({
25462             url : this.form.progressUrl,
25463             params: {
25464                 id : uid
25465             },
25466             method: 'GET',
25467             success : function(req){
25468                //console.log(data);
25469                 var rdata = false;
25470                 var edata;
25471                 try  {
25472                    rdata = Roo.decode(req.responseText)
25473                 } catch (e) {
25474                     Roo.log("Invalid data from server..");
25475                     Roo.log(edata);
25476                     return;
25477                 }
25478                 if (!rdata || !rdata.success) {
25479                     Roo.log(rdata);
25480                     Roo.MessageBox.alert(Roo.encode(rdata));
25481                     return;
25482                 }
25483                 var data = rdata.data;
25484                 
25485                 if (this.uploadComplete) {
25486                    Roo.MessageBox.hide();
25487                    return;
25488                 }
25489                    
25490                 if (data){
25491                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25492                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25493                     );
25494                 }
25495                 this.uploadProgress.defer(2000,this);
25496             },
25497        
25498             failure: function(data) {
25499                 Roo.log('progress url failed ');
25500                 Roo.log(data);
25501             },
25502             scope : this
25503         });
25504            
25505     },
25506     
25507     
25508     run : function()
25509     {
25510         // run get Values on the form, so it syncs any secondary forms.
25511         this.form.getValues();
25512         
25513         var o = this.options;
25514         var method = this.getMethod();
25515         var isPost = method == 'POST';
25516         if(o.clientValidation === false || this.form.isValid()){
25517             
25518             if (this.form.progressUrl) {
25519                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25520                     (new Date() * 1) + '' + Math.random());
25521                     
25522             } 
25523             
25524             
25525             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25526                 form:this.form.el.dom,
25527                 url:this.getUrl(!isPost),
25528                 method: method,
25529                 params:isPost ? this.getParams() : null,
25530                 isUpload: this.form.fileUpload
25531             }));
25532             
25533             this.uploadProgress();
25534
25535         }else if (o.clientValidation !== false){ // client validation failed
25536             this.failureType = Roo.form.Action.CLIENT_INVALID;
25537             this.form.afterAction(this, false);
25538         }
25539     },
25540
25541     success : function(response)
25542     {
25543         this.uploadComplete= true;
25544         if (this.haveProgress) {
25545             Roo.MessageBox.hide();
25546         }
25547         
25548         
25549         var result = this.processResponse(response);
25550         if(result === true || result.success){
25551             this.form.afterAction(this, true);
25552             return;
25553         }
25554         if(result.errors){
25555             this.form.markInvalid(result.errors);
25556             this.failureType = Roo.form.Action.SERVER_INVALID;
25557         }
25558         this.form.afterAction(this, false);
25559     },
25560     failure : function(response)
25561     {
25562         this.uploadComplete= true;
25563         if (this.haveProgress) {
25564             Roo.MessageBox.hide();
25565         }
25566         
25567         this.response = response;
25568         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25569         this.form.afterAction(this, false);
25570     },
25571     
25572     handleResponse : function(response){
25573         if(this.form.errorReader){
25574             var rs = this.form.errorReader.read(response);
25575             var errors = [];
25576             if(rs.records){
25577                 for(var i = 0, len = rs.records.length; i < len; i++) {
25578                     var r = rs.records[i];
25579                     errors[i] = r.data;
25580                 }
25581             }
25582             if(errors.length < 1){
25583                 errors = null;
25584             }
25585             return {
25586                 success : rs.success,
25587                 errors : errors
25588             };
25589         }
25590         var ret = false;
25591         try {
25592             ret = Roo.decode(response.responseText);
25593         } catch (e) {
25594             ret = {
25595                 success: false,
25596                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25597                 errors : []
25598             };
25599         }
25600         return ret;
25601         
25602     }
25603 });
25604
25605
25606 Roo.form.Action.Load = function(form, options){
25607     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25608     this.reader = this.form.reader;
25609 };
25610
25611 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25612     type : 'load',
25613
25614     run : function(){
25615         
25616         Roo.Ajax.request(Roo.apply(
25617                 this.createCallback(), {
25618                     method:this.getMethod(),
25619                     url:this.getUrl(false),
25620                     params:this.getParams()
25621         }));
25622     },
25623
25624     success : function(response){
25625         
25626         var result = this.processResponse(response);
25627         if(result === true || !result.success || !result.data){
25628             this.failureType = Roo.form.Action.LOAD_FAILURE;
25629             this.form.afterAction(this, false);
25630             return;
25631         }
25632         this.form.clearInvalid();
25633         this.form.setValues(result.data);
25634         this.form.afterAction(this, true);
25635     },
25636
25637     handleResponse : function(response){
25638         if(this.form.reader){
25639             var rs = this.form.reader.read(response);
25640             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25641             return {
25642                 success : rs.success,
25643                 data : data
25644             };
25645         }
25646         return Roo.decode(response.responseText);
25647     }
25648 });
25649
25650 Roo.form.Action.ACTION_TYPES = {
25651     'load' : Roo.form.Action.Load,
25652     'submit' : Roo.form.Action.Submit
25653 };/*
25654  * Based on:
25655  * Ext JS Library 1.1.1
25656  * Copyright(c) 2006-2007, Ext JS, LLC.
25657  *
25658  * Originally Released Under LGPL - original licence link has changed is not relivant.
25659  *
25660  * Fork - LGPL
25661  * <script type="text/javascript">
25662  */
25663  
25664 /**
25665  * @class Roo.form.Layout
25666  * @extends Roo.Component
25667  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25668  * @constructor
25669  * @param {Object} config Configuration options
25670  */
25671 Roo.form.Layout = function(config){
25672     var xitems = [];
25673     if (config.items) {
25674         xitems = config.items;
25675         delete config.items;
25676     }
25677     Roo.form.Layout.superclass.constructor.call(this, config);
25678     this.stack = [];
25679     Roo.each(xitems, this.addxtype, this);
25680      
25681 };
25682
25683 Roo.extend(Roo.form.Layout, Roo.Component, {
25684     /**
25685      * @cfg {String/Object} autoCreate
25686      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25687      */
25688     /**
25689      * @cfg {String/Object/Function} style
25690      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25691      * a function which returns such a specification.
25692      */
25693     /**
25694      * @cfg {String} labelAlign
25695      * Valid values are "left," "top" and "right" (defaults to "left")
25696      */
25697     /**
25698      * @cfg {Number} labelWidth
25699      * Fixed width in pixels of all field labels (defaults to undefined)
25700      */
25701     /**
25702      * @cfg {Boolean} clear
25703      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25704      */
25705     clear : true,
25706     /**
25707      * @cfg {String} labelSeparator
25708      * The separator to use after field labels (defaults to ':')
25709      */
25710     labelSeparator : ':',
25711     /**
25712      * @cfg {Boolean} hideLabels
25713      * True to suppress the display of field labels in this layout (defaults to false)
25714      */
25715     hideLabels : false,
25716
25717     // private
25718     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25719     
25720     isLayout : true,
25721     
25722     // private
25723     onRender : function(ct, position){
25724         if(this.el){ // from markup
25725             this.el = Roo.get(this.el);
25726         }else {  // generate
25727             var cfg = this.getAutoCreate();
25728             this.el = ct.createChild(cfg, position);
25729         }
25730         if(this.style){
25731             this.el.applyStyles(this.style);
25732         }
25733         if(this.labelAlign){
25734             this.el.addClass('x-form-label-'+this.labelAlign);
25735         }
25736         if(this.hideLabels){
25737             this.labelStyle = "display:none";
25738             this.elementStyle = "padding-left:0;";
25739         }else{
25740             if(typeof this.labelWidth == 'number'){
25741                 this.labelStyle = "width:"+this.labelWidth+"px;";
25742                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25743             }
25744             if(this.labelAlign == 'top'){
25745                 this.labelStyle = "width:auto;";
25746                 this.elementStyle = "padding-left:0;";
25747             }
25748         }
25749         var stack = this.stack;
25750         var slen = stack.length;
25751         if(slen > 0){
25752             if(!this.fieldTpl){
25753                 var t = new Roo.Template(
25754                     '<div class="x-form-item {5}">',
25755                         '<label for="{0}" style="{2}">{1}{4}</label>',
25756                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25757                         '</div>',
25758                     '</div><div class="x-form-clear-left"></div>'
25759                 );
25760                 t.disableFormats = true;
25761                 t.compile();
25762                 Roo.form.Layout.prototype.fieldTpl = t;
25763             }
25764             for(var i = 0; i < slen; i++) {
25765                 if(stack[i].isFormField){
25766                     this.renderField(stack[i]);
25767                 }else{
25768                     this.renderComponent(stack[i]);
25769                 }
25770             }
25771         }
25772         if(this.clear){
25773             this.el.createChild({cls:'x-form-clear'});
25774         }
25775     },
25776
25777     // private
25778     renderField : function(f){
25779         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25780                f.id, //0
25781                f.fieldLabel, //1
25782                f.labelStyle||this.labelStyle||'', //2
25783                this.elementStyle||'', //3
25784                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25785                f.itemCls||this.itemCls||''  //5
25786        ], true).getPrevSibling());
25787     },
25788
25789     // private
25790     renderComponent : function(c){
25791         c.render(c.isLayout ? this.el : this.el.createChild());    
25792     },
25793     /**
25794      * Adds a object form elements (using the xtype property as the factory method.)
25795      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25796      * @param {Object} config 
25797      */
25798     addxtype : function(o)
25799     {
25800         // create the lement.
25801         o.form = this.form;
25802         var fe = Roo.factory(o, Roo.form);
25803         this.form.allItems.push(fe);
25804         this.stack.push(fe);
25805         
25806         if (fe.isFormField) {
25807             this.form.items.add(fe);
25808         }
25809          
25810         return fe;
25811     }
25812 });
25813
25814 /**
25815  * @class Roo.form.Column
25816  * @extends Roo.form.Layout
25817  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25818  * @constructor
25819  * @param {Object} config Configuration options
25820  */
25821 Roo.form.Column = function(config){
25822     Roo.form.Column.superclass.constructor.call(this, config);
25823 };
25824
25825 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25826     /**
25827      * @cfg {Number/String} width
25828      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25829      */
25830     /**
25831      * @cfg {String/Object} autoCreate
25832      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25833      */
25834
25835     // private
25836     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25837
25838     // private
25839     onRender : function(ct, position){
25840         Roo.form.Column.superclass.onRender.call(this, ct, position);
25841         if(this.width){
25842             this.el.setWidth(this.width);
25843         }
25844     }
25845 });
25846
25847
25848 /**
25849  * @class Roo.form.Row
25850  * @extends Roo.form.Layout
25851  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25852  * @constructor
25853  * @param {Object} config Configuration options
25854  */
25855
25856  
25857 Roo.form.Row = function(config){
25858     Roo.form.Row.superclass.constructor.call(this, config);
25859 };
25860  
25861 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25862       /**
25863      * @cfg {Number/String} width
25864      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25865      */
25866     /**
25867      * @cfg {Number/String} height
25868      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25869      */
25870     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25871     
25872     padWidth : 20,
25873     // private
25874     onRender : function(ct, position){
25875         //console.log('row render');
25876         if(!this.rowTpl){
25877             var t = new Roo.Template(
25878                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25879                     '<label for="{0}" style="{2}">{1}{4}</label>',
25880                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25881                     '</div>',
25882                 '</div>'
25883             );
25884             t.disableFormats = true;
25885             t.compile();
25886             Roo.form.Layout.prototype.rowTpl = t;
25887         }
25888         this.fieldTpl = this.rowTpl;
25889         
25890         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25891         var labelWidth = 100;
25892         
25893         if ((this.labelAlign != 'top')) {
25894             if (typeof this.labelWidth == 'number') {
25895                 labelWidth = this.labelWidth
25896             }
25897             this.padWidth =  20 + labelWidth;
25898             
25899         }
25900         
25901         Roo.form.Column.superclass.onRender.call(this, ct, position);
25902         if(this.width){
25903             this.el.setWidth(this.width);
25904         }
25905         if(this.height){
25906             this.el.setHeight(this.height);
25907         }
25908     },
25909     
25910     // private
25911     renderField : function(f){
25912         f.fieldEl = this.fieldTpl.append(this.el, [
25913                f.id, f.fieldLabel,
25914                f.labelStyle||this.labelStyle||'',
25915                this.elementStyle||'',
25916                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25917                f.itemCls||this.itemCls||'',
25918                f.width ? f.width + this.padWidth : 160 + this.padWidth
25919        ],true);
25920     }
25921 });
25922  
25923
25924 /**
25925  * @class Roo.form.FieldSet
25926  * @extends Roo.form.Layout
25927  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25928  * @constructor
25929  * @param {Object} config Configuration options
25930  */
25931 Roo.form.FieldSet = function(config){
25932     Roo.form.FieldSet.superclass.constructor.call(this, config);
25933 };
25934
25935 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25936     /**
25937      * @cfg {String} legend
25938      * The text to display as the legend for the FieldSet (defaults to '')
25939      */
25940     /**
25941      * @cfg {String/Object} autoCreate
25942      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25943      */
25944
25945     // private
25946     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25947
25948     // private
25949     onRender : function(ct, position){
25950         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25951         if(this.legend){
25952             this.setLegend(this.legend);
25953         }
25954     },
25955
25956     // private
25957     setLegend : function(text){
25958         if(this.rendered){
25959             this.el.child('legend').update(text);
25960         }
25961     }
25962 });/*
25963  * Based on:
25964  * Ext JS Library 1.1.1
25965  * Copyright(c) 2006-2007, Ext JS, LLC.
25966  *
25967  * Originally Released Under LGPL - original licence link has changed is not relivant.
25968  *
25969  * Fork - LGPL
25970  * <script type="text/javascript">
25971  */
25972 /**
25973  * @class Roo.form.VTypes
25974  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
25975  * @singleton
25976  */
25977 Roo.form.VTypes = function(){
25978     // closure these in so they are only created once.
25979     var alpha = /^[a-zA-Z_]+$/;
25980     var alphanum = /^[a-zA-Z0-9_]+$/;
25981     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
25982     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
25983
25984     // All these messages and functions are configurable
25985     return {
25986         /**
25987          * The function used to validate email addresses
25988          * @param {String} value The email address
25989          */
25990         'email' : function(v){
25991             return email.test(v);
25992         },
25993         /**
25994          * The error text to display when the email validation function returns false
25995          * @type String
25996          */
25997         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
25998         /**
25999          * The keystroke filter mask to be applied on email input
26000          * @type RegExp
26001          */
26002         'emailMask' : /[a-z0-9_\.\-@]/i,
26003
26004         /**
26005          * The function used to validate URLs
26006          * @param {String} value The URL
26007          */
26008         'url' : function(v){
26009             return url.test(v);
26010         },
26011         /**
26012          * The error text to display when the url validation function returns false
26013          * @type String
26014          */
26015         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26016         
26017         /**
26018          * The function used to validate alpha values
26019          * @param {String} value The value
26020          */
26021         'alpha' : function(v){
26022             return alpha.test(v);
26023         },
26024         /**
26025          * The error text to display when the alpha validation function returns false
26026          * @type String
26027          */
26028         'alphaText' : 'This field should only contain letters and _',
26029         /**
26030          * The keystroke filter mask to be applied on alpha input
26031          * @type RegExp
26032          */
26033         'alphaMask' : /[a-z_]/i,
26034
26035         /**
26036          * The function used to validate alphanumeric values
26037          * @param {String} value The value
26038          */
26039         'alphanum' : function(v){
26040             return alphanum.test(v);
26041         },
26042         /**
26043          * The error text to display when the alphanumeric validation function returns false
26044          * @type String
26045          */
26046         'alphanumText' : 'This field should only contain letters, numbers and _',
26047         /**
26048          * The keystroke filter mask to be applied on alphanumeric input
26049          * @type RegExp
26050          */
26051         'alphanumMask' : /[a-z0-9_]/i
26052     };
26053 }();//<script type="text/javascript">
26054
26055 /**
26056  * @class Roo.form.FCKeditor
26057  * @extends Roo.form.TextArea
26058  * Wrapper around the FCKEditor http://www.fckeditor.net
26059  * @constructor
26060  * Creates a new FCKeditor
26061  * @param {Object} config Configuration options
26062  */
26063 Roo.form.FCKeditor = function(config){
26064     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26065     this.addEvents({
26066          /**
26067          * @event editorinit
26068          * Fired when the editor is initialized - you can add extra handlers here..
26069          * @param {FCKeditor} this
26070          * @param {Object} the FCK object.
26071          */
26072         editorinit : true
26073     });
26074     
26075     
26076 };
26077 Roo.form.FCKeditor.editors = { };
26078 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26079 {
26080     //defaultAutoCreate : {
26081     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26082     //},
26083     // private
26084     /**
26085      * @cfg {Object} fck options - see fck manual for details.
26086      */
26087     fckconfig : false,
26088     
26089     /**
26090      * @cfg {Object} fck toolbar set (Basic or Default)
26091      */
26092     toolbarSet : 'Basic',
26093     /**
26094      * @cfg {Object} fck BasePath
26095      */ 
26096     basePath : '/fckeditor/',
26097     
26098     
26099     frame : false,
26100     
26101     value : '',
26102     
26103    
26104     onRender : function(ct, position)
26105     {
26106         if(!this.el){
26107             this.defaultAutoCreate = {
26108                 tag: "textarea",
26109                 style:"width:300px;height:60px;",
26110                 autocomplete: "new-password"
26111             };
26112         }
26113         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26114         /*
26115         if(this.grow){
26116             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26117             if(this.preventScrollbars){
26118                 this.el.setStyle("overflow", "hidden");
26119             }
26120             this.el.setHeight(this.growMin);
26121         }
26122         */
26123         //console.log('onrender' + this.getId() );
26124         Roo.form.FCKeditor.editors[this.getId()] = this;
26125          
26126
26127         this.replaceTextarea() ;
26128         
26129     },
26130     
26131     getEditor : function() {
26132         return this.fckEditor;
26133     },
26134     /**
26135      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26136      * @param {Mixed} value The value to set
26137      */
26138     
26139     
26140     setValue : function(value)
26141     {
26142         //console.log('setValue: ' + value);
26143         
26144         if(typeof(value) == 'undefined') { // not sure why this is happending...
26145             return;
26146         }
26147         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26148         
26149         //if(!this.el || !this.getEditor()) {
26150         //    this.value = value;
26151             //this.setValue.defer(100,this,[value]);    
26152         //    return;
26153         //} 
26154         
26155         if(!this.getEditor()) {
26156             return;
26157         }
26158         
26159         this.getEditor().SetData(value);
26160         
26161         //
26162
26163     },
26164
26165     /**
26166      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26167      * @return {Mixed} value The field value
26168      */
26169     getValue : function()
26170     {
26171         
26172         if (this.frame && this.frame.dom.style.display == 'none') {
26173             return Roo.form.FCKeditor.superclass.getValue.call(this);
26174         }
26175         
26176         if(!this.el || !this.getEditor()) {
26177            
26178            // this.getValue.defer(100,this); 
26179             return this.value;
26180         }
26181        
26182         
26183         var value=this.getEditor().GetData();
26184         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26185         return Roo.form.FCKeditor.superclass.getValue.call(this);
26186         
26187
26188     },
26189
26190     /**
26191      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26192      * @return {Mixed} value The field value
26193      */
26194     getRawValue : function()
26195     {
26196         if (this.frame && this.frame.dom.style.display == 'none') {
26197             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26198         }
26199         
26200         if(!this.el || !this.getEditor()) {
26201             //this.getRawValue.defer(100,this); 
26202             return this.value;
26203             return;
26204         }
26205         
26206         
26207         
26208         var value=this.getEditor().GetData();
26209         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26210         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26211          
26212     },
26213     
26214     setSize : function(w,h) {
26215         
26216         
26217         
26218         //if (this.frame && this.frame.dom.style.display == 'none') {
26219         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26220         //    return;
26221         //}
26222         //if(!this.el || !this.getEditor()) {
26223         //    this.setSize.defer(100,this, [w,h]); 
26224         //    return;
26225         //}
26226         
26227         
26228         
26229         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26230         
26231         this.frame.dom.setAttribute('width', w);
26232         this.frame.dom.setAttribute('height', h);
26233         this.frame.setSize(w,h);
26234         
26235     },
26236     
26237     toggleSourceEdit : function(value) {
26238         
26239       
26240          
26241         this.el.dom.style.display = value ? '' : 'none';
26242         this.frame.dom.style.display = value ?  'none' : '';
26243         
26244     },
26245     
26246     
26247     focus: function(tag)
26248     {
26249         if (this.frame.dom.style.display == 'none') {
26250             return Roo.form.FCKeditor.superclass.focus.call(this);
26251         }
26252         if(!this.el || !this.getEditor()) {
26253             this.focus.defer(100,this, [tag]); 
26254             return;
26255         }
26256         
26257         
26258         
26259         
26260         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26261         this.getEditor().Focus();
26262         if (tgs.length) {
26263             if (!this.getEditor().Selection.GetSelection()) {
26264                 this.focus.defer(100,this, [tag]); 
26265                 return;
26266             }
26267             
26268             
26269             var r = this.getEditor().EditorDocument.createRange();
26270             r.setStart(tgs[0],0);
26271             r.setEnd(tgs[0],0);
26272             this.getEditor().Selection.GetSelection().removeAllRanges();
26273             this.getEditor().Selection.GetSelection().addRange(r);
26274             this.getEditor().Focus();
26275         }
26276         
26277     },
26278     
26279     
26280     
26281     replaceTextarea : function()
26282     {
26283         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26284             return ;
26285         }
26286         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26287         //{
26288             // We must check the elements firstly using the Id and then the name.
26289         var oTextarea = document.getElementById( this.getId() );
26290         
26291         var colElementsByName = document.getElementsByName( this.getId() ) ;
26292          
26293         oTextarea.style.display = 'none' ;
26294
26295         if ( oTextarea.tabIndex ) {            
26296             this.TabIndex = oTextarea.tabIndex ;
26297         }
26298         
26299         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26300         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26301         this.frame = Roo.get(this.getId() + '___Frame')
26302     },
26303     
26304     _getConfigHtml : function()
26305     {
26306         var sConfig = '' ;
26307
26308         for ( var o in this.fckconfig ) {
26309             sConfig += sConfig.length > 0  ? '&amp;' : '';
26310             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26311         }
26312
26313         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26314     },
26315     
26316     
26317     _getIFrameHtml : function()
26318     {
26319         var sFile = 'fckeditor.html' ;
26320         /* no idea what this is about..
26321         try
26322         {
26323             if ( (/fcksource=true/i).test( window.top.location.search ) )
26324                 sFile = 'fckeditor.original.html' ;
26325         }
26326         catch (e) { 
26327         */
26328
26329         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26330         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26331         
26332         
26333         var html = '<iframe id="' + this.getId() +
26334             '___Frame" src="' + sLink +
26335             '" width="' + this.width +
26336             '" height="' + this.height + '"' +
26337             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26338             ' frameborder="0" scrolling="no"></iframe>' ;
26339
26340         return html ;
26341     },
26342     
26343     _insertHtmlBefore : function( html, element )
26344     {
26345         if ( element.insertAdjacentHTML )       {
26346             // IE
26347             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26348         } else { // Gecko
26349             var oRange = document.createRange() ;
26350             oRange.setStartBefore( element ) ;
26351             var oFragment = oRange.createContextualFragment( html );
26352             element.parentNode.insertBefore( oFragment, element ) ;
26353         }
26354     }
26355     
26356     
26357   
26358     
26359     
26360     
26361     
26362
26363 });
26364
26365 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26366
26367 function FCKeditor_OnComplete(editorInstance){
26368     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26369     f.fckEditor = editorInstance;
26370     //console.log("loaded");
26371     f.fireEvent('editorinit', f, editorInstance);
26372
26373   
26374
26375  
26376
26377
26378
26379
26380
26381
26382
26383
26384
26385
26386
26387
26388
26389
26390
26391 //<script type="text/javascript">
26392 /**
26393  * @class Roo.form.GridField
26394  * @extends Roo.form.Field
26395  * Embed a grid (or editable grid into a form)
26396  * STATUS ALPHA
26397  * 
26398  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26399  * it needs 
26400  * xgrid.store = Roo.data.Store
26401  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26402  * xgrid.store.reader = Roo.data.JsonReader 
26403  * 
26404  * 
26405  * @constructor
26406  * Creates a new GridField
26407  * @param {Object} config Configuration options
26408  */
26409 Roo.form.GridField = function(config){
26410     Roo.form.GridField.superclass.constructor.call(this, config);
26411      
26412 };
26413
26414 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26415     /**
26416      * @cfg {Number} width  - used to restrict width of grid..
26417      */
26418     width : 100,
26419     /**
26420      * @cfg {Number} height - used to restrict height of grid..
26421      */
26422     height : 50,
26423      /**
26424      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26425          * 
26426          *}
26427      */
26428     xgrid : false, 
26429     /**
26430      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26431      * {tag: "input", type: "checkbox", autocomplete: "off"})
26432      */
26433    // defaultAutoCreate : { tag: 'div' },
26434     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26435     /**
26436      * @cfg {String} addTitle Text to include for adding a title.
26437      */
26438     addTitle : false,
26439     //
26440     onResize : function(){
26441         Roo.form.Field.superclass.onResize.apply(this, arguments);
26442     },
26443
26444     initEvents : function(){
26445         // Roo.form.Checkbox.superclass.initEvents.call(this);
26446         // has no events...
26447        
26448     },
26449
26450
26451     getResizeEl : function(){
26452         return this.wrap;
26453     },
26454
26455     getPositionEl : function(){
26456         return this.wrap;
26457     },
26458
26459     // private
26460     onRender : function(ct, position){
26461         
26462         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26463         var style = this.style;
26464         delete this.style;
26465         
26466         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26467         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26468         this.viewEl = this.wrap.createChild({ tag: 'div' });
26469         if (style) {
26470             this.viewEl.applyStyles(style);
26471         }
26472         if (this.width) {
26473             this.viewEl.setWidth(this.width);
26474         }
26475         if (this.height) {
26476             this.viewEl.setHeight(this.height);
26477         }
26478         //if(this.inputValue !== undefined){
26479         //this.setValue(this.value);
26480         
26481         
26482         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26483         
26484         
26485         this.grid.render();
26486         this.grid.getDataSource().on('remove', this.refreshValue, this);
26487         this.grid.getDataSource().on('update', this.refreshValue, this);
26488         this.grid.on('afteredit', this.refreshValue, this);
26489  
26490     },
26491      
26492     
26493     /**
26494      * Sets the value of the item. 
26495      * @param {String} either an object  or a string..
26496      */
26497     setValue : function(v){
26498         //this.value = v;
26499         v = v || []; // empty set..
26500         // this does not seem smart - it really only affects memoryproxy grids..
26501         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26502             var ds = this.grid.getDataSource();
26503             // assumes a json reader..
26504             var data = {}
26505             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26506             ds.loadData( data);
26507         }
26508         // clear selection so it does not get stale.
26509         if (this.grid.sm) { 
26510             this.grid.sm.clearSelections();
26511         }
26512         
26513         Roo.form.GridField.superclass.setValue.call(this, v);
26514         this.refreshValue();
26515         // should load data in the grid really....
26516     },
26517     
26518     // private
26519     refreshValue: function() {
26520          var val = [];
26521         this.grid.getDataSource().each(function(r) {
26522             val.push(r.data);
26523         });
26524         this.el.dom.value = Roo.encode(val);
26525     }
26526     
26527      
26528     
26529     
26530 });/*
26531  * Based on:
26532  * Ext JS Library 1.1.1
26533  * Copyright(c) 2006-2007, Ext JS, LLC.
26534  *
26535  * Originally Released Under LGPL - original licence link has changed is not relivant.
26536  *
26537  * Fork - LGPL
26538  * <script type="text/javascript">
26539  */
26540 /**
26541  * @class Roo.form.DisplayField
26542  * @extends Roo.form.Field
26543  * A generic Field to display non-editable data.
26544  * @cfg {Boolean} closable (true|false) default false
26545  * @constructor
26546  * Creates a new Display Field item.
26547  * @param {Object} config Configuration options
26548  */
26549 Roo.form.DisplayField = function(config){
26550     Roo.form.DisplayField.superclass.constructor.call(this, config);
26551     
26552     this.addEvents({
26553         /**
26554          * @event close
26555          * Fires after the click the close btn
26556              * @param {Roo.form.DisplayField} this
26557              */
26558         close : true
26559     });
26560 };
26561
26562 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26563     inputType:      'hidden',
26564     allowBlank:     true,
26565     readOnly:         true,
26566     
26567  
26568     /**
26569      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26570      */
26571     focusClass : undefined,
26572     /**
26573      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26574      */
26575     fieldClass: 'x-form-field',
26576     
26577      /**
26578      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26579      */
26580     valueRenderer: undefined,
26581     
26582     width: 100,
26583     /**
26584      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26585      * {tag: "input", type: "checkbox", autocomplete: "off"})
26586      */
26587      
26588  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26589  
26590     closable : false,
26591     
26592     onResize : function(){
26593         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26594         
26595     },
26596
26597     initEvents : function(){
26598         // Roo.form.Checkbox.superclass.initEvents.call(this);
26599         // has no events...
26600         
26601         if(this.closable){
26602             this.closeEl.on('click', this.onClose, this);
26603         }
26604        
26605     },
26606
26607
26608     getResizeEl : function(){
26609         return this.wrap;
26610     },
26611
26612     getPositionEl : function(){
26613         return this.wrap;
26614     },
26615
26616     // private
26617     onRender : function(ct, position){
26618         
26619         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26620         //if(this.inputValue !== undefined){
26621         this.wrap = this.el.wrap();
26622         
26623         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26624         
26625         if(this.closable){
26626             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26627         }
26628         
26629         if (this.bodyStyle) {
26630             this.viewEl.applyStyles(this.bodyStyle);
26631         }
26632         //this.viewEl.setStyle('padding', '2px');
26633         
26634         this.setValue(this.value);
26635         
26636     },
26637 /*
26638     // private
26639     initValue : Roo.emptyFn,
26640
26641   */
26642
26643         // private
26644     onClick : function(){
26645         
26646     },
26647
26648     /**
26649      * Sets the checked state of the checkbox.
26650      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26651      */
26652     setValue : function(v){
26653         this.value = v;
26654         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26655         // this might be called before we have a dom element..
26656         if (!this.viewEl) {
26657             return;
26658         }
26659         this.viewEl.dom.innerHTML = html;
26660         Roo.form.DisplayField.superclass.setValue.call(this, v);
26661
26662     },
26663     
26664     onClose : function(e)
26665     {
26666         e.preventDefault();
26667         
26668         this.fireEvent('close', this);
26669     }
26670 });/*
26671  * 
26672  * Licence- LGPL
26673  * 
26674  */
26675
26676 /**
26677  * @class Roo.form.DayPicker
26678  * @extends Roo.form.Field
26679  * A Day picker show [M] [T] [W] ....
26680  * @constructor
26681  * Creates a new Day Picker
26682  * @param {Object} config Configuration options
26683  */
26684 Roo.form.DayPicker= function(config){
26685     Roo.form.DayPicker.superclass.constructor.call(this, config);
26686      
26687 };
26688
26689 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26690     /**
26691      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26692      */
26693     focusClass : undefined,
26694     /**
26695      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26696      */
26697     fieldClass: "x-form-field",
26698    
26699     /**
26700      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26701      * {tag: "input", type: "checkbox", autocomplete: "off"})
26702      */
26703     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26704     
26705    
26706     actionMode : 'viewEl', 
26707     //
26708     // private
26709  
26710     inputType : 'hidden',
26711     
26712      
26713     inputElement: false, // real input element?
26714     basedOn: false, // ????
26715     
26716     isFormField: true, // not sure where this is needed!!!!
26717
26718     onResize : function(){
26719         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26720         if(!this.boxLabel){
26721             this.el.alignTo(this.wrap, 'c-c');
26722         }
26723     },
26724
26725     initEvents : function(){
26726         Roo.form.Checkbox.superclass.initEvents.call(this);
26727         this.el.on("click", this.onClick,  this);
26728         this.el.on("change", this.onClick,  this);
26729     },
26730
26731
26732     getResizeEl : function(){
26733         return this.wrap;
26734     },
26735
26736     getPositionEl : function(){
26737         return this.wrap;
26738     },
26739
26740     
26741     // private
26742     onRender : function(ct, position){
26743         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26744        
26745         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26746         
26747         var r1 = '<table><tr>';
26748         var r2 = '<tr class="x-form-daypick-icons">';
26749         for (var i=0; i < 7; i++) {
26750             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26751             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26752         }
26753         
26754         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26755         viewEl.select('img').on('click', this.onClick, this);
26756         this.viewEl = viewEl;   
26757         
26758         
26759         // this will not work on Chrome!!!
26760         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26761         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26762         
26763         
26764           
26765
26766     },
26767
26768     // private
26769     initValue : Roo.emptyFn,
26770
26771     /**
26772      * Returns the checked state of the checkbox.
26773      * @return {Boolean} True if checked, else false
26774      */
26775     getValue : function(){
26776         return this.el.dom.value;
26777         
26778     },
26779
26780         // private
26781     onClick : function(e){ 
26782         //this.setChecked(!this.checked);
26783         Roo.get(e.target).toggleClass('x-menu-item-checked');
26784         this.refreshValue();
26785         //if(this.el.dom.checked != this.checked){
26786         //    this.setValue(this.el.dom.checked);
26787        // }
26788     },
26789     
26790     // private
26791     refreshValue : function()
26792     {
26793         var val = '';
26794         this.viewEl.select('img',true).each(function(e,i,n)  {
26795             val += e.is(".x-menu-item-checked") ? String(n) : '';
26796         });
26797         this.setValue(val, true);
26798     },
26799
26800     /**
26801      * Sets the checked state of the checkbox.
26802      * On is always based on a string comparison between inputValue and the param.
26803      * @param {Boolean/String} value - the value to set 
26804      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26805      */
26806     setValue : function(v,suppressEvent){
26807         if (!this.el.dom) {
26808             return;
26809         }
26810         var old = this.el.dom.value ;
26811         this.el.dom.value = v;
26812         if (suppressEvent) {
26813             return ;
26814         }
26815          
26816         // update display..
26817         this.viewEl.select('img',true).each(function(e,i,n)  {
26818             
26819             var on = e.is(".x-menu-item-checked");
26820             var newv = v.indexOf(String(n)) > -1;
26821             if (on != newv) {
26822                 e.toggleClass('x-menu-item-checked');
26823             }
26824             
26825         });
26826         
26827         
26828         this.fireEvent('change', this, v, old);
26829         
26830         
26831     },
26832    
26833     // handle setting of hidden value by some other method!!?!?
26834     setFromHidden: function()
26835     {
26836         if(!this.el){
26837             return;
26838         }
26839         //console.log("SET FROM HIDDEN");
26840         //alert('setFrom hidden');
26841         this.setValue(this.el.dom.value);
26842     },
26843     
26844     onDestroy : function()
26845     {
26846         if(this.viewEl){
26847             Roo.get(this.viewEl).remove();
26848         }
26849          
26850         Roo.form.DayPicker.superclass.onDestroy.call(this);
26851     }
26852
26853 });/*
26854  * RooJS Library 1.1.1
26855  * Copyright(c) 2008-2011  Alan Knowles
26856  *
26857  * License - LGPL
26858  */
26859  
26860
26861 /**
26862  * @class Roo.form.ComboCheck
26863  * @extends Roo.form.ComboBox
26864  * A combobox for multiple select items.
26865  *
26866  * FIXME - could do with a reset button..
26867  * 
26868  * @constructor
26869  * Create a new ComboCheck
26870  * @param {Object} config Configuration options
26871  */
26872 Roo.form.ComboCheck = function(config){
26873     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26874     // should verify some data...
26875     // like
26876     // hiddenName = required..
26877     // displayField = required
26878     // valudField == required
26879     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26880     var _t = this;
26881     Roo.each(req, function(e) {
26882         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26883             throw "Roo.form.ComboCheck : missing value for: " + e;
26884         }
26885     });
26886     
26887     
26888 };
26889
26890 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26891      
26892      
26893     editable : false,
26894      
26895     selectedClass: 'x-menu-item-checked', 
26896     
26897     // private
26898     onRender : function(ct, position){
26899         var _t = this;
26900         
26901         
26902         
26903         if(!this.tpl){
26904             var cls = 'x-combo-list';
26905
26906             
26907             this.tpl =  new Roo.Template({
26908                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26909                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26910                    '<span>{' + this.displayField + '}</span>' +
26911                     '</div>' 
26912                 
26913             });
26914         }
26915  
26916         
26917         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26918         this.view.singleSelect = false;
26919         this.view.multiSelect = true;
26920         this.view.toggleSelect = true;
26921         this.pageTb.add(new Roo.Toolbar.Fill(), {
26922             
26923             text: 'Done',
26924             handler: function()
26925             {
26926                 _t.collapse();
26927             }
26928         });
26929     },
26930     
26931     onViewOver : function(e, t){
26932         // do nothing...
26933         return;
26934         
26935     },
26936     
26937     onViewClick : function(doFocus,index){
26938         return;
26939         
26940     },
26941     select: function () {
26942         //Roo.log("SELECT CALLED");
26943     },
26944      
26945     selectByValue : function(xv, scrollIntoView){
26946         var ar = this.getValueArray();
26947         var sels = [];
26948         
26949         Roo.each(ar, function(v) {
26950             if(v === undefined || v === null){
26951                 return;
26952             }
26953             var r = this.findRecord(this.valueField, v);
26954             if(r){
26955                 sels.push(this.store.indexOf(r))
26956                 
26957             }
26958         },this);
26959         this.view.select(sels);
26960         return false;
26961     },
26962     
26963     
26964     
26965     onSelect : function(record, index){
26966        // Roo.log("onselect Called");
26967        // this is only called by the clear button now..
26968         this.view.clearSelections();
26969         this.setValue('[]');
26970         if (this.value != this.valueBefore) {
26971             this.fireEvent('change', this, this.value, this.valueBefore);
26972             this.valueBefore = this.value;
26973         }
26974     },
26975     getValueArray : function()
26976     {
26977         var ar = [] ;
26978         
26979         try {
26980             //Roo.log(this.value);
26981             if (typeof(this.value) == 'undefined') {
26982                 return [];
26983             }
26984             var ar = Roo.decode(this.value);
26985             return  ar instanceof Array ? ar : []; //?? valid?
26986             
26987         } catch(e) {
26988             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
26989             return [];
26990         }
26991          
26992     },
26993     expand : function ()
26994     {
26995         
26996         Roo.form.ComboCheck.superclass.expand.call(this);
26997         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
26998         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
26999         
27000
27001     },
27002     
27003     collapse : function(){
27004         Roo.form.ComboCheck.superclass.collapse.call(this);
27005         var sl = this.view.getSelectedIndexes();
27006         var st = this.store;
27007         var nv = [];
27008         var tv = [];
27009         var r;
27010         Roo.each(sl, function(i) {
27011             r = st.getAt(i);
27012             nv.push(r.get(this.valueField));
27013         },this);
27014         this.setValue(Roo.encode(nv));
27015         if (this.value != this.valueBefore) {
27016
27017             this.fireEvent('change', this, this.value, this.valueBefore);
27018             this.valueBefore = this.value;
27019         }
27020         
27021     },
27022     
27023     setValue : function(v){
27024         // Roo.log(v);
27025         this.value = v;
27026         
27027         var vals = this.getValueArray();
27028         var tv = [];
27029         Roo.each(vals, function(k) {
27030             var r = this.findRecord(this.valueField, k);
27031             if(r){
27032                 tv.push(r.data[this.displayField]);
27033             }else if(this.valueNotFoundText !== undefined){
27034                 tv.push( this.valueNotFoundText );
27035             }
27036         },this);
27037        // Roo.log(tv);
27038         
27039         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27040         this.hiddenField.value = v;
27041         this.value = v;
27042     }
27043     
27044 });/*
27045  * Based on:
27046  * Ext JS Library 1.1.1
27047  * Copyright(c) 2006-2007, Ext JS, LLC.
27048  *
27049  * Originally Released Under LGPL - original licence link has changed is not relivant.
27050  *
27051  * Fork - LGPL
27052  * <script type="text/javascript">
27053  */
27054  
27055 /**
27056  * @class Roo.form.Signature
27057  * @extends Roo.form.Field
27058  * Signature field.  
27059  * @constructor
27060  * 
27061  * @param {Object} config Configuration options
27062  */
27063
27064 Roo.form.Signature = function(config){
27065     Roo.form.Signature.superclass.constructor.call(this, config);
27066     
27067     this.addEvents({// not in used??
27068          /**
27069          * @event confirm
27070          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27071              * @param {Roo.form.Signature} combo This combo box
27072              */
27073         'confirm' : true,
27074         /**
27075          * @event reset
27076          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27077              * @param {Roo.form.ComboBox} combo This combo box
27078              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27079              */
27080         'reset' : true
27081     });
27082 };
27083
27084 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27085     /**
27086      * @cfg {Object} labels Label to use when rendering a form.
27087      * defaults to 
27088      * labels : { 
27089      *      clear : "Clear",
27090      *      confirm : "Confirm"
27091      *  }
27092      */
27093     labels : { 
27094         clear : "Clear",
27095         confirm : "Confirm"
27096     },
27097     /**
27098      * @cfg {Number} width The signature panel width (defaults to 300)
27099      */
27100     width: 300,
27101     /**
27102      * @cfg {Number} height The signature panel height (defaults to 100)
27103      */
27104     height : 100,
27105     /**
27106      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27107      */
27108     allowBlank : false,
27109     
27110     //private
27111     // {Object} signPanel The signature SVG panel element (defaults to {})
27112     signPanel : {},
27113     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27114     isMouseDown : false,
27115     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27116     isConfirmed : false,
27117     // {String} signatureTmp SVG mapping string (defaults to empty string)
27118     signatureTmp : '',
27119     
27120     
27121     defaultAutoCreate : { // modified by initCompnoent..
27122         tag: "input",
27123         type:"hidden"
27124     },
27125
27126     // private
27127     onRender : function(ct, position){
27128         
27129         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27130         
27131         this.wrap = this.el.wrap({
27132             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27133         });
27134         
27135         this.createToolbar(this);
27136         this.signPanel = this.wrap.createChild({
27137                 tag: 'div',
27138                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27139             }, this.el
27140         );
27141             
27142         this.svgID = Roo.id();
27143         this.svgEl = this.signPanel.createChild({
27144               xmlns : 'http://www.w3.org/2000/svg',
27145               tag : 'svg',
27146               id : this.svgID + "-svg",
27147               width: this.width,
27148               height: this.height,
27149               viewBox: '0 0 '+this.width+' '+this.height,
27150               cn : [
27151                 {
27152                     tag: "rect",
27153                     id: this.svgID + "-svg-r",
27154                     width: this.width,
27155                     height: this.height,
27156                     fill: "#ffa"
27157                 },
27158                 {
27159                     tag: "line",
27160                     id: this.svgID + "-svg-l",
27161                     x1: "0", // start
27162                     y1: (this.height*0.8), // start set the line in 80% of height
27163                     x2: this.width, // end
27164                     y2: (this.height*0.8), // end set the line in 80% of height
27165                     'stroke': "#666",
27166                     'stroke-width': "1",
27167                     'stroke-dasharray': "3",
27168                     'shape-rendering': "crispEdges",
27169                     'pointer-events': "none"
27170                 },
27171                 {
27172                     tag: "path",
27173                     id: this.svgID + "-svg-p",
27174                     'stroke': "navy",
27175                     'stroke-width': "3",
27176                     'fill': "none",
27177                     'pointer-events': 'none'
27178                 }
27179               ]
27180         });
27181         this.createSVG();
27182         this.svgBox = this.svgEl.dom.getScreenCTM();
27183     },
27184     createSVG : function(){ 
27185         var svg = this.signPanel;
27186         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27187         var t = this;
27188
27189         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27190         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27191         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27192         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27193         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27194         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27195         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27196         
27197     },
27198     isTouchEvent : function(e){
27199         return e.type.match(/^touch/);
27200     },
27201     getCoords : function (e) {
27202         var pt    = this.svgEl.dom.createSVGPoint();
27203         pt.x = e.clientX; 
27204         pt.y = e.clientY;
27205         if (this.isTouchEvent(e)) {
27206             pt.x =  e.targetTouches[0].clientX;
27207             pt.y = e.targetTouches[0].clientY;
27208         }
27209         var a = this.svgEl.dom.getScreenCTM();
27210         var b = a.inverse();
27211         var mx = pt.matrixTransform(b);
27212         return mx.x + ',' + mx.y;
27213     },
27214     //mouse event headler 
27215     down : function (e) {
27216         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27217         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27218         
27219         this.isMouseDown = true;
27220         
27221         e.preventDefault();
27222     },
27223     move : function (e) {
27224         if (this.isMouseDown) {
27225             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27226             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27227         }
27228         
27229         e.preventDefault();
27230     },
27231     up : function (e) {
27232         this.isMouseDown = false;
27233         var sp = this.signatureTmp.split(' ');
27234         
27235         if(sp.length > 1){
27236             if(!sp[sp.length-2].match(/^L/)){
27237                 sp.pop();
27238                 sp.pop();
27239                 sp.push("");
27240                 this.signatureTmp = sp.join(" ");
27241             }
27242         }
27243         if(this.getValue() != this.signatureTmp){
27244             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27245             this.isConfirmed = false;
27246         }
27247         e.preventDefault();
27248     },
27249     
27250     /**
27251      * Protected method that will not generally be called directly. It
27252      * is called when the editor creates its toolbar. Override this method if you need to
27253      * add custom toolbar buttons.
27254      * @param {HtmlEditor} editor
27255      */
27256     createToolbar : function(editor){
27257          function btn(id, toggle, handler){
27258             var xid = fid + '-'+ id ;
27259             return {
27260                 id : xid,
27261                 cmd : id,
27262                 cls : 'x-btn-icon x-edit-'+id,
27263                 enableToggle:toggle !== false,
27264                 scope: editor, // was editor...
27265                 handler:handler||editor.relayBtnCmd,
27266                 clickEvent:'mousedown',
27267                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27268                 tabIndex:-1
27269             };
27270         }
27271         
27272         
27273         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27274         this.tb = tb;
27275         this.tb.add(
27276            {
27277                 cls : ' x-signature-btn x-signature-'+id,
27278                 scope: editor, // was editor...
27279                 handler: this.reset,
27280                 clickEvent:'mousedown',
27281                 text: this.labels.clear
27282             },
27283             {
27284                  xtype : 'Fill',
27285                  xns: Roo.Toolbar
27286             }, 
27287             {
27288                 cls : '  x-signature-btn x-signature-'+id,
27289                 scope: editor, // was editor...
27290                 handler: this.confirmHandler,
27291                 clickEvent:'mousedown',
27292                 text: this.labels.confirm
27293             }
27294         );
27295     
27296     },
27297     //public
27298     /**
27299      * when user is clicked confirm then show this image.....
27300      * 
27301      * @return {String} Image Data URI
27302      */
27303     getImageDataURI : function(){
27304         var svg = this.svgEl.dom.parentNode.innerHTML;
27305         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27306         return src; 
27307     },
27308     /**
27309      * 
27310      * @return {Boolean} this.isConfirmed
27311      */
27312     getConfirmed : function(){
27313         return this.isConfirmed;
27314     },
27315     /**
27316      * 
27317      * @return {Number} this.width
27318      */
27319     getWidth : function(){
27320         return this.width;
27321     },
27322     /**
27323      * 
27324      * @return {Number} this.height
27325      */
27326     getHeight : function(){
27327         return this.height;
27328     },
27329     // private
27330     getSignature : function(){
27331         return this.signatureTmp;
27332     },
27333     // private
27334     reset : function(){
27335         this.signatureTmp = '';
27336         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27337         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27338         this.isConfirmed = false;
27339         Roo.form.Signature.superclass.reset.call(this);
27340     },
27341     setSignature : function(s){
27342         this.signatureTmp = s;
27343         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27344         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27345         this.setValue(s);
27346         this.isConfirmed = false;
27347         Roo.form.Signature.superclass.reset.call(this);
27348     }, 
27349     test : function(){
27350 //        Roo.log(this.signPanel.dom.contentWindow.up())
27351     },
27352     //private
27353     setConfirmed : function(){
27354         
27355         
27356         
27357 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27358     },
27359     // private
27360     confirmHandler : function(){
27361         if(!this.getSignature()){
27362             return;
27363         }
27364         
27365         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27366         this.setValue(this.getSignature());
27367         this.isConfirmed = true;
27368         
27369         this.fireEvent('confirm', this);
27370     },
27371     // private
27372     // Subclasses should provide the validation implementation by overriding this
27373     validateValue : function(value){
27374         if(this.allowBlank){
27375             return true;
27376         }
27377         
27378         if(this.isConfirmed){
27379             return true;
27380         }
27381         return false;
27382     }
27383 });/*
27384  * Based on:
27385  * Ext JS Library 1.1.1
27386  * Copyright(c) 2006-2007, Ext JS, LLC.
27387  *
27388  * Originally Released Under LGPL - original licence link has changed is not relivant.
27389  *
27390  * Fork - LGPL
27391  * <script type="text/javascript">
27392  */
27393  
27394
27395 /**
27396  * @class Roo.form.ComboBox
27397  * @extends Roo.form.TriggerField
27398  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27399  * @constructor
27400  * Create a new ComboBox.
27401  * @param {Object} config Configuration options
27402  */
27403 Roo.form.Select = function(config){
27404     Roo.form.Select.superclass.constructor.call(this, config);
27405      
27406 };
27407
27408 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27409     /**
27410      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27411      */
27412     /**
27413      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27414      * rendering into an Roo.Editor, defaults to false)
27415      */
27416     /**
27417      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27418      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27419      */
27420     /**
27421      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27422      */
27423     /**
27424      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27425      * the dropdown list (defaults to undefined, with no header element)
27426      */
27427
27428      /**
27429      * @cfg {String/Roo.Template} tpl The template to use to render the output
27430      */
27431      
27432     // private
27433     defaultAutoCreate : {tag: "select"  },
27434     /**
27435      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27436      */
27437     listWidth: undefined,
27438     /**
27439      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27440      * mode = 'remote' or 'text' if mode = 'local')
27441      */
27442     displayField: undefined,
27443     /**
27444      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27445      * mode = 'remote' or 'value' if mode = 'local'). 
27446      * Note: use of a valueField requires the user make a selection
27447      * in order for a value to be mapped.
27448      */
27449     valueField: undefined,
27450     
27451     
27452     /**
27453      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27454      * field's data value (defaults to the underlying DOM element's name)
27455      */
27456     hiddenName: undefined,
27457     /**
27458      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27459      */
27460     listClass: '',
27461     /**
27462      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27463      */
27464     selectedClass: 'x-combo-selected',
27465     /**
27466      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27467      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27468      * which displays a downward arrow icon).
27469      */
27470     triggerClass : 'x-form-arrow-trigger',
27471     /**
27472      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27473      */
27474     shadow:'sides',
27475     /**
27476      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27477      * anchor positions (defaults to 'tl-bl')
27478      */
27479     listAlign: 'tl-bl?',
27480     /**
27481      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27482      */
27483     maxHeight: 300,
27484     /**
27485      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27486      * query specified by the allQuery config option (defaults to 'query')
27487      */
27488     triggerAction: 'query',
27489     /**
27490      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27491      * (defaults to 4, does not apply if editable = false)
27492      */
27493     minChars : 4,
27494     /**
27495      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27496      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27497      */
27498     typeAhead: false,
27499     /**
27500      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27501      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27502      */
27503     queryDelay: 500,
27504     /**
27505      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27506      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27507      */
27508     pageSize: 0,
27509     /**
27510      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27511      * when editable = true (defaults to false)
27512      */
27513     selectOnFocus:false,
27514     /**
27515      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27516      */
27517     queryParam: 'query',
27518     /**
27519      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27520      * when mode = 'remote' (defaults to 'Loading...')
27521      */
27522     loadingText: 'Loading...',
27523     /**
27524      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27525      */
27526     resizable: false,
27527     /**
27528      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27529      */
27530     handleHeight : 8,
27531     /**
27532      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27533      * traditional select (defaults to true)
27534      */
27535     editable: true,
27536     /**
27537      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27538      */
27539     allQuery: '',
27540     /**
27541      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27542      */
27543     mode: 'remote',
27544     /**
27545      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27546      * listWidth has a higher value)
27547      */
27548     minListWidth : 70,
27549     /**
27550      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27551      * allow the user to set arbitrary text into the field (defaults to false)
27552      */
27553     forceSelection:false,
27554     /**
27555      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27556      * if typeAhead = true (defaults to 250)
27557      */
27558     typeAheadDelay : 250,
27559     /**
27560      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27561      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27562      */
27563     valueNotFoundText : undefined,
27564     
27565     /**
27566      * @cfg {String} defaultValue The value displayed after loading the store.
27567      */
27568     defaultValue: '',
27569     
27570     /**
27571      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27572      */
27573     blockFocus : false,
27574     
27575     /**
27576      * @cfg {Boolean} disableClear Disable showing of clear button.
27577      */
27578     disableClear : false,
27579     /**
27580      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27581      */
27582     alwaysQuery : false,
27583     
27584     //private
27585     addicon : false,
27586     editicon: false,
27587     
27588     // element that contains real text value.. (when hidden is used..)
27589      
27590     // private
27591     onRender : function(ct, position){
27592         Roo.form.Field.prototype.onRender.call(this, ct, position);
27593         
27594         if(this.store){
27595             this.store.on('beforeload', this.onBeforeLoad, this);
27596             this.store.on('load', this.onLoad, this);
27597             this.store.on('loadexception', this.onLoadException, this);
27598             this.store.load({});
27599         }
27600         
27601         
27602         
27603     },
27604
27605     // private
27606     initEvents : function(){
27607         //Roo.form.ComboBox.superclass.initEvents.call(this);
27608  
27609     },
27610
27611     onDestroy : function(){
27612        
27613         if(this.store){
27614             this.store.un('beforeload', this.onBeforeLoad, this);
27615             this.store.un('load', this.onLoad, this);
27616             this.store.un('loadexception', this.onLoadException, this);
27617         }
27618         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27619     },
27620
27621     // private
27622     fireKey : function(e){
27623         if(e.isNavKeyPress() && !this.list.isVisible()){
27624             this.fireEvent("specialkey", this, e);
27625         }
27626     },
27627
27628     // private
27629     onResize: function(w, h){
27630         
27631         return; 
27632     
27633         
27634     },
27635
27636     /**
27637      * Allow or prevent the user from directly editing the field text.  If false is passed,
27638      * the user will only be able to select from the items defined in the dropdown list.  This method
27639      * is the runtime equivalent of setting the 'editable' config option at config time.
27640      * @param {Boolean} value True to allow the user to directly edit the field text
27641      */
27642     setEditable : function(value){
27643          
27644     },
27645
27646     // private
27647     onBeforeLoad : function(){
27648         
27649         Roo.log("Select before load");
27650         return;
27651     
27652         this.innerList.update(this.loadingText ?
27653                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27654         //this.restrictHeight();
27655         this.selectedIndex = -1;
27656     },
27657
27658     // private
27659     onLoad : function(){
27660
27661     
27662         var dom = this.el.dom;
27663         dom.innerHTML = '';
27664          var od = dom.ownerDocument;
27665          
27666         if (this.emptyText) {
27667             var op = od.createElement('option');
27668             op.setAttribute('value', '');
27669             op.innerHTML = String.format('{0}', this.emptyText);
27670             dom.appendChild(op);
27671         }
27672         if(this.store.getCount() > 0){
27673            
27674             var vf = this.valueField;
27675             var df = this.displayField;
27676             this.store.data.each(function(r) {
27677                 // which colmsn to use... testing - cdoe / title..
27678                 var op = od.createElement('option');
27679                 op.setAttribute('value', r.data[vf]);
27680                 op.innerHTML = String.format('{0}', r.data[df]);
27681                 dom.appendChild(op);
27682             });
27683             if (typeof(this.defaultValue != 'undefined')) {
27684                 this.setValue(this.defaultValue);
27685             }
27686             
27687              
27688         }else{
27689             //this.onEmptyResults();
27690         }
27691         //this.el.focus();
27692     },
27693     // private
27694     onLoadException : function()
27695     {
27696         dom.innerHTML = '';
27697             
27698         Roo.log("Select on load exception");
27699         return;
27700     
27701         this.collapse();
27702         Roo.log(this.store.reader.jsonData);
27703         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27704             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27705         }
27706         
27707         
27708     },
27709     // private
27710     onTypeAhead : function(){
27711          
27712     },
27713
27714     // private
27715     onSelect : function(record, index){
27716         Roo.log('on select?');
27717         return;
27718         if(this.fireEvent('beforeselect', this, record, index) !== false){
27719             this.setFromData(index > -1 ? record.data : false);
27720             this.collapse();
27721             this.fireEvent('select', this, record, index);
27722         }
27723     },
27724
27725     /**
27726      * Returns the currently selected field value or empty string if no value is set.
27727      * @return {String} value The selected value
27728      */
27729     getValue : function(){
27730         var dom = this.el.dom;
27731         this.value = dom.options[dom.selectedIndex].value;
27732         return this.value;
27733         
27734     },
27735
27736     /**
27737      * Clears any text/value currently set in the field
27738      */
27739     clearValue : function(){
27740         this.value = '';
27741         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27742         
27743     },
27744
27745     /**
27746      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27747      * will be displayed in the field.  If the value does not match the data value of an existing item,
27748      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27749      * Otherwise the field will be blank (although the value will still be set).
27750      * @param {String} value The value to match
27751      */
27752     setValue : function(v){
27753         var d = this.el.dom;
27754         for (var i =0; i < d.options.length;i++) {
27755             if (v == d.options[i].value) {
27756                 d.selectedIndex = i;
27757                 this.value = v;
27758                 return;
27759             }
27760         }
27761         this.clearValue();
27762     },
27763     /**
27764      * @property {Object} the last set data for the element
27765      */
27766     
27767     lastData : false,
27768     /**
27769      * Sets the value of the field based on a object which is related to the record format for the store.
27770      * @param {Object} value the value to set as. or false on reset?
27771      */
27772     setFromData : function(o){
27773         Roo.log('setfrom data?');
27774          
27775         
27776         
27777     },
27778     // private
27779     reset : function(){
27780         this.clearValue();
27781     },
27782     // private
27783     findRecord : function(prop, value){
27784         
27785         return false;
27786     
27787         var record;
27788         if(this.store.getCount() > 0){
27789             this.store.each(function(r){
27790                 if(r.data[prop] == value){
27791                     record = r;
27792                     return false;
27793                 }
27794                 return true;
27795             });
27796         }
27797         return record;
27798     },
27799     
27800     getName: function()
27801     {
27802         // returns hidden if it's set..
27803         if (!this.rendered) {return ''};
27804         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27805         
27806     },
27807      
27808
27809     
27810
27811     // private
27812     onEmptyResults : function(){
27813         Roo.log('empty results');
27814         //this.collapse();
27815     },
27816
27817     /**
27818      * Returns true if the dropdown list is expanded, else false.
27819      */
27820     isExpanded : function(){
27821         return false;
27822     },
27823
27824     /**
27825      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27826      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27827      * @param {String} value The data value of the item to select
27828      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27829      * selected item if it is not currently in view (defaults to true)
27830      * @return {Boolean} True if the value matched an item in the list, else false
27831      */
27832     selectByValue : function(v, scrollIntoView){
27833         Roo.log('select By Value');
27834         return false;
27835     
27836         if(v !== undefined && v !== null){
27837             var r = this.findRecord(this.valueField || this.displayField, v);
27838             if(r){
27839                 this.select(this.store.indexOf(r), scrollIntoView);
27840                 return true;
27841             }
27842         }
27843         return false;
27844     },
27845
27846     /**
27847      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27848      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27849      * @param {Number} index The zero-based index of the list item to select
27850      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27851      * selected item if it is not currently in view (defaults to true)
27852      */
27853     select : function(index, scrollIntoView){
27854         Roo.log('select ');
27855         return  ;
27856         
27857         this.selectedIndex = index;
27858         this.view.select(index);
27859         if(scrollIntoView !== false){
27860             var el = this.view.getNode(index);
27861             if(el){
27862                 this.innerList.scrollChildIntoView(el, false);
27863             }
27864         }
27865     },
27866
27867       
27868
27869     // private
27870     validateBlur : function(){
27871         
27872         return;
27873         
27874     },
27875
27876     // private
27877     initQuery : function(){
27878         this.doQuery(this.getRawValue());
27879     },
27880
27881     // private
27882     doForce : function(){
27883         if(this.el.dom.value.length > 0){
27884             this.el.dom.value =
27885                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27886              
27887         }
27888     },
27889
27890     /**
27891      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27892      * query allowing the query action to be canceled if needed.
27893      * @param {String} query The SQL query to execute
27894      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27895      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27896      * saved in the current store (defaults to false)
27897      */
27898     doQuery : function(q, forceAll){
27899         
27900         Roo.log('doQuery?');
27901         if(q === undefined || q === null){
27902             q = '';
27903         }
27904         var qe = {
27905             query: q,
27906             forceAll: forceAll,
27907             combo: this,
27908             cancel:false
27909         };
27910         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27911             return false;
27912         }
27913         q = qe.query;
27914         forceAll = qe.forceAll;
27915         if(forceAll === true || (q.length >= this.minChars)){
27916             if(this.lastQuery != q || this.alwaysQuery){
27917                 this.lastQuery = q;
27918                 if(this.mode == 'local'){
27919                     this.selectedIndex = -1;
27920                     if(forceAll){
27921                         this.store.clearFilter();
27922                     }else{
27923                         this.store.filter(this.displayField, q);
27924                     }
27925                     this.onLoad();
27926                 }else{
27927                     this.store.baseParams[this.queryParam] = q;
27928                     this.store.load({
27929                         params: this.getParams(q)
27930                     });
27931                     this.expand();
27932                 }
27933             }else{
27934                 this.selectedIndex = -1;
27935                 this.onLoad();   
27936             }
27937         }
27938     },
27939
27940     // private
27941     getParams : function(q){
27942         var p = {};
27943         //p[this.queryParam] = q;
27944         if(this.pageSize){
27945             p.start = 0;
27946             p.limit = this.pageSize;
27947         }
27948         return p;
27949     },
27950
27951     /**
27952      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27953      */
27954     collapse : function(){
27955         
27956     },
27957
27958     // private
27959     collapseIf : function(e){
27960         
27961     },
27962
27963     /**
27964      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27965      */
27966     expand : function(){
27967         
27968     } ,
27969
27970     // private
27971      
27972
27973     /** 
27974     * @cfg {Boolean} grow 
27975     * @hide 
27976     */
27977     /** 
27978     * @cfg {Number} growMin 
27979     * @hide 
27980     */
27981     /** 
27982     * @cfg {Number} growMax 
27983     * @hide 
27984     */
27985     /**
27986      * @hide
27987      * @method autoSize
27988      */
27989     
27990     setWidth : function()
27991     {
27992         
27993     },
27994     getResizeEl : function(){
27995         return this.el;
27996     }
27997 });//<script type="text/javasscript">
27998  
27999
28000 /**
28001  * @class Roo.DDView
28002  * A DnD enabled version of Roo.View.
28003  * @param {Element/String} container The Element in which to create the View.
28004  * @param {String} tpl The template string used to create the markup for each element of the View
28005  * @param {Object} config The configuration properties. These include all the config options of
28006  * {@link Roo.View} plus some specific to this class.<br>
28007  * <p>
28008  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28009  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28010  * <p>
28011  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28012 .x-view-drag-insert-above {
28013         border-top:1px dotted #3366cc;
28014 }
28015 .x-view-drag-insert-below {
28016         border-bottom:1px dotted #3366cc;
28017 }
28018 </code></pre>
28019  * 
28020  */
28021  
28022 Roo.DDView = function(container, tpl, config) {
28023     Roo.DDView.superclass.constructor.apply(this, arguments);
28024     this.getEl().setStyle("outline", "0px none");
28025     this.getEl().unselectable();
28026     if (this.dragGroup) {
28027                 this.setDraggable(this.dragGroup.split(","));
28028     }
28029     if (this.dropGroup) {
28030                 this.setDroppable(this.dropGroup.split(","));
28031     }
28032     if (this.deletable) {
28033         this.setDeletable();
28034     }
28035     this.isDirtyFlag = false;
28036         this.addEvents({
28037                 "drop" : true
28038         });
28039 };
28040
28041 Roo.extend(Roo.DDView, Roo.View, {
28042 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28043 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28044 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28045 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28046
28047         isFormField: true,
28048
28049         reset: Roo.emptyFn,
28050         
28051         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28052
28053         validate: function() {
28054                 return true;
28055         },
28056         
28057         destroy: function() {
28058                 this.purgeListeners();
28059                 this.getEl.removeAllListeners();
28060                 this.getEl().remove();
28061                 if (this.dragZone) {
28062                         if (this.dragZone.destroy) {
28063                                 this.dragZone.destroy();
28064                         }
28065                 }
28066                 if (this.dropZone) {
28067                         if (this.dropZone.destroy) {
28068                                 this.dropZone.destroy();
28069                         }
28070                 }
28071         },
28072
28073 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28074         getName: function() {
28075                 return this.name;
28076         },
28077
28078 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28079         setValue: function(v) {
28080                 if (!this.store) {
28081                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28082                 }
28083                 var data = {};
28084                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28085                 this.store.proxy = new Roo.data.MemoryProxy(data);
28086                 this.store.load();
28087         },
28088
28089 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28090         getValue: function() {
28091                 var result = '(';
28092                 this.store.each(function(rec) {
28093                         result += rec.id + ',';
28094                 });
28095                 return result.substr(0, result.length - 1) + ')';
28096         },
28097         
28098         getIds: function() {
28099                 var i = 0, result = new Array(this.store.getCount());
28100                 this.store.each(function(rec) {
28101                         result[i++] = rec.id;
28102                 });
28103                 return result;
28104         },
28105         
28106         isDirty: function() {
28107                 return this.isDirtyFlag;
28108         },
28109
28110 /**
28111  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28112  *      whole Element becomes the target, and this causes the drop gesture to append.
28113  */
28114     getTargetFromEvent : function(e) {
28115                 var target = e.getTarget();
28116                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28117                 target = target.parentNode;
28118                 }
28119                 if (!target) {
28120                         target = this.el.dom.lastChild || this.el.dom;
28121                 }
28122                 return target;
28123     },
28124
28125 /**
28126  *      Create the drag data which consists of an object which has the property "ddel" as
28127  *      the drag proxy element. 
28128  */
28129     getDragData : function(e) {
28130         var target = this.findItemFromChild(e.getTarget());
28131                 if(target) {
28132                         this.handleSelection(e);
28133                         var selNodes = this.getSelectedNodes();
28134             var dragData = {
28135                 source: this,
28136                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28137                 nodes: selNodes,
28138                 records: []
28139                         };
28140                         var selectedIndices = this.getSelectedIndexes();
28141                         for (var i = 0; i < selectedIndices.length; i++) {
28142                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28143                         }
28144                         if (selNodes.length == 1) {
28145                                 dragData.ddel = target.cloneNode(true); // the div element
28146                         } else {
28147                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28148                                 div.className = 'multi-proxy';
28149                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28150                                         div.appendChild(selNodes[i].cloneNode(true));
28151                                 }
28152                                 dragData.ddel = div;
28153                         }
28154             //console.log(dragData)
28155             //console.log(dragData.ddel.innerHTML)
28156                         return dragData;
28157                 }
28158         //console.log('nodragData')
28159                 return false;
28160     },
28161     
28162 /**     Specify to which ddGroup items in this DDView may be dragged. */
28163     setDraggable: function(ddGroup) {
28164         if (ddGroup instanceof Array) {
28165                 Roo.each(ddGroup, this.setDraggable, this);
28166                 return;
28167         }
28168         if (this.dragZone) {
28169                 this.dragZone.addToGroup(ddGroup);
28170         } else {
28171                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28172                                 containerScroll: true,
28173                                 ddGroup: ddGroup 
28174
28175                         });
28176 //                      Draggability implies selection. DragZone's mousedown selects the element.
28177                         if (!this.multiSelect) { this.singleSelect = true; }
28178
28179 //                      Wire the DragZone's handlers up to methods in *this*
28180                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28181                 }
28182     },
28183
28184 /**     Specify from which ddGroup this DDView accepts drops. */
28185     setDroppable: function(ddGroup) {
28186         if (ddGroup instanceof Array) {
28187                 Roo.each(ddGroup, this.setDroppable, this);
28188                 return;
28189         }
28190         if (this.dropZone) {
28191                 this.dropZone.addToGroup(ddGroup);
28192         } else {
28193                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28194                                 containerScroll: true,
28195                                 ddGroup: ddGroup
28196                         });
28197
28198 //                      Wire the DropZone's handlers up to methods in *this*
28199                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28200                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28201                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28202                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28203                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28204                 }
28205     },
28206
28207 /**     Decide whether to drop above or below a View node. */
28208     getDropPoint : function(e, n, dd){
28209         if (n == this.el.dom) { return "above"; }
28210                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28211                 var c = t + (b - t) / 2;
28212                 var y = Roo.lib.Event.getPageY(e);
28213                 if(y <= c) {
28214                         return "above";
28215                 }else{
28216                         return "below";
28217                 }
28218     },
28219
28220     onNodeEnter : function(n, dd, e, data){
28221                 return false;
28222     },
28223     
28224     onNodeOver : function(n, dd, e, data){
28225                 var pt = this.getDropPoint(e, n, dd);
28226                 // set the insert point style on the target node
28227                 var dragElClass = this.dropNotAllowed;
28228                 if (pt) {
28229                         var targetElClass;
28230                         if (pt == "above"){
28231                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28232                                 targetElClass = "x-view-drag-insert-above";
28233                         } else {
28234                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28235                                 targetElClass = "x-view-drag-insert-below";
28236                         }
28237                         if (this.lastInsertClass != targetElClass){
28238                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28239                                 this.lastInsertClass = targetElClass;
28240                         }
28241                 }
28242                 return dragElClass;
28243         },
28244
28245     onNodeOut : function(n, dd, e, data){
28246                 this.removeDropIndicators(n);
28247     },
28248
28249     onNodeDrop : function(n, dd, e, data){
28250         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28251                 return false;
28252         }
28253         var pt = this.getDropPoint(e, n, dd);
28254                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28255                 if (pt == "below") { insertAt++; }
28256                 for (var i = 0; i < data.records.length; i++) {
28257                         var r = data.records[i];
28258                         var dup = this.store.getById(r.id);
28259                         if (dup && (dd != this.dragZone)) {
28260                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28261                         } else {
28262                                 if (data.copy) {
28263                                         this.store.insert(insertAt++, r.copy());
28264                                 } else {
28265                                         data.source.isDirtyFlag = true;
28266                                         r.store.remove(r);
28267                                         this.store.insert(insertAt++, r);
28268                                 }
28269                                 this.isDirtyFlag = true;
28270                         }
28271                 }
28272                 this.dragZone.cachedTarget = null;
28273                 return true;
28274     },
28275
28276     removeDropIndicators : function(n){
28277                 if(n){
28278                         Roo.fly(n).removeClass([
28279                                 "x-view-drag-insert-above",
28280                                 "x-view-drag-insert-below"]);
28281                         this.lastInsertClass = "_noclass";
28282                 }
28283     },
28284
28285 /**
28286  *      Utility method. Add a delete option to the DDView's context menu.
28287  *      @param {String} imageUrl The URL of the "delete" icon image.
28288  */
28289         setDeletable: function(imageUrl) {
28290                 if (!this.singleSelect && !this.multiSelect) {
28291                         this.singleSelect = true;
28292                 }
28293                 var c = this.getContextMenu();
28294                 this.contextMenu.on("itemclick", function(item) {
28295                         switch (item.id) {
28296                                 case "delete":
28297                                         this.remove(this.getSelectedIndexes());
28298                                         break;
28299                         }
28300                 }, this);
28301                 this.contextMenu.add({
28302                         icon: imageUrl,
28303                         id: "delete",
28304                         text: 'Delete'
28305                 });
28306         },
28307         
28308 /**     Return the context menu for this DDView. */
28309         getContextMenu: function() {
28310                 if (!this.contextMenu) {
28311 //                      Create the View's context menu
28312                         this.contextMenu = new Roo.menu.Menu({
28313                                 id: this.id + "-contextmenu"
28314                         });
28315                         this.el.on("contextmenu", this.showContextMenu, this);
28316                 }
28317                 return this.contextMenu;
28318         },
28319         
28320         disableContextMenu: function() {
28321                 if (this.contextMenu) {
28322                         this.el.un("contextmenu", this.showContextMenu, this);
28323                 }
28324         },
28325
28326         showContextMenu: function(e, item) {
28327         item = this.findItemFromChild(e.getTarget());
28328                 if (item) {
28329                         e.stopEvent();
28330                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28331                         this.contextMenu.showAt(e.getXY());
28332             }
28333     },
28334
28335 /**
28336  *      Remove {@link Roo.data.Record}s at the specified indices.
28337  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28338  */
28339     remove: function(selectedIndices) {
28340                 selectedIndices = [].concat(selectedIndices);
28341                 for (var i = 0; i < selectedIndices.length; i++) {
28342                         var rec = this.store.getAt(selectedIndices[i]);
28343                         this.store.remove(rec);
28344                 }
28345     },
28346
28347 /**
28348  *      Double click fires the event, but also, if this is draggable, and there is only one other
28349  *      related DropZone, it transfers the selected node.
28350  */
28351     onDblClick : function(e){
28352         var item = this.findItemFromChild(e.getTarget());
28353         if(item){
28354             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28355                 return false;
28356             }
28357             if (this.dragGroup) {
28358                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28359                     while (targets.indexOf(this.dropZone) > -1) {
28360                             targets.remove(this.dropZone);
28361                                 }
28362                     if (targets.length == 1) {
28363                                         this.dragZone.cachedTarget = null;
28364                         var el = Roo.get(targets[0].getEl());
28365                         var box = el.getBox(true);
28366                         targets[0].onNodeDrop(el.dom, {
28367                                 target: el.dom,
28368                                 xy: [box.x, box.y + box.height - 1]
28369                         }, null, this.getDragData(e));
28370                     }
28371                 }
28372         }
28373     },
28374     
28375     handleSelection: function(e) {
28376                 this.dragZone.cachedTarget = null;
28377         var item = this.findItemFromChild(e.getTarget());
28378         if (!item) {
28379                 this.clearSelections(true);
28380                 return;
28381         }
28382                 if (item && (this.multiSelect || this.singleSelect)){
28383                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28384                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28385                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28386                                 this.unselect(item);
28387                         } else {
28388                                 this.select(item, this.multiSelect && e.ctrlKey);
28389                                 this.lastSelection = item;
28390                         }
28391                 }
28392     },
28393
28394     onItemClick : function(item, index, e){
28395                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28396                         return false;
28397                 }
28398                 return true;
28399     },
28400
28401     unselect : function(nodeInfo, suppressEvent){
28402                 var node = this.getNode(nodeInfo);
28403                 if(node && this.isSelected(node)){
28404                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28405                                 Roo.fly(node).removeClass(this.selectedClass);
28406                                 this.selections.remove(node);
28407                                 if(!suppressEvent){
28408                                         this.fireEvent("selectionchange", this, this.selections);
28409                                 }
28410                         }
28411                 }
28412     }
28413 });
28414 /*
28415  * Based on:
28416  * Ext JS Library 1.1.1
28417  * Copyright(c) 2006-2007, Ext JS, LLC.
28418  *
28419  * Originally Released Under LGPL - original licence link has changed is not relivant.
28420  *
28421  * Fork - LGPL
28422  * <script type="text/javascript">
28423  */
28424  
28425 /**
28426  * @class Roo.LayoutManager
28427  * @extends Roo.util.Observable
28428  * Base class for layout managers.
28429  */
28430 Roo.LayoutManager = function(container, config){
28431     Roo.LayoutManager.superclass.constructor.call(this);
28432     this.el = Roo.get(container);
28433     // ie scrollbar fix
28434     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28435         document.body.scroll = "no";
28436     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28437         this.el.position('relative');
28438     }
28439     this.id = this.el.id;
28440     this.el.addClass("x-layout-container");
28441     /** false to disable window resize monitoring @type Boolean */
28442     this.monitorWindowResize = true;
28443     this.regions = {};
28444     this.addEvents({
28445         /**
28446          * @event layout
28447          * Fires when a layout is performed. 
28448          * @param {Roo.LayoutManager} this
28449          */
28450         "layout" : true,
28451         /**
28452          * @event regionresized
28453          * Fires when the user resizes a region. 
28454          * @param {Roo.LayoutRegion} region The resized region
28455          * @param {Number} newSize The new size (width for east/west, height for north/south)
28456          */
28457         "regionresized" : true,
28458         /**
28459          * @event regioncollapsed
28460          * Fires when a region is collapsed. 
28461          * @param {Roo.LayoutRegion} region The collapsed region
28462          */
28463         "regioncollapsed" : true,
28464         /**
28465          * @event regionexpanded
28466          * Fires when a region is expanded.  
28467          * @param {Roo.LayoutRegion} region The expanded region
28468          */
28469         "regionexpanded" : true
28470     });
28471     this.updating = false;
28472     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28473 };
28474
28475 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28476     /**
28477      * Returns true if this layout is currently being updated
28478      * @return {Boolean}
28479      */
28480     isUpdating : function(){
28481         return this.updating; 
28482     },
28483     
28484     /**
28485      * Suspend the LayoutManager from doing auto-layouts while
28486      * making multiple add or remove calls
28487      */
28488     beginUpdate : function(){
28489         this.updating = true;    
28490     },
28491     
28492     /**
28493      * Restore auto-layouts and optionally disable the manager from performing a layout
28494      * @param {Boolean} noLayout true to disable a layout update 
28495      */
28496     endUpdate : function(noLayout){
28497         this.updating = false;
28498         if(!noLayout){
28499             this.layout();
28500         }    
28501     },
28502     
28503     layout: function(){
28504         
28505     },
28506     
28507     onRegionResized : function(region, newSize){
28508         this.fireEvent("regionresized", region, newSize);
28509         this.layout();
28510     },
28511     
28512     onRegionCollapsed : function(region){
28513         this.fireEvent("regioncollapsed", region);
28514     },
28515     
28516     onRegionExpanded : function(region){
28517         this.fireEvent("regionexpanded", region);
28518     },
28519         
28520     /**
28521      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28522      * performs box-model adjustments.
28523      * @return {Object} The size as an object {width: (the width), height: (the height)}
28524      */
28525     getViewSize : function(){
28526         var size;
28527         if(this.el.dom != document.body){
28528             size = this.el.getSize();
28529         }else{
28530             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28531         }
28532         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28533         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28534         return size;
28535     },
28536     
28537     /**
28538      * Returns the Element this layout is bound to.
28539      * @return {Roo.Element}
28540      */
28541     getEl : function(){
28542         return this.el;
28543     },
28544     
28545     /**
28546      * Returns the specified region.
28547      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28548      * @return {Roo.LayoutRegion}
28549      */
28550     getRegion : function(target){
28551         return this.regions[target.toLowerCase()];
28552     },
28553     
28554     onWindowResize : function(){
28555         if(this.monitorWindowResize){
28556             this.layout();
28557         }
28558     }
28559 });/*
28560  * Based on:
28561  * Ext JS Library 1.1.1
28562  * Copyright(c) 2006-2007, Ext JS, LLC.
28563  *
28564  * Originally Released Under LGPL - original licence link has changed is not relivant.
28565  *
28566  * Fork - LGPL
28567  * <script type="text/javascript">
28568  */
28569 /**
28570  * @class Roo.BorderLayout
28571  * @extends Roo.LayoutManager
28572  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28573  * please see: <br><br>
28574  * <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>
28575  * <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>
28576  * Example:
28577  <pre><code>
28578  var layout = new Roo.BorderLayout(document.body, {
28579     north: {
28580         initialSize: 25,
28581         titlebar: false
28582     },
28583     west: {
28584         split:true,
28585         initialSize: 200,
28586         minSize: 175,
28587         maxSize: 400,
28588         titlebar: true,
28589         collapsible: true
28590     },
28591     east: {
28592         split:true,
28593         initialSize: 202,
28594         minSize: 175,
28595         maxSize: 400,
28596         titlebar: true,
28597         collapsible: true
28598     },
28599     south: {
28600         split:true,
28601         initialSize: 100,
28602         minSize: 100,
28603         maxSize: 200,
28604         titlebar: true,
28605         collapsible: true
28606     },
28607     center: {
28608         titlebar: true,
28609         autoScroll:true,
28610         resizeTabs: true,
28611         minTabWidth: 50,
28612         preferredTabWidth: 150
28613     }
28614 });
28615
28616 // shorthand
28617 var CP = Roo.ContentPanel;
28618
28619 layout.beginUpdate();
28620 layout.add("north", new CP("north", "North"));
28621 layout.add("south", new CP("south", {title: "South", closable: true}));
28622 layout.add("west", new CP("west", {title: "West"}));
28623 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28624 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28625 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28626 layout.getRegion("center").showPanel("center1");
28627 layout.endUpdate();
28628 </code></pre>
28629
28630 <b>The container the layout is rendered into can be either the body element or any other element.
28631 If it is not the body element, the container needs to either be an absolute positioned element,
28632 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28633 the container size if it is not the body element.</b>
28634
28635 * @constructor
28636 * Create a new BorderLayout
28637 * @param {String/HTMLElement/Element} container The container this layout is bound to
28638 * @param {Object} config Configuration options
28639  */
28640 Roo.BorderLayout = function(container, config){
28641     config = config || {};
28642     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28643     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28644     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28645         var target = this.factory.validRegions[i];
28646         if(config[target]){
28647             this.addRegion(target, config[target]);
28648         }
28649     }
28650 };
28651
28652 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28653     /**
28654      * Creates and adds a new region if it doesn't already exist.
28655      * @param {String} target The target region key (north, south, east, west or center).
28656      * @param {Object} config The regions config object
28657      * @return {BorderLayoutRegion} The new region
28658      */
28659     addRegion : function(target, config){
28660         if(!this.regions[target]){
28661             var r = this.factory.create(target, this, config);
28662             this.bindRegion(target, r);
28663         }
28664         return this.regions[target];
28665     },
28666
28667     // private (kinda)
28668     bindRegion : function(name, r){
28669         this.regions[name] = r;
28670         r.on("visibilitychange", this.layout, this);
28671         r.on("paneladded", this.layout, this);
28672         r.on("panelremoved", this.layout, this);
28673         r.on("invalidated", this.layout, this);
28674         r.on("resized", this.onRegionResized, this);
28675         r.on("collapsed", this.onRegionCollapsed, this);
28676         r.on("expanded", this.onRegionExpanded, this);
28677     },
28678
28679     /**
28680      * Performs a layout update.
28681      */
28682     layout : function(){
28683         if(this.updating) {
28684             return;
28685         }
28686         var size = this.getViewSize();
28687         var w = size.width;
28688         var h = size.height;
28689         var centerW = w;
28690         var centerH = h;
28691         var centerY = 0;
28692         var centerX = 0;
28693         //var x = 0, y = 0;
28694
28695         var rs = this.regions;
28696         var north = rs["north"];
28697         var south = rs["south"]; 
28698         var west = rs["west"];
28699         var east = rs["east"];
28700         var center = rs["center"];
28701         //if(this.hideOnLayout){ // not supported anymore
28702             //c.el.setStyle("display", "none");
28703         //}
28704         if(north && north.isVisible()){
28705             var b = north.getBox();
28706             var m = north.getMargins();
28707             b.width = w - (m.left+m.right);
28708             b.x = m.left;
28709             b.y = m.top;
28710             centerY = b.height + b.y + m.bottom;
28711             centerH -= centerY;
28712             north.updateBox(this.safeBox(b));
28713         }
28714         if(south && south.isVisible()){
28715             var b = south.getBox();
28716             var m = south.getMargins();
28717             b.width = w - (m.left+m.right);
28718             b.x = m.left;
28719             var totalHeight = (b.height + m.top + m.bottom);
28720             b.y = h - totalHeight + m.top;
28721             centerH -= totalHeight;
28722             south.updateBox(this.safeBox(b));
28723         }
28724         if(west && west.isVisible()){
28725             var b = west.getBox();
28726             var m = west.getMargins();
28727             b.height = centerH - (m.top+m.bottom);
28728             b.x = m.left;
28729             b.y = centerY + m.top;
28730             var totalWidth = (b.width + m.left + m.right);
28731             centerX += totalWidth;
28732             centerW -= totalWidth;
28733             west.updateBox(this.safeBox(b));
28734         }
28735         if(east && east.isVisible()){
28736             var b = east.getBox();
28737             var m = east.getMargins();
28738             b.height = centerH - (m.top+m.bottom);
28739             var totalWidth = (b.width + m.left + m.right);
28740             b.x = w - totalWidth + m.left;
28741             b.y = centerY + m.top;
28742             centerW -= totalWidth;
28743             east.updateBox(this.safeBox(b));
28744         }
28745         if(center){
28746             var m = center.getMargins();
28747             var centerBox = {
28748                 x: centerX + m.left,
28749                 y: centerY + m.top,
28750                 width: centerW - (m.left+m.right),
28751                 height: centerH - (m.top+m.bottom)
28752             };
28753             //if(this.hideOnLayout){
28754                 //center.el.setStyle("display", "block");
28755             //}
28756             center.updateBox(this.safeBox(centerBox));
28757         }
28758         this.el.repaint();
28759         this.fireEvent("layout", this);
28760     },
28761
28762     // private
28763     safeBox : function(box){
28764         box.width = Math.max(0, box.width);
28765         box.height = Math.max(0, box.height);
28766         return box;
28767     },
28768
28769     /**
28770      * Adds a ContentPanel (or subclass) to this layout.
28771      * @param {String} target The target region key (north, south, east, west or center).
28772      * @param {Roo.ContentPanel} panel The panel to add
28773      * @return {Roo.ContentPanel} The added panel
28774      */
28775     add : function(target, panel){
28776          
28777         target = target.toLowerCase();
28778         return this.regions[target].add(panel);
28779     },
28780
28781     /**
28782      * Remove a ContentPanel (or subclass) to this layout.
28783      * @param {String} target The target region key (north, south, east, west or center).
28784      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28785      * @return {Roo.ContentPanel} The removed panel
28786      */
28787     remove : function(target, panel){
28788         target = target.toLowerCase();
28789         return this.regions[target].remove(panel);
28790     },
28791
28792     /**
28793      * Searches all regions for a panel with the specified id
28794      * @param {String} panelId
28795      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28796      */
28797     findPanel : function(panelId){
28798         var rs = this.regions;
28799         for(var target in rs){
28800             if(typeof rs[target] != "function"){
28801                 var p = rs[target].getPanel(panelId);
28802                 if(p){
28803                     return p;
28804                 }
28805             }
28806         }
28807         return null;
28808     },
28809
28810     /**
28811      * Searches all regions for a panel with the specified id and activates (shows) it.
28812      * @param {String/ContentPanel} panelId The panels id or the panel itself
28813      * @return {Roo.ContentPanel} The shown panel or null
28814      */
28815     showPanel : function(panelId) {
28816       var rs = this.regions;
28817       for(var target in rs){
28818          var r = rs[target];
28819          if(typeof r != "function"){
28820             if(r.hasPanel(panelId)){
28821                return r.showPanel(panelId);
28822             }
28823          }
28824       }
28825       return null;
28826    },
28827
28828    /**
28829      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28830      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28831      */
28832     restoreState : function(provider){
28833         if(!provider){
28834             provider = Roo.state.Manager;
28835         }
28836         var sm = new Roo.LayoutStateManager();
28837         sm.init(this, provider);
28838     },
28839
28840     /**
28841      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28842      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28843      * a valid ContentPanel config object.  Example:
28844      * <pre><code>
28845 // Create the main layout
28846 var layout = new Roo.BorderLayout('main-ct', {
28847     west: {
28848         split:true,
28849         minSize: 175,
28850         titlebar: true
28851     },
28852     center: {
28853         title:'Components'
28854     }
28855 }, 'main-ct');
28856
28857 // Create and add multiple ContentPanels at once via configs
28858 layout.batchAdd({
28859    west: {
28860        id: 'source-files',
28861        autoCreate:true,
28862        title:'Ext Source Files',
28863        autoScroll:true,
28864        fitToFrame:true
28865    },
28866    center : {
28867        el: cview,
28868        autoScroll:true,
28869        fitToFrame:true,
28870        toolbar: tb,
28871        resizeEl:'cbody'
28872    }
28873 });
28874 </code></pre>
28875      * @param {Object} regions An object containing ContentPanel configs by region name
28876      */
28877     batchAdd : function(regions){
28878         this.beginUpdate();
28879         for(var rname in regions){
28880             var lr = this.regions[rname];
28881             if(lr){
28882                 this.addTypedPanels(lr, regions[rname]);
28883             }
28884         }
28885         this.endUpdate();
28886     },
28887
28888     // private
28889     addTypedPanels : function(lr, ps){
28890         if(typeof ps == 'string'){
28891             lr.add(new Roo.ContentPanel(ps));
28892         }
28893         else if(ps instanceof Array){
28894             for(var i =0, len = ps.length; i < len; i++){
28895                 this.addTypedPanels(lr, ps[i]);
28896             }
28897         }
28898         else if(!ps.events){ // raw config?
28899             var el = ps.el;
28900             delete ps.el; // prevent conflict
28901             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28902         }
28903         else {  // panel object assumed!
28904             lr.add(ps);
28905         }
28906     },
28907     /**
28908      * Adds a xtype elements to the layout.
28909      * <pre><code>
28910
28911 layout.addxtype({
28912        xtype : 'ContentPanel',
28913        region: 'west',
28914        items: [ .... ]
28915    }
28916 );
28917
28918 layout.addxtype({
28919         xtype : 'NestedLayoutPanel',
28920         region: 'west',
28921         layout: {
28922            center: { },
28923            west: { }   
28924         },
28925         items : [ ... list of content panels or nested layout panels.. ]
28926    }
28927 );
28928 </code></pre>
28929      * @param {Object} cfg Xtype definition of item to add.
28930      */
28931     addxtype : function(cfg)
28932     {
28933         // basically accepts a pannel...
28934         // can accept a layout region..!?!?
28935         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28936         
28937         if (!cfg.xtype.match(/Panel$/)) {
28938             return false;
28939         }
28940         var ret = false;
28941         
28942         if (typeof(cfg.region) == 'undefined') {
28943             Roo.log("Failed to add Panel, region was not set");
28944             Roo.log(cfg);
28945             return false;
28946         }
28947         var region = cfg.region;
28948         delete cfg.region;
28949         
28950           
28951         var xitems = [];
28952         if (cfg.items) {
28953             xitems = cfg.items;
28954             delete cfg.items;
28955         }
28956         var nb = false;
28957         
28958         switch(cfg.xtype) 
28959         {
28960             case 'ContentPanel':  // ContentPanel (el, cfg)
28961             case 'ScrollPanel':  // ContentPanel (el, cfg)
28962             case 'ViewPanel': 
28963                 if(cfg.autoCreate) {
28964                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28965                 } else {
28966                     var el = this.el.createChild();
28967                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28968                 }
28969                 
28970                 this.add(region, ret);
28971                 break;
28972             
28973             
28974             case 'TreePanel': // our new panel!
28975                 cfg.el = this.el.createChild();
28976                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28977                 this.add(region, ret);
28978                 break;
28979             
28980             case 'NestedLayoutPanel': 
28981                 // create a new Layout (which is  a Border Layout...
28982                 var el = this.el.createChild();
28983                 var clayout = cfg.layout;
28984                 delete cfg.layout;
28985                 clayout.items   = clayout.items  || [];
28986                 // replace this exitems with the clayout ones..
28987                 xitems = clayout.items;
28988                  
28989                 
28990                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28991                     cfg.background = false;
28992                 }
28993                 var layout = new Roo.BorderLayout(el, clayout);
28994                 
28995                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28996                 //console.log('adding nested layout panel '  + cfg.toSource());
28997                 this.add(region, ret);
28998                 nb = {}; /// find first...
28999                 break;
29000                 
29001             case 'GridPanel': 
29002             
29003                 // needs grid and region
29004                 
29005                 //var el = this.getRegion(region).el.createChild();
29006                 var el = this.el.createChild();
29007                 // create the grid first...
29008                 
29009                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29010                 delete cfg.grid;
29011                 if (region == 'center' && this.active ) {
29012                     cfg.background = false;
29013                 }
29014                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29015                 
29016                 this.add(region, ret);
29017                 if (cfg.background) {
29018                     ret.on('activate', function(gp) {
29019                         if (!gp.grid.rendered) {
29020                             gp.grid.render();
29021                         }
29022                     });
29023                 } else {
29024                     grid.render();
29025                 }
29026                 break;
29027            
29028            
29029            
29030                 
29031                 
29032                 
29033             default:
29034                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29035                     
29036                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29037                     this.add(region, ret);
29038                 } else {
29039                 
29040                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29041                     return null;
29042                 }
29043                 
29044              // GridPanel (grid, cfg)
29045             
29046         }
29047         this.beginUpdate();
29048         // add children..
29049         var region = '';
29050         var abn = {};
29051         Roo.each(xitems, function(i)  {
29052             region = nb && i.region ? i.region : false;
29053             
29054             var add = ret.addxtype(i);
29055            
29056             if (region) {
29057                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29058                 if (!i.background) {
29059                     abn[region] = nb[region] ;
29060                 }
29061             }
29062             
29063         });
29064         this.endUpdate();
29065
29066         // make the last non-background panel active..
29067         //if (nb) { Roo.log(abn); }
29068         if (nb) {
29069             
29070             for(var r in abn) {
29071                 region = this.getRegion(r);
29072                 if (region) {
29073                     // tried using nb[r], but it does not work..
29074                      
29075                     region.showPanel(abn[r]);
29076                    
29077                 }
29078             }
29079         }
29080         return ret;
29081         
29082     }
29083 });
29084
29085 /**
29086  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29087  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29088  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29089  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29090  * <pre><code>
29091 // shorthand
29092 var CP = Roo.ContentPanel;
29093
29094 var layout = Roo.BorderLayout.create({
29095     north: {
29096         initialSize: 25,
29097         titlebar: false,
29098         panels: [new CP("north", "North")]
29099     },
29100     west: {
29101         split:true,
29102         initialSize: 200,
29103         minSize: 175,
29104         maxSize: 400,
29105         titlebar: true,
29106         collapsible: true,
29107         panels: [new CP("west", {title: "West"})]
29108     },
29109     east: {
29110         split:true,
29111         initialSize: 202,
29112         minSize: 175,
29113         maxSize: 400,
29114         titlebar: true,
29115         collapsible: true,
29116         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29117     },
29118     south: {
29119         split:true,
29120         initialSize: 100,
29121         minSize: 100,
29122         maxSize: 200,
29123         titlebar: true,
29124         collapsible: true,
29125         panels: [new CP("south", {title: "South", closable: true})]
29126     },
29127     center: {
29128         titlebar: true,
29129         autoScroll:true,
29130         resizeTabs: true,
29131         minTabWidth: 50,
29132         preferredTabWidth: 150,
29133         panels: [
29134             new CP("center1", {title: "Close Me", closable: true}),
29135             new CP("center2", {title: "Center Panel", closable: false})
29136         ]
29137     }
29138 }, document.body);
29139
29140 layout.getRegion("center").showPanel("center1");
29141 </code></pre>
29142  * @param config
29143  * @param targetEl
29144  */
29145 Roo.BorderLayout.create = function(config, targetEl){
29146     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29147     layout.beginUpdate();
29148     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29149     for(var j = 0, jlen = regions.length; j < jlen; j++){
29150         var lr = regions[j];
29151         if(layout.regions[lr] && config[lr].panels){
29152             var r = layout.regions[lr];
29153             var ps = config[lr].panels;
29154             layout.addTypedPanels(r, ps);
29155         }
29156     }
29157     layout.endUpdate();
29158     return layout;
29159 };
29160
29161 // private
29162 Roo.BorderLayout.RegionFactory = {
29163     // private
29164     validRegions : ["north","south","east","west","center"],
29165
29166     // private
29167     create : function(target, mgr, config){
29168         target = target.toLowerCase();
29169         if(config.lightweight || config.basic){
29170             return new Roo.BasicLayoutRegion(mgr, config, target);
29171         }
29172         switch(target){
29173             case "north":
29174                 return new Roo.NorthLayoutRegion(mgr, config);
29175             case "south":
29176                 return new Roo.SouthLayoutRegion(mgr, config);
29177             case "east":
29178                 return new Roo.EastLayoutRegion(mgr, config);
29179             case "west":
29180                 return new Roo.WestLayoutRegion(mgr, config);
29181             case "center":
29182                 return new Roo.CenterLayoutRegion(mgr, config);
29183         }
29184         throw 'Layout region "'+target+'" not supported.';
29185     }
29186 };/*
29187  * Based on:
29188  * Ext JS Library 1.1.1
29189  * Copyright(c) 2006-2007, Ext JS, LLC.
29190  *
29191  * Originally Released Under LGPL - original licence link has changed is not relivant.
29192  *
29193  * Fork - LGPL
29194  * <script type="text/javascript">
29195  */
29196  
29197 /**
29198  * @class Roo.BasicLayoutRegion
29199  * @extends Roo.util.Observable
29200  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29201  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29202  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29203  */
29204 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29205     this.mgr = mgr;
29206     this.position  = pos;
29207     this.events = {
29208         /**
29209          * @scope Roo.BasicLayoutRegion
29210          */
29211         
29212         /**
29213          * @event beforeremove
29214          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29215          * @param {Roo.LayoutRegion} this
29216          * @param {Roo.ContentPanel} panel The panel
29217          * @param {Object} e The cancel event object
29218          */
29219         "beforeremove" : true,
29220         /**
29221          * @event invalidated
29222          * Fires when the layout for this region is changed.
29223          * @param {Roo.LayoutRegion} this
29224          */
29225         "invalidated" : true,
29226         /**
29227          * @event visibilitychange
29228          * Fires when this region is shown or hidden 
29229          * @param {Roo.LayoutRegion} this
29230          * @param {Boolean} visibility true or false
29231          */
29232         "visibilitychange" : true,
29233         /**
29234          * @event paneladded
29235          * Fires when a panel is added. 
29236          * @param {Roo.LayoutRegion} this
29237          * @param {Roo.ContentPanel} panel The panel
29238          */
29239         "paneladded" : true,
29240         /**
29241          * @event panelremoved
29242          * Fires when a panel is removed. 
29243          * @param {Roo.LayoutRegion} this
29244          * @param {Roo.ContentPanel} panel The panel
29245          */
29246         "panelremoved" : true,
29247         /**
29248          * @event beforecollapse
29249          * Fires when this region before collapse.
29250          * @param {Roo.LayoutRegion} this
29251          */
29252         "beforecollapse" : true,
29253         /**
29254          * @event collapsed
29255          * Fires when this region is collapsed.
29256          * @param {Roo.LayoutRegion} this
29257          */
29258         "collapsed" : true,
29259         /**
29260          * @event expanded
29261          * Fires when this region is expanded.
29262          * @param {Roo.LayoutRegion} this
29263          */
29264         "expanded" : true,
29265         /**
29266          * @event slideshow
29267          * Fires when this region is slid into view.
29268          * @param {Roo.LayoutRegion} this
29269          */
29270         "slideshow" : true,
29271         /**
29272          * @event slidehide
29273          * Fires when this region slides out of view. 
29274          * @param {Roo.LayoutRegion} this
29275          */
29276         "slidehide" : true,
29277         /**
29278          * @event panelactivated
29279          * Fires when a panel is activated. 
29280          * @param {Roo.LayoutRegion} this
29281          * @param {Roo.ContentPanel} panel The activated panel
29282          */
29283         "panelactivated" : true,
29284         /**
29285          * @event resized
29286          * Fires when the user resizes this region. 
29287          * @param {Roo.LayoutRegion} this
29288          * @param {Number} newSize The new size (width for east/west, height for north/south)
29289          */
29290         "resized" : true
29291     };
29292     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29293     this.panels = new Roo.util.MixedCollection();
29294     this.panels.getKey = this.getPanelId.createDelegate(this);
29295     this.box = null;
29296     this.activePanel = null;
29297     // ensure listeners are added...
29298     
29299     if (config.listeners || config.events) {
29300         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29301             listeners : config.listeners || {},
29302             events : config.events || {}
29303         });
29304     }
29305     
29306     if(skipConfig !== true){
29307         this.applyConfig(config);
29308     }
29309 };
29310
29311 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29312     getPanelId : function(p){
29313         return p.getId();
29314     },
29315     
29316     applyConfig : function(config){
29317         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29318         this.config = config;
29319         
29320     },
29321     
29322     /**
29323      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29324      * the width, for horizontal (north, south) the height.
29325      * @param {Number} newSize The new width or height
29326      */
29327     resizeTo : function(newSize){
29328         var el = this.el ? this.el :
29329                  (this.activePanel ? this.activePanel.getEl() : null);
29330         if(el){
29331             switch(this.position){
29332                 case "east":
29333                 case "west":
29334                     el.setWidth(newSize);
29335                     this.fireEvent("resized", this, newSize);
29336                 break;
29337                 case "north":
29338                 case "south":
29339                     el.setHeight(newSize);
29340                     this.fireEvent("resized", this, newSize);
29341                 break;                
29342             }
29343         }
29344     },
29345     
29346     getBox : function(){
29347         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29348     },
29349     
29350     getMargins : function(){
29351         return this.margins;
29352     },
29353     
29354     updateBox : function(box){
29355         this.box = box;
29356         var el = this.activePanel.getEl();
29357         el.dom.style.left = box.x + "px";
29358         el.dom.style.top = box.y + "px";
29359         this.activePanel.setSize(box.width, box.height);
29360     },
29361     
29362     /**
29363      * Returns the container element for this region.
29364      * @return {Roo.Element}
29365      */
29366     getEl : function(){
29367         return this.activePanel;
29368     },
29369     
29370     /**
29371      * Returns true if this region is currently visible.
29372      * @return {Boolean}
29373      */
29374     isVisible : function(){
29375         return this.activePanel ? true : false;
29376     },
29377     
29378     setActivePanel : function(panel){
29379         panel = this.getPanel(panel);
29380         if(this.activePanel && this.activePanel != panel){
29381             this.activePanel.setActiveState(false);
29382             this.activePanel.getEl().setLeftTop(-10000,-10000);
29383         }
29384         this.activePanel = panel;
29385         panel.setActiveState(true);
29386         if(this.box){
29387             panel.setSize(this.box.width, this.box.height);
29388         }
29389         this.fireEvent("panelactivated", this, panel);
29390         this.fireEvent("invalidated");
29391     },
29392     
29393     /**
29394      * Show the specified panel.
29395      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29396      * @return {Roo.ContentPanel} The shown panel or null
29397      */
29398     showPanel : function(panel){
29399         if(panel = this.getPanel(panel)){
29400             this.setActivePanel(panel);
29401         }
29402         return panel;
29403     },
29404     
29405     /**
29406      * Get the active panel for this region.
29407      * @return {Roo.ContentPanel} The active panel or null
29408      */
29409     getActivePanel : function(){
29410         return this.activePanel;
29411     },
29412     
29413     /**
29414      * Add the passed ContentPanel(s)
29415      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29416      * @return {Roo.ContentPanel} The panel added (if only one was added)
29417      */
29418     add : function(panel){
29419         if(arguments.length > 1){
29420             for(var i = 0, len = arguments.length; i < len; i++) {
29421                 this.add(arguments[i]);
29422             }
29423             return null;
29424         }
29425         if(this.hasPanel(panel)){
29426             this.showPanel(panel);
29427             return panel;
29428         }
29429         var el = panel.getEl();
29430         if(el.dom.parentNode != this.mgr.el.dom){
29431             this.mgr.el.dom.appendChild(el.dom);
29432         }
29433         if(panel.setRegion){
29434             panel.setRegion(this);
29435         }
29436         this.panels.add(panel);
29437         el.setStyle("position", "absolute");
29438         if(!panel.background){
29439             this.setActivePanel(panel);
29440             if(this.config.initialSize && this.panels.getCount()==1){
29441                 this.resizeTo(this.config.initialSize);
29442             }
29443         }
29444         this.fireEvent("paneladded", this, panel);
29445         return panel;
29446     },
29447     
29448     /**
29449      * Returns true if the panel is in this region.
29450      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29451      * @return {Boolean}
29452      */
29453     hasPanel : function(panel){
29454         if(typeof panel == "object"){ // must be panel obj
29455             panel = panel.getId();
29456         }
29457         return this.getPanel(panel) ? true : false;
29458     },
29459     
29460     /**
29461      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29462      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29463      * @param {Boolean} preservePanel Overrides the config preservePanel option
29464      * @return {Roo.ContentPanel} The panel that was removed
29465      */
29466     remove : function(panel, preservePanel){
29467         panel = this.getPanel(panel);
29468         if(!panel){
29469             return null;
29470         }
29471         var e = {};
29472         this.fireEvent("beforeremove", this, panel, e);
29473         if(e.cancel === true){
29474             return null;
29475         }
29476         var panelId = panel.getId();
29477         this.panels.removeKey(panelId);
29478         return panel;
29479     },
29480     
29481     /**
29482      * Returns the panel specified or null if it's not in this region.
29483      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29484      * @return {Roo.ContentPanel}
29485      */
29486     getPanel : function(id){
29487         if(typeof id == "object"){ // must be panel obj
29488             return id;
29489         }
29490         return this.panels.get(id);
29491     },
29492     
29493     /**
29494      * Returns this regions position (north/south/east/west/center).
29495      * @return {String} 
29496      */
29497     getPosition: function(){
29498         return this.position;    
29499     }
29500 });/*
29501  * Based on:
29502  * Ext JS Library 1.1.1
29503  * Copyright(c) 2006-2007, Ext JS, LLC.
29504  *
29505  * Originally Released Under LGPL - original licence link has changed is not relivant.
29506  *
29507  * Fork - LGPL
29508  * <script type="text/javascript">
29509  */
29510  
29511 /**
29512  * @class Roo.LayoutRegion
29513  * @extends Roo.BasicLayoutRegion
29514  * This class represents a region in a layout manager.
29515  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29516  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29517  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29518  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29519  * @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})
29520  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29521  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29522  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29523  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29524  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29525  * @cfg {String}    title           The title for the region (overrides panel titles)
29526  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29527  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29528  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29529  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29530  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29531  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29532  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29533  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29534  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29535  * @cfg {Boolean}   showPin         True to show a pin button
29536  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29537  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29538  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29539  * @cfg {Number}    width           For East/West panels
29540  * @cfg {Number}    height          For North/South panels
29541  * @cfg {Boolean}   split           To show the splitter
29542  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29543  */
29544 Roo.LayoutRegion = function(mgr, config, pos){
29545     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29546     var dh = Roo.DomHelper;
29547     /** This region's container element 
29548     * @type Roo.Element */
29549     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29550     /** This region's title element 
29551     * @type Roo.Element */
29552
29553     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29554         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29555         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29556     ]}, true);
29557     this.titleEl.enableDisplayMode();
29558     /** This region's title text element 
29559     * @type HTMLElement */
29560     this.titleTextEl = this.titleEl.dom.firstChild;
29561     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29562     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29563     this.closeBtn.enableDisplayMode();
29564     this.closeBtn.on("click", this.closeClicked, this);
29565     this.closeBtn.hide();
29566
29567     this.createBody(config);
29568     this.visible = true;
29569     this.collapsed = false;
29570
29571     if(config.hideWhenEmpty){
29572         this.hide();
29573         this.on("paneladded", this.validateVisibility, this);
29574         this.on("panelremoved", this.validateVisibility, this);
29575     }
29576     this.applyConfig(config);
29577 };
29578
29579 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29580
29581     createBody : function(){
29582         /** This region's body element 
29583         * @type Roo.Element */
29584         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29585     },
29586
29587     applyConfig : function(c){
29588         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29589             var dh = Roo.DomHelper;
29590             if(c.titlebar !== false){
29591                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29592                 this.collapseBtn.on("click", this.collapse, this);
29593                 this.collapseBtn.enableDisplayMode();
29594
29595                 if(c.showPin === true || this.showPin){
29596                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29597                     this.stickBtn.enableDisplayMode();
29598                     this.stickBtn.on("click", this.expand, this);
29599                     this.stickBtn.hide();
29600                 }
29601             }
29602             /** This region's collapsed element
29603             * @type Roo.Element */
29604             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29605                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29606             ]}, true);
29607             if(c.floatable !== false){
29608                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29609                this.collapsedEl.on("click", this.collapseClick, this);
29610             }
29611
29612             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29613                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29614                    id: "message", unselectable: "on", style:{"float":"left"}});
29615                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29616              }
29617             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29618             this.expandBtn.on("click", this.expand, this);
29619         }
29620         if(this.collapseBtn){
29621             this.collapseBtn.setVisible(c.collapsible == true);
29622         }
29623         this.cmargins = c.cmargins || this.cmargins ||
29624                          (this.position == "west" || this.position == "east" ?
29625                              {top: 0, left: 2, right:2, bottom: 0} :
29626                              {top: 2, left: 0, right:0, bottom: 2});
29627         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29628         this.bottomTabs = c.tabPosition != "top";
29629         this.autoScroll = c.autoScroll || false;
29630         if(this.autoScroll){
29631             this.bodyEl.setStyle("overflow", "auto");
29632         }else{
29633             this.bodyEl.setStyle("overflow", "hidden");
29634         }
29635         //if(c.titlebar !== false){
29636             if((!c.titlebar && !c.title) || c.titlebar === false){
29637                 this.titleEl.hide();
29638             }else{
29639                 this.titleEl.show();
29640                 if(c.title){
29641                     this.titleTextEl.innerHTML = c.title;
29642                 }
29643             }
29644         //}
29645         this.duration = c.duration || .30;
29646         this.slideDuration = c.slideDuration || .45;
29647         this.config = c;
29648         if(c.collapsed){
29649             this.collapse(true);
29650         }
29651         if(c.hidden){
29652             this.hide();
29653         }
29654     },
29655     /**
29656      * Returns true if this region is currently visible.
29657      * @return {Boolean}
29658      */
29659     isVisible : function(){
29660         return this.visible;
29661     },
29662
29663     /**
29664      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29665      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29666      */
29667     setCollapsedTitle : function(title){
29668         title = title || "&#160;";
29669         if(this.collapsedTitleTextEl){
29670             this.collapsedTitleTextEl.innerHTML = title;
29671         }
29672     },
29673
29674     getBox : function(){
29675         var b;
29676         if(!this.collapsed){
29677             b = this.el.getBox(false, true);
29678         }else{
29679             b = this.collapsedEl.getBox(false, true);
29680         }
29681         return b;
29682     },
29683
29684     getMargins : function(){
29685         return this.collapsed ? this.cmargins : this.margins;
29686     },
29687
29688     highlight : function(){
29689         this.el.addClass("x-layout-panel-dragover");
29690     },
29691
29692     unhighlight : function(){
29693         this.el.removeClass("x-layout-panel-dragover");
29694     },
29695
29696     updateBox : function(box){
29697         this.box = box;
29698         if(!this.collapsed){
29699             this.el.dom.style.left = box.x + "px";
29700             this.el.dom.style.top = box.y + "px";
29701             this.updateBody(box.width, box.height);
29702         }else{
29703             this.collapsedEl.dom.style.left = box.x + "px";
29704             this.collapsedEl.dom.style.top = box.y + "px";
29705             this.collapsedEl.setSize(box.width, box.height);
29706         }
29707         if(this.tabs){
29708             this.tabs.autoSizeTabs();
29709         }
29710     },
29711
29712     updateBody : function(w, h){
29713         if(w !== null){
29714             this.el.setWidth(w);
29715             w -= this.el.getBorderWidth("rl");
29716             if(this.config.adjustments){
29717                 w += this.config.adjustments[0];
29718             }
29719         }
29720         if(h !== null){
29721             this.el.setHeight(h);
29722             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29723             h -= this.el.getBorderWidth("tb");
29724             if(this.config.adjustments){
29725                 h += this.config.adjustments[1];
29726             }
29727             this.bodyEl.setHeight(h);
29728             if(this.tabs){
29729                 h = this.tabs.syncHeight(h);
29730             }
29731         }
29732         if(this.panelSize){
29733             w = w !== null ? w : this.panelSize.width;
29734             h = h !== null ? h : this.panelSize.height;
29735         }
29736         if(this.activePanel){
29737             var el = this.activePanel.getEl();
29738             w = w !== null ? w : el.getWidth();
29739             h = h !== null ? h : el.getHeight();
29740             this.panelSize = {width: w, height: h};
29741             this.activePanel.setSize(w, h);
29742         }
29743         if(Roo.isIE && this.tabs){
29744             this.tabs.el.repaint();
29745         }
29746     },
29747
29748     /**
29749      * Returns the container element for this region.
29750      * @return {Roo.Element}
29751      */
29752     getEl : function(){
29753         return this.el;
29754     },
29755
29756     /**
29757      * Hides this region.
29758      */
29759     hide : function(){
29760         if(!this.collapsed){
29761             this.el.dom.style.left = "-2000px";
29762             this.el.hide();
29763         }else{
29764             this.collapsedEl.dom.style.left = "-2000px";
29765             this.collapsedEl.hide();
29766         }
29767         this.visible = false;
29768         this.fireEvent("visibilitychange", this, false);
29769     },
29770
29771     /**
29772      * Shows this region if it was previously hidden.
29773      */
29774     show : function(){
29775         if(!this.collapsed){
29776             this.el.show();
29777         }else{
29778             this.collapsedEl.show();
29779         }
29780         this.visible = true;
29781         this.fireEvent("visibilitychange", this, true);
29782     },
29783
29784     closeClicked : function(){
29785         if(this.activePanel){
29786             this.remove(this.activePanel);
29787         }
29788     },
29789
29790     collapseClick : function(e){
29791         if(this.isSlid){
29792            e.stopPropagation();
29793            this.slideIn();
29794         }else{
29795            e.stopPropagation();
29796            this.slideOut();
29797         }
29798     },
29799
29800     /**
29801      * Collapses this region.
29802      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29803      */
29804     collapse : function(skipAnim, skipCheck = false){
29805         if(this.collapsed) {
29806             return;
29807         }
29808         
29809         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29810             
29811             this.collapsed = true;
29812             if(this.split){
29813                 this.split.el.hide();
29814             }
29815             if(this.config.animate && skipAnim !== true){
29816                 this.fireEvent("invalidated", this);
29817                 this.animateCollapse();
29818             }else{
29819                 this.el.setLocation(-20000,-20000);
29820                 this.el.hide();
29821                 this.collapsedEl.show();
29822                 this.fireEvent("collapsed", this);
29823                 this.fireEvent("invalidated", this);
29824             }
29825         }
29826         
29827     },
29828
29829     animateCollapse : function(){
29830         // overridden
29831     },
29832
29833     /**
29834      * Expands this region if it was previously collapsed.
29835      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29836      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29837      */
29838     expand : function(e, skipAnim){
29839         if(e) {
29840             e.stopPropagation();
29841         }
29842         if(!this.collapsed || this.el.hasActiveFx()) {
29843             return;
29844         }
29845         if(this.isSlid){
29846             this.afterSlideIn();
29847             skipAnim = true;
29848         }
29849         this.collapsed = false;
29850         if(this.config.animate && skipAnim !== true){
29851             this.animateExpand();
29852         }else{
29853             this.el.show();
29854             if(this.split){
29855                 this.split.el.show();
29856             }
29857             this.collapsedEl.setLocation(-2000,-2000);
29858             this.collapsedEl.hide();
29859             this.fireEvent("invalidated", this);
29860             this.fireEvent("expanded", this);
29861         }
29862     },
29863
29864     animateExpand : function(){
29865         // overridden
29866     },
29867
29868     initTabs : function()
29869     {
29870         this.bodyEl.setStyle("overflow", "hidden");
29871         var ts = new Roo.TabPanel(
29872                 this.bodyEl.dom,
29873                 {
29874                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29875                     disableTooltips: this.config.disableTabTips,
29876                     toolbar : this.config.toolbar
29877                 }
29878         );
29879         if(this.config.hideTabs){
29880             ts.stripWrap.setDisplayed(false);
29881         }
29882         this.tabs = ts;
29883         ts.resizeTabs = this.config.resizeTabs === true;
29884         ts.minTabWidth = this.config.minTabWidth || 40;
29885         ts.maxTabWidth = this.config.maxTabWidth || 250;
29886         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29887         ts.monitorResize = false;
29888         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29889         ts.bodyEl.addClass('x-layout-tabs-body');
29890         this.panels.each(this.initPanelAsTab, this);
29891     },
29892
29893     initPanelAsTab : function(panel){
29894         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29895                     this.config.closeOnTab && panel.isClosable());
29896         if(panel.tabTip !== undefined){
29897             ti.setTooltip(panel.tabTip);
29898         }
29899         ti.on("activate", function(){
29900               this.setActivePanel(panel);
29901         }, this);
29902         if(this.config.closeOnTab){
29903             ti.on("beforeclose", function(t, e){
29904                 e.cancel = true;
29905                 this.remove(panel);
29906             }, this);
29907         }
29908         return ti;
29909     },
29910
29911     updatePanelTitle : function(panel, title){
29912         if(this.activePanel == panel){
29913             this.updateTitle(title);
29914         }
29915         if(this.tabs){
29916             var ti = this.tabs.getTab(panel.getEl().id);
29917             ti.setText(title);
29918             if(panel.tabTip !== undefined){
29919                 ti.setTooltip(panel.tabTip);
29920             }
29921         }
29922     },
29923
29924     updateTitle : function(title){
29925         if(this.titleTextEl && !this.config.title){
29926             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29927         }
29928     },
29929
29930     setActivePanel : function(panel){
29931         panel = this.getPanel(panel);
29932         if(this.activePanel && this.activePanel != panel){
29933             this.activePanel.setActiveState(false);
29934         }
29935         this.activePanel = panel;
29936         panel.setActiveState(true);
29937         if(this.panelSize){
29938             panel.setSize(this.panelSize.width, this.panelSize.height);
29939         }
29940         if(this.closeBtn){
29941             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29942         }
29943         this.updateTitle(panel.getTitle());
29944         if(this.tabs){
29945             this.fireEvent("invalidated", this);
29946         }
29947         this.fireEvent("panelactivated", this, panel);
29948     },
29949
29950     /**
29951      * Shows the specified panel.
29952      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29953      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29954      */
29955     showPanel : function(panel)
29956     {
29957         panel = this.getPanel(panel);
29958         if(panel){
29959             if(this.tabs){
29960                 var tab = this.tabs.getTab(panel.getEl().id);
29961                 if(tab.isHidden()){
29962                     this.tabs.unhideTab(tab.id);
29963                 }
29964                 tab.activate();
29965             }else{
29966                 this.setActivePanel(panel);
29967             }
29968         }
29969         return panel;
29970     },
29971
29972     /**
29973      * Get the active panel for this region.
29974      * @return {Roo.ContentPanel} The active panel or null
29975      */
29976     getActivePanel : function(){
29977         return this.activePanel;
29978     },
29979
29980     validateVisibility : function(){
29981         if(this.panels.getCount() < 1){
29982             this.updateTitle("&#160;");
29983             this.closeBtn.hide();
29984             this.hide();
29985         }else{
29986             if(!this.isVisible()){
29987                 this.show();
29988             }
29989         }
29990     },
29991
29992     /**
29993      * Adds the passed ContentPanel(s) to this region.
29994      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29995      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29996      */
29997     add : function(panel){
29998         if(arguments.length > 1){
29999             for(var i = 0, len = arguments.length; i < len; i++) {
30000                 this.add(arguments[i]);
30001             }
30002             return null;
30003         }
30004         if(this.hasPanel(panel)){
30005             this.showPanel(panel);
30006             return panel;
30007         }
30008         panel.setRegion(this);
30009         this.panels.add(panel);
30010         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30011             this.bodyEl.dom.appendChild(panel.getEl().dom);
30012             if(panel.background !== true){
30013                 this.setActivePanel(panel);
30014             }
30015             this.fireEvent("paneladded", this, panel);
30016             return panel;
30017         }
30018         if(!this.tabs){
30019             this.initTabs();
30020         }else{
30021             this.initPanelAsTab(panel);
30022         }
30023         if(panel.background !== true){
30024             this.tabs.activate(panel.getEl().id);
30025         }
30026         this.fireEvent("paneladded", this, panel);
30027         return panel;
30028     },
30029
30030     /**
30031      * Hides the tab for the specified panel.
30032      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30033      */
30034     hidePanel : function(panel){
30035         if(this.tabs && (panel = this.getPanel(panel))){
30036             this.tabs.hideTab(panel.getEl().id);
30037         }
30038     },
30039
30040     /**
30041      * Unhides the tab for a previously hidden panel.
30042      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30043      */
30044     unhidePanel : function(panel){
30045         if(this.tabs && (panel = this.getPanel(panel))){
30046             this.tabs.unhideTab(panel.getEl().id);
30047         }
30048     },
30049
30050     clearPanels : function(){
30051         while(this.panels.getCount() > 0){
30052              this.remove(this.panels.first());
30053         }
30054     },
30055
30056     /**
30057      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30058      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30059      * @param {Boolean} preservePanel Overrides the config preservePanel option
30060      * @return {Roo.ContentPanel} The panel that was removed
30061      */
30062     remove : function(panel, preservePanel){
30063         panel = this.getPanel(panel);
30064         if(!panel){
30065             return null;
30066         }
30067         var e = {};
30068         this.fireEvent("beforeremove", this, panel, e);
30069         if(e.cancel === true){
30070             return null;
30071         }
30072         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30073         var panelId = panel.getId();
30074         this.panels.removeKey(panelId);
30075         if(preservePanel){
30076             document.body.appendChild(panel.getEl().dom);
30077         }
30078         if(this.tabs){
30079             this.tabs.removeTab(panel.getEl().id);
30080         }else if (!preservePanel){
30081             this.bodyEl.dom.removeChild(panel.getEl().dom);
30082         }
30083         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30084             var p = this.panels.first();
30085             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30086             tempEl.appendChild(p.getEl().dom);
30087             this.bodyEl.update("");
30088             this.bodyEl.dom.appendChild(p.getEl().dom);
30089             tempEl = null;
30090             this.updateTitle(p.getTitle());
30091             this.tabs = null;
30092             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30093             this.setActivePanel(p);
30094         }
30095         panel.setRegion(null);
30096         if(this.activePanel == panel){
30097             this.activePanel = null;
30098         }
30099         if(this.config.autoDestroy !== false && preservePanel !== true){
30100             try{panel.destroy();}catch(e){}
30101         }
30102         this.fireEvent("panelremoved", this, panel);
30103         return panel;
30104     },
30105
30106     /**
30107      * Returns the TabPanel component used by this region
30108      * @return {Roo.TabPanel}
30109      */
30110     getTabs : function(){
30111         return this.tabs;
30112     },
30113
30114     createTool : function(parentEl, className){
30115         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30116             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30117         btn.addClassOnOver("x-layout-tools-button-over");
30118         return btn;
30119     }
30120 });/*
30121  * Based on:
30122  * Ext JS Library 1.1.1
30123  * Copyright(c) 2006-2007, Ext JS, LLC.
30124  *
30125  * Originally Released Under LGPL - original licence link has changed is not relivant.
30126  *
30127  * Fork - LGPL
30128  * <script type="text/javascript">
30129  */
30130  
30131
30132
30133 /**
30134  * @class Roo.SplitLayoutRegion
30135  * @extends Roo.LayoutRegion
30136  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30137  */
30138 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30139     this.cursor = cursor;
30140     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30141 };
30142
30143 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30144     splitTip : "Drag to resize.",
30145     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30146     useSplitTips : false,
30147
30148     applyConfig : function(config){
30149         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30150         if(config.split){
30151             if(!this.split){
30152                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30153                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30154                 /** The SplitBar for this region 
30155                 * @type Roo.SplitBar */
30156                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30157                 this.split.on("moved", this.onSplitMove, this);
30158                 this.split.useShim = config.useShim === true;
30159                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30160                 if(this.useSplitTips){
30161                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30162                 }
30163                 if(config.collapsible){
30164                     this.split.el.on("dblclick", this.collapse,  this);
30165                 }
30166             }
30167             if(typeof config.minSize != "undefined"){
30168                 this.split.minSize = config.minSize;
30169             }
30170             if(typeof config.maxSize != "undefined"){
30171                 this.split.maxSize = config.maxSize;
30172             }
30173             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30174                 this.hideSplitter();
30175             }
30176         }
30177     },
30178
30179     getHMaxSize : function(){
30180          var cmax = this.config.maxSize || 10000;
30181          var center = this.mgr.getRegion("center");
30182          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30183     },
30184
30185     getVMaxSize : function(){
30186          var cmax = this.config.maxSize || 10000;
30187          var center = this.mgr.getRegion("center");
30188          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30189     },
30190
30191     onSplitMove : function(split, newSize){
30192         this.fireEvent("resized", this, newSize);
30193     },
30194     
30195     /** 
30196      * Returns the {@link Roo.SplitBar} for this region.
30197      * @return {Roo.SplitBar}
30198      */
30199     getSplitBar : function(){
30200         return this.split;
30201     },
30202     
30203     hide : function(){
30204         this.hideSplitter();
30205         Roo.SplitLayoutRegion.superclass.hide.call(this);
30206     },
30207
30208     hideSplitter : function(){
30209         if(this.split){
30210             this.split.el.setLocation(-2000,-2000);
30211             this.split.el.hide();
30212         }
30213     },
30214
30215     show : function(){
30216         if(this.split){
30217             this.split.el.show();
30218         }
30219         Roo.SplitLayoutRegion.superclass.show.call(this);
30220     },
30221     
30222     beforeSlide: function(){
30223         if(Roo.isGecko){// firefox overflow auto bug workaround
30224             this.bodyEl.clip();
30225             if(this.tabs) {
30226                 this.tabs.bodyEl.clip();
30227             }
30228             if(this.activePanel){
30229                 this.activePanel.getEl().clip();
30230                 
30231                 if(this.activePanel.beforeSlide){
30232                     this.activePanel.beforeSlide();
30233                 }
30234             }
30235         }
30236     },
30237     
30238     afterSlide : function(){
30239         if(Roo.isGecko){// firefox overflow auto bug workaround
30240             this.bodyEl.unclip();
30241             if(this.tabs) {
30242                 this.tabs.bodyEl.unclip();
30243             }
30244             if(this.activePanel){
30245                 this.activePanel.getEl().unclip();
30246                 if(this.activePanel.afterSlide){
30247                     this.activePanel.afterSlide();
30248                 }
30249             }
30250         }
30251     },
30252
30253     initAutoHide : function(){
30254         if(this.autoHide !== false){
30255             if(!this.autoHideHd){
30256                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30257                 this.autoHideHd = {
30258                     "mouseout": function(e){
30259                         if(!e.within(this.el, true)){
30260                             st.delay(500);
30261                         }
30262                     },
30263                     "mouseover" : function(e){
30264                         st.cancel();
30265                     },
30266                     scope : this
30267                 };
30268             }
30269             this.el.on(this.autoHideHd);
30270         }
30271     },
30272
30273     clearAutoHide : function(){
30274         if(this.autoHide !== false){
30275             this.el.un("mouseout", this.autoHideHd.mouseout);
30276             this.el.un("mouseover", this.autoHideHd.mouseover);
30277         }
30278     },
30279
30280     clearMonitor : function(){
30281         Roo.get(document).un("click", this.slideInIf, this);
30282     },
30283
30284     // these names are backwards but not changed for compat
30285     slideOut : function(){
30286         if(this.isSlid || this.el.hasActiveFx()){
30287             return;
30288         }
30289         this.isSlid = true;
30290         if(this.collapseBtn){
30291             this.collapseBtn.hide();
30292         }
30293         this.closeBtnState = this.closeBtn.getStyle('display');
30294         this.closeBtn.hide();
30295         if(this.stickBtn){
30296             this.stickBtn.show();
30297         }
30298         this.el.show();
30299         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30300         this.beforeSlide();
30301         this.el.setStyle("z-index", 10001);
30302         this.el.slideIn(this.getSlideAnchor(), {
30303             callback: function(){
30304                 this.afterSlide();
30305                 this.initAutoHide();
30306                 Roo.get(document).on("click", this.slideInIf, this);
30307                 this.fireEvent("slideshow", this);
30308             },
30309             scope: this,
30310             block: true
30311         });
30312     },
30313
30314     afterSlideIn : function(){
30315         this.clearAutoHide();
30316         this.isSlid = false;
30317         this.clearMonitor();
30318         this.el.setStyle("z-index", "");
30319         if(this.collapseBtn){
30320             this.collapseBtn.show();
30321         }
30322         this.closeBtn.setStyle('display', this.closeBtnState);
30323         if(this.stickBtn){
30324             this.stickBtn.hide();
30325         }
30326         this.fireEvent("slidehide", this);
30327     },
30328
30329     slideIn : function(cb){
30330         if(!this.isSlid || this.el.hasActiveFx()){
30331             Roo.callback(cb);
30332             return;
30333         }
30334         this.isSlid = false;
30335         this.beforeSlide();
30336         this.el.slideOut(this.getSlideAnchor(), {
30337             callback: function(){
30338                 this.el.setLeftTop(-10000, -10000);
30339                 this.afterSlide();
30340                 this.afterSlideIn();
30341                 Roo.callback(cb);
30342             },
30343             scope: this,
30344             block: true
30345         });
30346     },
30347     
30348     slideInIf : function(e){
30349         if(!e.within(this.el)){
30350             this.slideIn();
30351         }
30352     },
30353
30354     animateCollapse : function(){
30355         this.beforeSlide();
30356         this.el.setStyle("z-index", 20000);
30357         var anchor = this.getSlideAnchor();
30358         this.el.slideOut(anchor, {
30359             callback : function(){
30360                 this.el.setStyle("z-index", "");
30361                 this.collapsedEl.slideIn(anchor, {duration:.3});
30362                 this.afterSlide();
30363                 this.el.setLocation(-10000,-10000);
30364                 this.el.hide();
30365                 this.fireEvent("collapsed", this);
30366             },
30367             scope: this,
30368             block: true
30369         });
30370     },
30371
30372     animateExpand : function(){
30373         this.beforeSlide();
30374         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30375         this.el.setStyle("z-index", 20000);
30376         this.collapsedEl.hide({
30377             duration:.1
30378         });
30379         this.el.slideIn(this.getSlideAnchor(), {
30380             callback : function(){
30381                 this.el.setStyle("z-index", "");
30382                 this.afterSlide();
30383                 if(this.split){
30384                     this.split.el.show();
30385                 }
30386                 this.fireEvent("invalidated", this);
30387                 this.fireEvent("expanded", this);
30388             },
30389             scope: this,
30390             block: true
30391         });
30392     },
30393
30394     anchors : {
30395         "west" : "left",
30396         "east" : "right",
30397         "north" : "top",
30398         "south" : "bottom"
30399     },
30400
30401     sanchors : {
30402         "west" : "l",
30403         "east" : "r",
30404         "north" : "t",
30405         "south" : "b"
30406     },
30407
30408     canchors : {
30409         "west" : "tl-tr",
30410         "east" : "tr-tl",
30411         "north" : "tl-bl",
30412         "south" : "bl-tl"
30413     },
30414
30415     getAnchor : function(){
30416         return this.anchors[this.position];
30417     },
30418
30419     getCollapseAnchor : function(){
30420         return this.canchors[this.position];
30421     },
30422
30423     getSlideAnchor : function(){
30424         return this.sanchors[this.position];
30425     },
30426
30427     getAlignAdj : function(){
30428         var cm = this.cmargins;
30429         switch(this.position){
30430             case "west":
30431                 return [0, 0];
30432             break;
30433             case "east":
30434                 return [0, 0];
30435             break;
30436             case "north":
30437                 return [0, 0];
30438             break;
30439             case "south":
30440                 return [0, 0];
30441             break;
30442         }
30443     },
30444
30445     getExpandAdj : function(){
30446         var c = this.collapsedEl, cm = this.cmargins;
30447         switch(this.position){
30448             case "west":
30449                 return [-(cm.right+c.getWidth()+cm.left), 0];
30450             break;
30451             case "east":
30452                 return [cm.right+c.getWidth()+cm.left, 0];
30453             break;
30454             case "north":
30455                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30456             break;
30457             case "south":
30458                 return [0, cm.top+cm.bottom+c.getHeight()];
30459             break;
30460         }
30461     }
30462 });/*
30463  * Based on:
30464  * Ext JS Library 1.1.1
30465  * Copyright(c) 2006-2007, Ext JS, LLC.
30466  *
30467  * Originally Released Under LGPL - original licence link has changed is not relivant.
30468  *
30469  * Fork - LGPL
30470  * <script type="text/javascript">
30471  */
30472 /*
30473  * These classes are private internal classes
30474  */
30475 Roo.CenterLayoutRegion = function(mgr, config){
30476     Roo.LayoutRegion.call(this, mgr, config, "center");
30477     this.visible = true;
30478     this.minWidth = config.minWidth || 20;
30479     this.minHeight = config.minHeight || 20;
30480 };
30481
30482 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30483     hide : function(){
30484         // center panel can't be hidden
30485     },
30486     
30487     show : function(){
30488         // center panel can't be hidden
30489     },
30490     
30491     getMinWidth: function(){
30492         return this.minWidth;
30493     },
30494     
30495     getMinHeight: function(){
30496         return this.minHeight;
30497     }
30498 });
30499
30500
30501 Roo.NorthLayoutRegion = function(mgr, config){
30502     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30503     if(this.split){
30504         this.split.placement = Roo.SplitBar.TOP;
30505         this.split.orientation = Roo.SplitBar.VERTICAL;
30506         this.split.el.addClass("x-layout-split-v");
30507     }
30508     var size = config.initialSize || config.height;
30509     if(typeof size != "undefined"){
30510         this.el.setHeight(size);
30511     }
30512 };
30513 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30514     orientation: Roo.SplitBar.VERTICAL,
30515     getBox : function(){
30516         if(this.collapsed){
30517             return this.collapsedEl.getBox();
30518         }
30519         var box = this.el.getBox();
30520         if(this.split){
30521             box.height += this.split.el.getHeight();
30522         }
30523         return box;
30524     },
30525     
30526     updateBox : function(box){
30527         if(this.split && !this.collapsed){
30528             box.height -= this.split.el.getHeight();
30529             this.split.el.setLeft(box.x);
30530             this.split.el.setTop(box.y+box.height);
30531             this.split.el.setWidth(box.width);
30532         }
30533         if(this.collapsed){
30534             this.updateBody(box.width, null);
30535         }
30536         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30537     }
30538 });
30539
30540 Roo.SouthLayoutRegion = function(mgr, config){
30541     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30542     if(this.split){
30543         this.split.placement = Roo.SplitBar.BOTTOM;
30544         this.split.orientation = Roo.SplitBar.VERTICAL;
30545         this.split.el.addClass("x-layout-split-v");
30546     }
30547     var size = config.initialSize || config.height;
30548     if(typeof size != "undefined"){
30549         this.el.setHeight(size);
30550     }
30551 };
30552 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30553     orientation: Roo.SplitBar.VERTICAL,
30554     getBox : function(){
30555         if(this.collapsed){
30556             return this.collapsedEl.getBox();
30557         }
30558         var box = this.el.getBox();
30559         if(this.split){
30560             var sh = this.split.el.getHeight();
30561             box.height += sh;
30562             box.y -= sh;
30563         }
30564         return box;
30565     },
30566     
30567     updateBox : function(box){
30568         if(this.split && !this.collapsed){
30569             var sh = this.split.el.getHeight();
30570             box.height -= sh;
30571             box.y += sh;
30572             this.split.el.setLeft(box.x);
30573             this.split.el.setTop(box.y-sh);
30574             this.split.el.setWidth(box.width);
30575         }
30576         if(this.collapsed){
30577             this.updateBody(box.width, null);
30578         }
30579         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30580     }
30581 });
30582
30583 Roo.EastLayoutRegion = function(mgr, config){
30584     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30585     if(this.split){
30586         this.split.placement = Roo.SplitBar.RIGHT;
30587         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30588         this.split.el.addClass("x-layout-split-h");
30589     }
30590     var size = config.initialSize || config.width;
30591     if(typeof size != "undefined"){
30592         this.el.setWidth(size);
30593     }
30594 };
30595 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30596     orientation: Roo.SplitBar.HORIZONTAL,
30597     getBox : function(){
30598         if(this.collapsed){
30599             return this.collapsedEl.getBox();
30600         }
30601         var box = this.el.getBox();
30602         if(this.split){
30603             var sw = this.split.el.getWidth();
30604             box.width += sw;
30605             box.x -= sw;
30606         }
30607         return box;
30608     },
30609
30610     updateBox : function(box){
30611         if(this.split && !this.collapsed){
30612             var sw = this.split.el.getWidth();
30613             box.width -= sw;
30614             this.split.el.setLeft(box.x);
30615             this.split.el.setTop(box.y);
30616             this.split.el.setHeight(box.height);
30617             box.x += sw;
30618         }
30619         if(this.collapsed){
30620             this.updateBody(null, box.height);
30621         }
30622         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30623     }
30624 });
30625
30626 Roo.WestLayoutRegion = function(mgr, config){
30627     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30628     if(this.split){
30629         this.split.placement = Roo.SplitBar.LEFT;
30630         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30631         this.split.el.addClass("x-layout-split-h");
30632     }
30633     var size = config.initialSize || config.width;
30634     if(typeof size != "undefined"){
30635         this.el.setWidth(size);
30636     }
30637 };
30638 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30639     orientation: Roo.SplitBar.HORIZONTAL,
30640     getBox : function(){
30641         if(this.collapsed){
30642             return this.collapsedEl.getBox();
30643         }
30644         var box = this.el.getBox();
30645         if(this.split){
30646             box.width += this.split.el.getWidth();
30647         }
30648         return box;
30649     },
30650     
30651     updateBox : function(box){
30652         if(this.split && !this.collapsed){
30653             var sw = this.split.el.getWidth();
30654             box.width -= sw;
30655             this.split.el.setLeft(box.x+box.width);
30656             this.split.el.setTop(box.y);
30657             this.split.el.setHeight(box.height);
30658         }
30659         if(this.collapsed){
30660             this.updateBody(null, box.height);
30661         }
30662         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30663     }
30664 });
30665 /*
30666  * Based on:
30667  * Ext JS Library 1.1.1
30668  * Copyright(c) 2006-2007, Ext JS, LLC.
30669  *
30670  * Originally Released Under LGPL - original licence link has changed is not relivant.
30671  *
30672  * Fork - LGPL
30673  * <script type="text/javascript">
30674  */
30675  
30676  
30677 /*
30678  * Private internal class for reading and applying state
30679  */
30680 Roo.LayoutStateManager = function(layout){
30681      // default empty state
30682      this.state = {
30683         north: {},
30684         south: {},
30685         east: {},
30686         west: {}       
30687     };
30688 };
30689
30690 Roo.LayoutStateManager.prototype = {
30691     init : function(layout, provider){
30692         this.provider = provider;
30693         var state = provider.get(layout.id+"-layout-state");
30694         if(state){
30695             var wasUpdating = layout.isUpdating();
30696             if(!wasUpdating){
30697                 layout.beginUpdate();
30698             }
30699             for(var key in state){
30700                 if(typeof state[key] != "function"){
30701                     var rstate = state[key];
30702                     var r = layout.getRegion(key);
30703                     if(r && rstate){
30704                         if(rstate.size){
30705                             r.resizeTo(rstate.size);
30706                         }
30707                         if(rstate.collapsed == true){
30708                             r.collapse(true);
30709                         }else{
30710                             r.expand(null, true);
30711                         }
30712                     }
30713                 }
30714             }
30715             if(!wasUpdating){
30716                 layout.endUpdate();
30717             }
30718             this.state = state; 
30719         }
30720         this.layout = layout;
30721         layout.on("regionresized", this.onRegionResized, this);
30722         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30723         layout.on("regionexpanded", this.onRegionExpanded, this);
30724     },
30725     
30726     storeState : function(){
30727         this.provider.set(this.layout.id+"-layout-state", this.state);
30728     },
30729     
30730     onRegionResized : function(region, newSize){
30731         this.state[region.getPosition()].size = newSize;
30732         this.storeState();
30733     },
30734     
30735     onRegionCollapsed : function(region){
30736         this.state[region.getPosition()].collapsed = true;
30737         this.storeState();
30738     },
30739     
30740     onRegionExpanded : function(region){
30741         this.state[region.getPosition()].collapsed = false;
30742         this.storeState();
30743     }
30744 };/*
30745  * Based on:
30746  * Ext JS Library 1.1.1
30747  * Copyright(c) 2006-2007, Ext JS, LLC.
30748  *
30749  * Originally Released Under LGPL - original licence link has changed is not relivant.
30750  *
30751  * Fork - LGPL
30752  * <script type="text/javascript">
30753  */
30754 /**
30755  * @class Roo.ContentPanel
30756  * @extends Roo.util.Observable
30757  * A basic ContentPanel element.
30758  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30759  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30760  * @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
30761  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30762  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30763  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30764  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30765  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30766  * @cfg {String} title          The title for this panel
30767  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30768  * @cfg {String} url            Calls {@link #setUrl} with this value
30769  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30770  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30771  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30772  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30773
30774  * @constructor
30775  * Create a new ContentPanel.
30776  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30777  * @param {String/Object} config A string to set only the title or a config object
30778  * @param {String} content (optional) Set the HTML content for this panel
30779  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30780  */
30781 Roo.ContentPanel = function(el, config, content){
30782     
30783      
30784     /*
30785     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30786         config = el;
30787         el = Roo.id();
30788     }
30789     if (config && config.parentLayout) { 
30790         el = config.parentLayout.el.createChild(); 
30791     }
30792     */
30793     if(el.autoCreate){ // xtype is available if this is called from factory
30794         config = el;
30795         el = Roo.id();
30796     }
30797     this.el = Roo.get(el);
30798     if(!this.el && config && config.autoCreate){
30799         if(typeof config.autoCreate == "object"){
30800             if(!config.autoCreate.id){
30801                 config.autoCreate.id = config.id||el;
30802             }
30803             this.el = Roo.DomHelper.append(document.body,
30804                         config.autoCreate, true);
30805         }else{
30806             this.el = Roo.DomHelper.append(document.body,
30807                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30808         }
30809     }
30810     this.closable = false;
30811     this.loaded = false;
30812     this.active = false;
30813     if(typeof config == "string"){
30814         this.title = config;
30815     }else{
30816         Roo.apply(this, config);
30817     }
30818     
30819     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30820         this.wrapEl = this.el.wrap();
30821         this.toolbar.container = this.el.insertSibling(false, 'before');
30822         this.toolbar = new Roo.Toolbar(this.toolbar);
30823     }
30824     
30825     // xtype created footer. - not sure if will work as we normally have to render first..
30826     if (this.footer && !this.footer.el && this.footer.xtype) {
30827         if (!this.wrapEl) {
30828             this.wrapEl = this.el.wrap();
30829         }
30830     
30831         this.footer.container = this.wrapEl.createChild();
30832          
30833         this.footer = Roo.factory(this.footer, Roo);
30834         
30835     }
30836     
30837     if(this.resizeEl){
30838         this.resizeEl = Roo.get(this.resizeEl, true);
30839     }else{
30840         this.resizeEl = this.el;
30841     }
30842     // handle view.xtype
30843     
30844  
30845     
30846     
30847     this.addEvents({
30848         /**
30849          * @event activate
30850          * Fires when this panel is activated. 
30851          * @param {Roo.ContentPanel} this
30852          */
30853         "activate" : true,
30854         /**
30855          * @event deactivate
30856          * Fires when this panel is activated. 
30857          * @param {Roo.ContentPanel} this
30858          */
30859         "deactivate" : true,
30860
30861         /**
30862          * @event resize
30863          * Fires when this panel is resized if fitToFrame is true.
30864          * @param {Roo.ContentPanel} this
30865          * @param {Number} width The width after any component adjustments
30866          * @param {Number} height The height after any component adjustments
30867          */
30868         "resize" : true,
30869         
30870          /**
30871          * @event render
30872          * Fires when this tab is created
30873          * @param {Roo.ContentPanel} this
30874          */
30875         "render" : true
30876         
30877         
30878         
30879     });
30880     
30881
30882     
30883     
30884     if(this.autoScroll){
30885         this.resizeEl.setStyle("overflow", "auto");
30886     } else {
30887         // fix randome scrolling
30888         this.el.on('scroll', function() {
30889             Roo.log('fix random scolling');
30890             this.scrollTo('top',0); 
30891         });
30892     }
30893     content = content || this.content;
30894     if(content){
30895         this.setContent(content);
30896     }
30897     if(config && config.url){
30898         this.setUrl(this.url, this.params, this.loadOnce);
30899     }
30900     
30901     
30902     
30903     Roo.ContentPanel.superclass.constructor.call(this);
30904     
30905     if (this.view && typeof(this.view.xtype) != 'undefined') {
30906         this.view.el = this.el.appendChild(document.createElement("div"));
30907         this.view = Roo.factory(this.view); 
30908         this.view.render  &&  this.view.render(false, '');  
30909     }
30910     
30911     
30912     this.fireEvent('render', this);
30913 };
30914
30915 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30916     tabTip:'',
30917     setRegion : function(region){
30918         this.region = region;
30919         if(region){
30920            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30921         }else{
30922            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30923         } 
30924     },
30925     
30926     /**
30927      * Returns the toolbar for this Panel if one was configured. 
30928      * @return {Roo.Toolbar} 
30929      */
30930     getToolbar : function(){
30931         return this.toolbar;
30932     },
30933     
30934     setActiveState : function(active){
30935         this.active = active;
30936         if(!active){
30937             this.fireEvent("deactivate", this);
30938         }else{
30939             this.fireEvent("activate", this);
30940         }
30941     },
30942     /**
30943      * Updates this panel's element
30944      * @param {String} content The new content
30945      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30946     */
30947     setContent : function(content, loadScripts){
30948         this.el.update(content, loadScripts);
30949     },
30950
30951     ignoreResize : function(w, h){
30952         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30953             return true;
30954         }else{
30955             this.lastSize = {width: w, height: h};
30956             return false;
30957         }
30958     },
30959     /**
30960      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30961      * @return {Roo.UpdateManager} The UpdateManager
30962      */
30963     getUpdateManager : function(){
30964         return this.el.getUpdateManager();
30965     },
30966      /**
30967      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30968      * @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:
30969 <pre><code>
30970 panel.load({
30971     url: "your-url.php",
30972     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30973     callback: yourFunction,
30974     scope: yourObject, //(optional scope)
30975     discardUrl: false,
30976     nocache: false,
30977     text: "Loading...",
30978     timeout: 30,
30979     scripts: false
30980 });
30981 </code></pre>
30982      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30983      * 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.
30984      * @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}
30985      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30986      * @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.
30987      * @return {Roo.ContentPanel} this
30988      */
30989     load : function(){
30990         var um = this.el.getUpdateManager();
30991         um.update.apply(um, arguments);
30992         return this;
30993     },
30994
30995
30996     /**
30997      * 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.
30998      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30999      * @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)
31000      * @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)
31001      * @return {Roo.UpdateManager} The UpdateManager
31002      */
31003     setUrl : function(url, params, loadOnce){
31004         if(this.refreshDelegate){
31005             this.removeListener("activate", this.refreshDelegate);
31006         }
31007         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31008         this.on("activate", this.refreshDelegate);
31009         return this.el.getUpdateManager();
31010     },
31011     
31012     _handleRefresh : function(url, params, loadOnce){
31013         if(!loadOnce || !this.loaded){
31014             var updater = this.el.getUpdateManager();
31015             updater.update(url, params, this._setLoaded.createDelegate(this));
31016         }
31017     },
31018     
31019     _setLoaded : function(){
31020         this.loaded = true;
31021     }, 
31022     
31023     /**
31024      * Returns this panel's id
31025      * @return {String} 
31026      */
31027     getId : function(){
31028         return this.el.id;
31029     },
31030     
31031     /** 
31032      * Returns this panel's element - used by regiosn to add.
31033      * @return {Roo.Element} 
31034      */
31035     getEl : function(){
31036         return this.wrapEl || this.el;
31037     },
31038     
31039     adjustForComponents : function(width, height)
31040     {
31041         //Roo.log('adjustForComponents ');
31042         if(this.resizeEl != this.el){
31043             width -= this.el.getFrameWidth('lr');
31044             height -= this.el.getFrameWidth('tb');
31045         }
31046         if(this.toolbar){
31047             var te = this.toolbar.getEl();
31048             height -= te.getHeight();
31049             te.setWidth(width);
31050         }
31051         if(this.footer){
31052             var te = this.footer.getEl();
31053             Roo.log("footer:" + te.getHeight());
31054             
31055             height -= te.getHeight();
31056             te.setWidth(width);
31057         }
31058         
31059         
31060         if(this.adjustments){
31061             width += this.adjustments[0];
31062             height += this.adjustments[1];
31063         }
31064         return {"width": width, "height": height};
31065     },
31066     
31067     setSize : function(width, height){
31068         if(this.fitToFrame && !this.ignoreResize(width, height)){
31069             if(this.fitContainer && this.resizeEl != this.el){
31070                 this.el.setSize(width, height);
31071             }
31072             var size = this.adjustForComponents(width, height);
31073             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31074             this.fireEvent('resize', this, size.width, size.height);
31075         }
31076     },
31077     
31078     /**
31079      * Returns this panel's title
31080      * @return {String} 
31081      */
31082     getTitle : function(){
31083         return this.title;
31084     },
31085     
31086     /**
31087      * Set this panel's title
31088      * @param {String} title
31089      */
31090     setTitle : function(title){
31091         this.title = title;
31092         if(this.region){
31093             this.region.updatePanelTitle(this, title);
31094         }
31095     },
31096     
31097     /**
31098      * Returns true is this panel was configured to be closable
31099      * @return {Boolean} 
31100      */
31101     isClosable : function(){
31102         return this.closable;
31103     },
31104     
31105     beforeSlide : function(){
31106         this.el.clip();
31107         this.resizeEl.clip();
31108     },
31109     
31110     afterSlide : function(){
31111         this.el.unclip();
31112         this.resizeEl.unclip();
31113     },
31114     
31115     /**
31116      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31117      *   Will fail silently if the {@link #setUrl} method has not been called.
31118      *   This does not activate the panel, just updates its content.
31119      */
31120     refresh : function(){
31121         if(this.refreshDelegate){
31122            this.loaded = false;
31123            this.refreshDelegate();
31124         }
31125     },
31126     
31127     /**
31128      * Destroys this panel
31129      */
31130     destroy : function(){
31131         this.el.removeAllListeners();
31132         var tempEl = document.createElement("span");
31133         tempEl.appendChild(this.el.dom);
31134         tempEl.innerHTML = "";
31135         this.el.remove();
31136         this.el = null;
31137     },
31138     
31139     /**
31140      * form - if the content panel contains a form - this is a reference to it.
31141      * @type {Roo.form.Form}
31142      */
31143     form : false,
31144     /**
31145      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31146      *    This contains a reference to it.
31147      * @type {Roo.View}
31148      */
31149     view : false,
31150     
31151       /**
31152      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31153      * <pre><code>
31154
31155 layout.addxtype({
31156        xtype : 'Form',
31157        items: [ .... ]
31158    }
31159 );
31160
31161 </code></pre>
31162      * @param {Object} cfg Xtype definition of item to add.
31163      */
31164     
31165     addxtype : function(cfg) {
31166         // add form..
31167         if (cfg.xtype.match(/^Form$/)) {
31168             
31169             var el;
31170             //if (this.footer) {
31171             //    el = this.footer.container.insertSibling(false, 'before');
31172             //} else {
31173                 el = this.el.createChild();
31174             //}
31175
31176             this.form = new  Roo.form.Form(cfg);
31177             
31178             
31179             if ( this.form.allItems.length) {
31180                 this.form.render(el.dom);
31181             }
31182             return this.form;
31183         }
31184         // should only have one of theses..
31185         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31186             // views.. should not be just added - used named prop 'view''
31187             
31188             cfg.el = this.el.appendChild(document.createElement("div"));
31189             // factory?
31190             
31191             var ret = new Roo.factory(cfg);
31192              
31193              ret.render && ret.render(false, ''); // render blank..
31194             this.view = ret;
31195             return ret;
31196         }
31197         return false;
31198     }
31199 });
31200
31201 /**
31202  * @class Roo.GridPanel
31203  * @extends Roo.ContentPanel
31204  * @constructor
31205  * Create a new GridPanel.
31206  * @param {Roo.grid.Grid} grid The grid for this panel
31207  * @param {String/Object} config A string to set only the panel's title, or a config object
31208  */
31209 Roo.GridPanel = function(grid, config){
31210     
31211   
31212     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31213         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31214         
31215     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31216     
31217     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31218     
31219     if(this.toolbar){
31220         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31221     }
31222     // xtype created footer. - not sure if will work as we normally have to render first..
31223     if (this.footer && !this.footer.el && this.footer.xtype) {
31224         
31225         this.footer.container = this.grid.getView().getFooterPanel(true);
31226         this.footer.dataSource = this.grid.dataSource;
31227         this.footer = Roo.factory(this.footer, Roo);
31228         
31229     }
31230     
31231     grid.monitorWindowResize = false; // turn off autosizing
31232     grid.autoHeight = false;
31233     grid.autoWidth = false;
31234     this.grid = grid;
31235     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31236 };
31237
31238 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31239     getId : function(){
31240         return this.grid.id;
31241     },
31242     
31243     /**
31244      * Returns the grid for this panel
31245      * @return {Roo.grid.Grid} 
31246      */
31247     getGrid : function(){
31248         return this.grid;    
31249     },
31250     
31251     setSize : function(width, height){
31252         if(!this.ignoreResize(width, height)){
31253             var grid = this.grid;
31254             var size = this.adjustForComponents(width, height);
31255             grid.getGridEl().setSize(size.width, size.height);
31256             grid.autoSize();
31257         }
31258     },
31259     
31260     beforeSlide : function(){
31261         this.grid.getView().scroller.clip();
31262     },
31263     
31264     afterSlide : function(){
31265         this.grid.getView().scroller.unclip();
31266     },
31267     
31268     destroy : function(){
31269         this.grid.destroy();
31270         delete this.grid;
31271         Roo.GridPanel.superclass.destroy.call(this); 
31272     }
31273 });
31274
31275
31276 /**
31277  * @class Roo.NestedLayoutPanel
31278  * @extends Roo.ContentPanel
31279  * @constructor
31280  * Create a new NestedLayoutPanel.
31281  * 
31282  * 
31283  * @param {Roo.BorderLayout} layout The layout for this panel
31284  * @param {String/Object} config A string to set only the title or a config object
31285  */
31286 Roo.NestedLayoutPanel = function(layout, config)
31287 {
31288     // construct with only one argument..
31289     /* FIXME - implement nicer consturctors
31290     if (layout.layout) {
31291         config = layout;
31292         layout = config.layout;
31293         delete config.layout;
31294     }
31295     if (layout.xtype && !layout.getEl) {
31296         // then layout needs constructing..
31297         layout = Roo.factory(layout, Roo);
31298     }
31299     */
31300     
31301     
31302     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31303     
31304     layout.monitorWindowResize = false; // turn off autosizing
31305     this.layout = layout;
31306     this.layout.getEl().addClass("x-layout-nested-layout");
31307     
31308     
31309     
31310     
31311 };
31312
31313 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31314
31315     setSize : function(width, height){
31316         if(!this.ignoreResize(width, height)){
31317             var size = this.adjustForComponents(width, height);
31318             var el = this.layout.getEl();
31319             el.setSize(size.width, size.height);
31320             var touch = el.dom.offsetWidth;
31321             this.layout.layout();
31322             // ie requires a double layout on the first pass
31323             if(Roo.isIE && !this.initialized){
31324                 this.initialized = true;
31325                 this.layout.layout();
31326             }
31327         }
31328     },
31329     
31330     // activate all subpanels if not currently active..
31331     
31332     setActiveState : function(active){
31333         this.active = active;
31334         if(!active){
31335             this.fireEvent("deactivate", this);
31336             return;
31337         }
31338         
31339         this.fireEvent("activate", this);
31340         // not sure if this should happen before or after..
31341         if (!this.layout) {
31342             return; // should not happen..
31343         }
31344         var reg = false;
31345         for (var r in this.layout.regions) {
31346             reg = this.layout.getRegion(r);
31347             if (reg.getActivePanel()) {
31348                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31349                 reg.setActivePanel(reg.getActivePanel());
31350                 continue;
31351             }
31352             if (!reg.panels.length) {
31353                 continue;
31354             }
31355             reg.showPanel(reg.getPanel(0));
31356         }
31357         
31358         
31359         
31360         
31361     },
31362     
31363     /**
31364      * Returns the nested BorderLayout for this panel
31365      * @return {Roo.BorderLayout} 
31366      */
31367     getLayout : function(){
31368         return this.layout;
31369     },
31370     
31371      /**
31372      * Adds a xtype elements to the layout of the nested panel
31373      * <pre><code>
31374
31375 panel.addxtype({
31376        xtype : 'ContentPanel',
31377        region: 'west',
31378        items: [ .... ]
31379    }
31380 );
31381
31382 panel.addxtype({
31383         xtype : 'NestedLayoutPanel',
31384         region: 'west',
31385         layout: {
31386            center: { },
31387            west: { }   
31388         },
31389         items : [ ... list of content panels or nested layout panels.. ]
31390    }
31391 );
31392 </code></pre>
31393      * @param {Object} cfg Xtype definition of item to add.
31394      */
31395     addxtype : function(cfg) {
31396         return this.layout.addxtype(cfg);
31397     
31398     }
31399 });
31400
31401 Roo.ScrollPanel = function(el, config, content){
31402     config = config || {};
31403     config.fitToFrame = true;
31404     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31405     
31406     this.el.dom.style.overflow = "hidden";
31407     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31408     this.el.removeClass("x-layout-inactive-content");
31409     this.el.on("mousewheel", this.onWheel, this);
31410
31411     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31412     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31413     up.unselectable(); down.unselectable();
31414     up.on("click", this.scrollUp, this);
31415     down.on("click", this.scrollDown, this);
31416     up.addClassOnOver("x-scroller-btn-over");
31417     down.addClassOnOver("x-scroller-btn-over");
31418     up.addClassOnClick("x-scroller-btn-click");
31419     down.addClassOnClick("x-scroller-btn-click");
31420     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31421
31422     this.resizeEl = this.el;
31423     this.el = wrap; this.up = up; this.down = down;
31424 };
31425
31426 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31427     increment : 100,
31428     wheelIncrement : 5,
31429     scrollUp : function(){
31430         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31431     },
31432
31433     scrollDown : function(){
31434         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31435     },
31436
31437     afterScroll : function(){
31438         var el = this.resizeEl;
31439         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31440         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31441         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31442     },
31443
31444     setSize : function(){
31445         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31446         this.afterScroll();
31447     },
31448
31449     onWheel : function(e){
31450         var d = e.getWheelDelta();
31451         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31452         this.afterScroll();
31453         e.stopEvent();
31454     },
31455
31456     setContent : function(content, loadScripts){
31457         this.resizeEl.update(content, loadScripts);
31458     }
31459
31460 });
31461
31462
31463
31464
31465
31466
31467
31468
31469
31470 /**
31471  * @class Roo.TreePanel
31472  * @extends Roo.ContentPanel
31473  * @constructor
31474  * Create a new TreePanel. - defaults to fit/scoll contents.
31475  * @param {String/Object} config A string to set only the panel's title, or a config object
31476  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31477  */
31478 Roo.TreePanel = function(config){
31479     var el = config.el;
31480     var tree = config.tree;
31481     delete config.tree; 
31482     delete config.el; // hopefull!
31483     
31484     // wrapper for IE7 strict & safari scroll issue
31485     
31486     var treeEl = el.createChild();
31487     config.resizeEl = treeEl;
31488     
31489     
31490     
31491     Roo.TreePanel.superclass.constructor.call(this, el, config);
31492  
31493  
31494     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31495     //console.log(tree);
31496     this.on('activate', function()
31497     {
31498         if (this.tree.rendered) {
31499             return;
31500         }
31501         //console.log('render tree');
31502         this.tree.render();
31503     });
31504     // this should not be needed.. - it's actually the 'el' that resizes?
31505     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31506     
31507     //this.on('resize',  function (cp, w, h) {
31508     //        this.tree.innerCt.setWidth(w);
31509     //        this.tree.innerCt.setHeight(h);
31510     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31511     //});
31512
31513         
31514     
31515 };
31516
31517 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31518     fitToFrame : true,
31519     autoScroll : true
31520 });
31521
31522
31523
31524
31525
31526
31527
31528
31529
31530
31531
31532 /*
31533  * Based on:
31534  * Ext JS Library 1.1.1
31535  * Copyright(c) 2006-2007, Ext JS, LLC.
31536  *
31537  * Originally Released Under LGPL - original licence link has changed is not relivant.
31538  *
31539  * Fork - LGPL
31540  * <script type="text/javascript">
31541  */
31542  
31543
31544 /**
31545  * @class Roo.ReaderLayout
31546  * @extends Roo.BorderLayout
31547  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31548  * center region containing two nested regions (a top one for a list view and one for item preview below),
31549  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31550  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31551  * expedites the setup of the overall layout and regions for this common application style.
31552  * Example:
31553  <pre><code>
31554 var reader = new Roo.ReaderLayout();
31555 var CP = Roo.ContentPanel;  // shortcut for adding
31556
31557 reader.beginUpdate();
31558 reader.add("north", new CP("north", "North"));
31559 reader.add("west", new CP("west", {title: "West"}));
31560 reader.add("east", new CP("east", {title: "East"}));
31561
31562 reader.regions.listView.add(new CP("listView", "List"));
31563 reader.regions.preview.add(new CP("preview", "Preview"));
31564 reader.endUpdate();
31565 </code></pre>
31566 * @constructor
31567 * Create a new ReaderLayout
31568 * @param {Object} config Configuration options
31569 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31570 * document.body if omitted)
31571 */
31572 Roo.ReaderLayout = function(config, renderTo){
31573     var c = config || {size:{}};
31574     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31575         north: c.north !== false ? Roo.apply({
31576             split:false,
31577             initialSize: 32,
31578             titlebar: false
31579         }, c.north) : false,
31580         west: c.west !== false ? Roo.apply({
31581             split:true,
31582             initialSize: 200,
31583             minSize: 175,
31584             maxSize: 400,
31585             titlebar: true,
31586             collapsible: true,
31587             animate: true,
31588             margins:{left:5,right:0,bottom:5,top:5},
31589             cmargins:{left:5,right:5,bottom:5,top:5}
31590         }, c.west) : false,
31591         east: c.east !== false ? Roo.apply({
31592             split:true,
31593             initialSize: 200,
31594             minSize: 175,
31595             maxSize: 400,
31596             titlebar: true,
31597             collapsible: true,
31598             animate: true,
31599             margins:{left:0,right:5,bottom:5,top:5},
31600             cmargins:{left:5,right:5,bottom:5,top:5}
31601         }, c.east) : false,
31602         center: Roo.apply({
31603             tabPosition: 'top',
31604             autoScroll:false,
31605             closeOnTab: true,
31606             titlebar:false,
31607             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31608         }, c.center)
31609     });
31610
31611     this.el.addClass('x-reader');
31612
31613     this.beginUpdate();
31614
31615     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31616         south: c.preview !== false ? Roo.apply({
31617             split:true,
31618             initialSize: 200,
31619             minSize: 100,
31620             autoScroll:true,
31621             collapsible:true,
31622             titlebar: true,
31623             cmargins:{top:5,left:0, right:0, bottom:0}
31624         }, c.preview) : false,
31625         center: Roo.apply({
31626             autoScroll:false,
31627             titlebar:false,
31628             minHeight:200
31629         }, c.listView)
31630     });
31631     this.add('center', new Roo.NestedLayoutPanel(inner,
31632             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31633
31634     this.endUpdate();
31635
31636     this.regions.preview = inner.getRegion('south');
31637     this.regions.listView = inner.getRegion('center');
31638 };
31639
31640 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31641  * Based on:
31642  * Ext JS Library 1.1.1
31643  * Copyright(c) 2006-2007, Ext JS, LLC.
31644  *
31645  * Originally Released Under LGPL - original licence link has changed is not relivant.
31646  *
31647  * Fork - LGPL
31648  * <script type="text/javascript">
31649  */
31650  
31651 /**
31652  * @class Roo.grid.Grid
31653  * @extends Roo.util.Observable
31654  * This class represents the primary interface of a component based grid control.
31655  * <br><br>Usage:<pre><code>
31656  var grid = new Roo.grid.Grid("my-container-id", {
31657      ds: myDataStore,
31658      cm: myColModel,
31659      selModel: mySelectionModel,
31660      autoSizeColumns: true,
31661      monitorWindowResize: false,
31662      trackMouseOver: true
31663  });
31664  // set any options
31665  grid.render();
31666  * </code></pre>
31667  * <b>Common Problems:</b><br/>
31668  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31669  * element will correct this<br/>
31670  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31671  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31672  * are unpredictable.<br/>
31673  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31674  * grid to calculate dimensions/offsets.<br/>
31675   * @constructor
31676  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31677  * The container MUST have some type of size defined for the grid to fill. The container will be
31678  * automatically set to position relative if it isn't already.
31679  * @param {Object} config A config object that sets properties on this grid.
31680  */
31681 Roo.grid.Grid = function(container, config){
31682         // initialize the container
31683         this.container = Roo.get(container);
31684         this.container.update("");
31685         this.container.setStyle("overflow", "hidden");
31686     this.container.addClass('x-grid-container');
31687
31688     this.id = this.container.id;
31689
31690     Roo.apply(this, config);
31691     // check and correct shorthanded configs
31692     if(this.ds){
31693         this.dataSource = this.ds;
31694         delete this.ds;
31695     }
31696     if(this.cm){
31697         this.colModel = this.cm;
31698         delete this.cm;
31699     }
31700     if(this.sm){
31701         this.selModel = this.sm;
31702         delete this.sm;
31703     }
31704
31705     if (this.selModel) {
31706         this.selModel = Roo.factory(this.selModel, Roo.grid);
31707         this.sm = this.selModel;
31708         this.sm.xmodule = this.xmodule || false;
31709     }
31710     if (typeof(this.colModel.config) == 'undefined') {
31711         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31712         this.cm = this.colModel;
31713         this.cm.xmodule = this.xmodule || false;
31714     }
31715     if (this.dataSource) {
31716         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31717         this.ds = this.dataSource;
31718         this.ds.xmodule = this.xmodule || false;
31719          
31720     }
31721     
31722     
31723     
31724     if(this.width){
31725         this.container.setWidth(this.width);
31726     }
31727
31728     if(this.height){
31729         this.container.setHeight(this.height);
31730     }
31731     /** @private */
31732         this.addEvents({
31733         // raw events
31734         /**
31735          * @event click
31736          * The raw click event for the entire grid.
31737          * @param {Roo.EventObject} e
31738          */
31739         "click" : true,
31740         /**
31741          * @event dblclick
31742          * The raw dblclick event for the entire grid.
31743          * @param {Roo.EventObject} e
31744          */
31745         "dblclick" : true,
31746         /**
31747          * @event contextmenu
31748          * The raw contextmenu event for the entire grid.
31749          * @param {Roo.EventObject} e
31750          */
31751         "contextmenu" : true,
31752         /**
31753          * @event mousedown
31754          * The raw mousedown event for the entire grid.
31755          * @param {Roo.EventObject} e
31756          */
31757         "mousedown" : true,
31758         /**
31759          * @event mouseup
31760          * The raw mouseup event for the entire grid.
31761          * @param {Roo.EventObject} e
31762          */
31763         "mouseup" : true,
31764         /**
31765          * @event mouseover
31766          * The raw mouseover event for the entire grid.
31767          * @param {Roo.EventObject} e
31768          */
31769         "mouseover" : true,
31770         /**
31771          * @event mouseout
31772          * The raw mouseout event for the entire grid.
31773          * @param {Roo.EventObject} e
31774          */
31775         "mouseout" : true,
31776         /**
31777          * @event keypress
31778          * The raw keypress event for the entire grid.
31779          * @param {Roo.EventObject} e
31780          */
31781         "keypress" : true,
31782         /**
31783          * @event keydown
31784          * The raw keydown event for the entire grid.
31785          * @param {Roo.EventObject} e
31786          */
31787         "keydown" : true,
31788
31789         // custom events
31790
31791         /**
31792          * @event cellclick
31793          * Fires when a cell is clicked
31794          * @param {Grid} this
31795          * @param {Number} rowIndex
31796          * @param {Number} columnIndex
31797          * @param {Roo.EventObject} e
31798          */
31799         "cellclick" : true,
31800         /**
31801          * @event celldblclick
31802          * Fires when a cell is double clicked
31803          * @param {Grid} this
31804          * @param {Number} rowIndex
31805          * @param {Number} columnIndex
31806          * @param {Roo.EventObject} e
31807          */
31808         "celldblclick" : true,
31809         /**
31810          * @event rowclick
31811          * Fires when a row is clicked
31812          * @param {Grid} this
31813          * @param {Number} rowIndex
31814          * @param {Roo.EventObject} e
31815          */
31816         "rowclick" : true,
31817         /**
31818          * @event rowdblclick
31819          * Fires when a row is double clicked
31820          * @param {Grid} this
31821          * @param {Number} rowIndex
31822          * @param {Roo.EventObject} e
31823          */
31824         "rowdblclick" : true,
31825         /**
31826          * @event headerclick
31827          * Fires when a header is clicked
31828          * @param {Grid} this
31829          * @param {Number} columnIndex
31830          * @param {Roo.EventObject} e
31831          */
31832         "headerclick" : true,
31833         /**
31834          * @event headerdblclick
31835          * Fires when a header cell is double clicked
31836          * @param {Grid} this
31837          * @param {Number} columnIndex
31838          * @param {Roo.EventObject} e
31839          */
31840         "headerdblclick" : true,
31841         /**
31842          * @event rowcontextmenu
31843          * Fires when a row is right clicked
31844          * @param {Grid} this
31845          * @param {Number} rowIndex
31846          * @param {Roo.EventObject} e
31847          */
31848         "rowcontextmenu" : true,
31849         /**
31850          * @event cellcontextmenu
31851          * Fires when a cell is right clicked
31852          * @param {Grid} this
31853          * @param {Number} rowIndex
31854          * @param {Number} cellIndex
31855          * @param {Roo.EventObject} e
31856          */
31857          "cellcontextmenu" : true,
31858         /**
31859          * @event headercontextmenu
31860          * Fires when a header is right clicked
31861          * @param {Grid} this
31862          * @param {Number} columnIndex
31863          * @param {Roo.EventObject} e
31864          */
31865         "headercontextmenu" : true,
31866         /**
31867          * @event bodyscroll
31868          * Fires when the body element is scrolled
31869          * @param {Number} scrollLeft
31870          * @param {Number} scrollTop
31871          */
31872         "bodyscroll" : true,
31873         /**
31874          * @event columnresize
31875          * Fires when the user resizes a column
31876          * @param {Number} columnIndex
31877          * @param {Number} newSize
31878          */
31879         "columnresize" : true,
31880         /**
31881          * @event columnmove
31882          * Fires when the user moves a column
31883          * @param {Number} oldIndex
31884          * @param {Number} newIndex
31885          */
31886         "columnmove" : true,
31887         /**
31888          * @event startdrag
31889          * Fires when row(s) start being dragged
31890          * @param {Grid} this
31891          * @param {Roo.GridDD} dd The drag drop object
31892          * @param {event} e The raw browser event
31893          */
31894         "startdrag" : true,
31895         /**
31896          * @event enddrag
31897          * Fires when a drag operation is complete
31898          * @param {Grid} this
31899          * @param {Roo.GridDD} dd The drag drop object
31900          * @param {event} e The raw browser event
31901          */
31902         "enddrag" : true,
31903         /**
31904          * @event dragdrop
31905          * Fires when dragged row(s) are dropped on a valid DD target
31906          * @param {Grid} this
31907          * @param {Roo.GridDD} dd The drag drop object
31908          * @param {String} targetId The target drag drop object
31909          * @param {event} e The raw browser event
31910          */
31911         "dragdrop" : true,
31912         /**
31913          * @event dragover
31914          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
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         "dragover" : true,
31921         /**
31922          * @event dragenter
31923          *  Fires when the dragged row(s) first cross another DD target while being dragged
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         "dragenter" : true,
31930         /**
31931          * @event dragout
31932          * Fires when the dragged row(s) leave 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         "dragout" : true,
31939         /**
31940          * @event rowclass
31941          * Fires when a row is rendered, so you can change add a style to it.
31942          * @param {GridView} gridview   The grid view
31943          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31944          */
31945         'rowclass' : true,
31946
31947         /**
31948          * @event render
31949          * Fires when the grid is rendered
31950          * @param {Grid} grid
31951          */
31952         'render' : true
31953     });
31954
31955     Roo.grid.Grid.superclass.constructor.call(this);
31956 };
31957 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31958     
31959     /**
31960      * @cfg {String} ddGroup - drag drop group.
31961      */
31962
31963     /**
31964      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31965      */
31966     minColumnWidth : 25,
31967
31968     /**
31969      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31970      * <b>on initial render.</b> It is more efficient to explicitly size the columns
31971      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31972      */
31973     autoSizeColumns : false,
31974
31975     /**
31976      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31977      */
31978     autoSizeHeaders : true,
31979
31980     /**
31981      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31982      */
31983     monitorWindowResize : true,
31984
31985     /**
31986      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31987      * rows measured to get a columns size. Default is 0 (all rows).
31988      */
31989     maxRowsToMeasure : 0,
31990
31991     /**
31992      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31993      */
31994     trackMouseOver : true,
31995
31996     /**
31997     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
31998     */
31999     
32000     /**
32001     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32002     */
32003     enableDragDrop : false,
32004     
32005     /**
32006     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32007     */
32008     enableColumnMove : true,
32009     
32010     /**
32011     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32012     */
32013     enableColumnHide : true,
32014     
32015     /**
32016     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32017     */
32018     enableRowHeightSync : false,
32019     
32020     /**
32021     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32022     */
32023     stripeRows : true,
32024     
32025     /**
32026     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32027     */
32028     autoHeight : false,
32029
32030     /**
32031      * @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.
32032      */
32033     autoExpandColumn : false,
32034
32035     /**
32036     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32037     * Default is 50.
32038     */
32039     autoExpandMin : 50,
32040
32041     /**
32042     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32043     */
32044     autoExpandMax : 1000,
32045
32046     /**
32047     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32048     */
32049     view : null,
32050
32051     /**
32052     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32053     */
32054     loadMask : false,
32055     /**
32056     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32057     */
32058     dropTarget: false,
32059     
32060    
32061     
32062     // private
32063     rendered : false,
32064
32065     /**
32066     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32067     * of a fixed width. Default is false.
32068     */
32069     /**
32070     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32071     */
32072     /**
32073      * Called once after all setup has been completed and the grid is ready to be rendered.
32074      * @return {Roo.grid.Grid} this
32075      */
32076     render : function()
32077     {
32078         var c = this.container;
32079         // try to detect autoHeight/width mode
32080         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32081             this.autoHeight = true;
32082         }
32083         var view = this.getView();
32084         view.init(this);
32085
32086         c.on("click", this.onClick, this);
32087         c.on("dblclick", this.onDblClick, this);
32088         c.on("contextmenu", this.onContextMenu, this);
32089         c.on("keydown", this.onKeyDown, this);
32090         if (Roo.isTouch) {
32091             c.on("touchstart", this.onTouchStart, this);
32092         }
32093
32094         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32095
32096         this.getSelectionModel().init(this);
32097
32098         view.render();
32099
32100         if(this.loadMask){
32101             this.loadMask = new Roo.LoadMask(this.container,
32102                     Roo.apply({store:this.dataSource}, this.loadMask));
32103         }
32104         
32105         
32106         if (this.toolbar && this.toolbar.xtype) {
32107             this.toolbar.container = this.getView().getHeaderPanel(true);
32108             this.toolbar = new Roo.Toolbar(this.toolbar);
32109         }
32110         if (this.footer && this.footer.xtype) {
32111             this.footer.dataSource = this.getDataSource();
32112             this.footer.container = this.getView().getFooterPanel(true);
32113             this.footer = Roo.factory(this.footer, Roo);
32114         }
32115         if (this.dropTarget && this.dropTarget.xtype) {
32116             delete this.dropTarget.xtype;
32117             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32118         }
32119         
32120         
32121         this.rendered = true;
32122         this.fireEvent('render', this);
32123         return this;
32124     },
32125
32126         /**
32127          * Reconfigures the grid to use a different Store and Column Model.
32128          * The View will be bound to the new objects and refreshed.
32129          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32130          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32131          */
32132     reconfigure : function(dataSource, colModel){
32133         if(this.loadMask){
32134             this.loadMask.destroy();
32135             this.loadMask = new Roo.LoadMask(this.container,
32136                     Roo.apply({store:dataSource}, this.loadMask));
32137         }
32138         this.view.bind(dataSource, colModel);
32139         this.dataSource = dataSource;
32140         this.colModel = colModel;
32141         this.view.refresh(true);
32142     },
32143
32144     // private
32145     onKeyDown : function(e){
32146         this.fireEvent("keydown", e);
32147     },
32148
32149     /**
32150      * Destroy this grid.
32151      * @param {Boolean} removeEl True to remove the element
32152      */
32153     destroy : function(removeEl, keepListeners){
32154         if(this.loadMask){
32155             this.loadMask.destroy();
32156         }
32157         var c = this.container;
32158         c.removeAllListeners();
32159         this.view.destroy();
32160         this.colModel.purgeListeners();
32161         if(!keepListeners){
32162             this.purgeListeners();
32163         }
32164         c.update("");
32165         if(removeEl === true){
32166             c.remove();
32167         }
32168     },
32169
32170     // private
32171     processEvent : function(name, e){
32172         // does this fire select???
32173         //Roo.log('grid:processEvent '  + name);
32174         
32175         if (name != 'touchstart' ) {
32176             this.fireEvent(name, e);    
32177         }
32178         
32179         var t = e.getTarget();
32180         var v = this.view;
32181         var header = v.findHeaderIndex(t);
32182         if(header !== false){
32183             var ename = name == 'touchstart' ? 'click' : name;
32184              
32185             this.fireEvent("header" + ename, this, header, e);
32186         }else{
32187             var row = v.findRowIndex(t);
32188             var cell = v.findCellIndex(t);
32189             if (name == 'touchstart') {
32190                 // first touch is always a click.
32191                 // hopefull this happens after selection is updated.?
32192                 name = false;
32193                 
32194                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32195                     var cs = this.selModel.getSelectedCell();
32196                     if (row == cs[0] && cell == cs[1]){
32197                         name = 'dblclick';
32198                     }
32199                 }
32200                 if (typeof(this.selModel.getSelections) != 'undefined') {
32201                     var cs = this.selModel.getSelections();
32202                     var ds = this.dataSource;
32203                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32204                         name = 'dblclick';
32205                     }
32206                 }
32207                 if (!name) {
32208                     return;
32209                 }
32210             }
32211             
32212             
32213             if(row !== false){
32214                 this.fireEvent("row" + name, this, row, e);
32215                 if(cell !== false){
32216                     this.fireEvent("cell" + name, this, row, cell, e);
32217                 }
32218             }
32219         }
32220     },
32221
32222     // private
32223     onClick : function(e){
32224         this.processEvent("click", e);
32225     },
32226    // private
32227     onTouchStart : function(e){
32228         this.processEvent("touchstart", e);
32229     },
32230
32231     // private
32232     onContextMenu : function(e, t){
32233         this.processEvent("contextmenu", e);
32234     },
32235
32236     // private
32237     onDblClick : function(e){
32238         this.processEvent("dblclick", e);
32239     },
32240
32241     // private
32242     walkCells : function(row, col, step, fn, scope){
32243         var cm = this.colModel, clen = cm.getColumnCount();
32244         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32245         if(step < 0){
32246             if(col < 0){
32247                 row--;
32248                 first = false;
32249             }
32250             while(row >= 0){
32251                 if(!first){
32252                     col = clen-1;
32253                 }
32254                 first = false;
32255                 while(col >= 0){
32256                     if(fn.call(scope || this, row, col, cm) === true){
32257                         return [row, col];
32258                     }
32259                     col--;
32260                 }
32261                 row--;
32262             }
32263         } else {
32264             if(col >= clen){
32265                 row++;
32266                 first = false;
32267             }
32268             while(row < rlen){
32269                 if(!first){
32270                     col = 0;
32271                 }
32272                 first = false;
32273                 while(col < clen){
32274                     if(fn.call(scope || this, row, col, cm) === true){
32275                         return [row, col];
32276                     }
32277                     col++;
32278                 }
32279                 row++;
32280             }
32281         }
32282         return null;
32283     },
32284
32285     // private
32286     getSelections : function(){
32287         return this.selModel.getSelections();
32288     },
32289
32290     /**
32291      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32292      * but if manual update is required this method will initiate it.
32293      */
32294     autoSize : function(){
32295         if(this.rendered){
32296             this.view.layout();
32297             if(this.view.adjustForScroll){
32298                 this.view.adjustForScroll();
32299             }
32300         }
32301     },
32302
32303     /**
32304      * Returns the grid's underlying element.
32305      * @return {Element} The element
32306      */
32307     getGridEl : function(){
32308         return this.container;
32309     },
32310
32311     // private for compatibility, overridden by editor grid
32312     stopEditing : function(){},
32313
32314     /**
32315      * Returns the grid's SelectionModel.
32316      * @return {SelectionModel}
32317      */
32318     getSelectionModel : function(){
32319         if(!this.selModel){
32320             this.selModel = new Roo.grid.RowSelectionModel();
32321         }
32322         return this.selModel;
32323     },
32324
32325     /**
32326      * Returns the grid's DataSource.
32327      * @return {DataSource}
32328      */
32329     getDataSource : function(){
32330         return this.dataSource;
32331     },
32332
32333     /**
32334      * Returns the grid's ColumnModel.
32335      * @return {ColumnModel}
32336      */
32337     getColumnModel : function(){
32338         return this.colModel;
32339     },
32340
32341     /**
32342      * Returns the grid's GridView object.
32343      * @return {GridView}
32344      */
32345     getView : function(){
32346         if(!this.view){
32347             this.view = new Roo.grid.GridView(this.viewConfig);
32348         }
32349         return this.view;
32350     },
32351     /**
32352      * Called to get grid's drag proxy text, by default returns this.ddText.
32353      * @return {String}
32354      */
32355     getDragDropText : function(){
32356         var count = this.selModel.getCount();
32357         return String.format(this.ddText, count, count == 1 ? '' : 's');
32358     }
32359 });
32360 /**
32361  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32362  * %0 is replaced with the number of selected rows.
32363  * @type String
32364  */
32365 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32366  * Based on:
32367  * Ext JS Library 1.1.1
32368  * Copyright(c) 2006-2007, Ext JS, LLC.
32369  *
32370  * Originally Released Under LGPL - original licence link has changed is not relivant.
32371  *
32372  * Fork - LGPL
32373  * <script type="text/javascript">
32374  */
32375  
32376 Roo.grid.AbstractGridView = function(){
32377         this.grid = null;
32378         
32379         this.events = {
32380             "beforerowremoved" : true,
32381             "beforerowsinserted" : true,
32382             "beforerefresh" : true,
32383             "rowremoved" : true,
32384             "rowsinserted" : true,
32385             "rowupdated" : true,
32386             "refresh" : true
32387         };
32388     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32389 };
32390
32391 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32392     rowClass : "x-grid-row",
32393     cellClass : "x-grid-cell",
32394     tdClass : "x-grid-td",
32395     hdClass : "x-grid-hd",
32396     splitClass : "x-grid-hd-split",
32397     
32398     init: function(grid){
32399         this.grid = grid;
32400                 var cid = this.grid.getGridEl().id;
32401         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32402         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32403         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32404         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32405         },
32406         
32407     getColumnRenderers : function(){
32408         var renderers = [];
32409         var cm = this.grid.colModel;
32410         var colCount = cm.getColumnCount();
32411         for(var i = 0; i < colCount; i++){
32412             renderers[i] = cm.getRenderer(i);
32413         }
32414         return renderers;
32415     },
32416     
32417     getColumnIds : function(){
32418         var ids = [];
32419         var cm = this.grid.colModel;
32420         var colCount = cm.getColumnCount();
32421         for(var i = 0; i < colCount; i++){
32422             ids[i] = cm.getColumnId(i);
32423         }
32424         return ids;
32425     },
32426     
32427     getDataIndexes : function(){
32428         if(!this.indexMap){
32429             this.indexMap = this.buildIndexMap();
32430         }
32431         return this.indexMap.colToData;
32432     },
32433     
32434     getColumnIndexByDataIndex : function(dataIndex){
32435         if(!this.indexMap){
32436             this.indexMap = this.buildIndexMap();
32437         }
32438         return this.indexMap.dataToCol[dataIndex];
32439     },
32440     
32441     /**
32442      * Set a css style for a column dynamically. 
32443      * @param {Number} colIndex The index of the column
32444      * @param {String} name The css property name
32445      * @param {String} value The css value
32446      */
32447     setCSSStyle : function(colIndex, name, value){
32448         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32449         Roo.util.CSS.updateRule(selector, name, value);
32450     },
32451     
32452     generateRules : function(cm){
32453         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32454         Roo.util.CSS.removeStyleSheet(rulesId);
32455         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32456             var cid = cm.getColumnId(i);
32457             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32458                          this.tdSelector, cid, " {\n}\n",
32459                          this.hdSelector, cid, " {\n}\n",
32460                          this.splitSelector, cid, " {\n}\n");
32461         }
32462         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32463     }
32464 });/*
32465  * Based on:
32466  * Ext JS Library 1.1.1
32467  * Copyright(c) 2006-2007, Ext JS, LLC.
32468  *
32469  * Originally Released Under LGPL - original licence link has changed is not relivant.
32470  *
32471  * Fork - LGPL
32472  * <script type="text/javascript">
32473  */
32474
32475 // private
32476 // This is a support class used internally by the Grid components
32477 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32478     this.grid = grid;
32479     this.view = grid.getView();
32480     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32481     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32482     if(hd2){
32483         this.setHandleElId(Roo.id(hd));
32484         this.setOuterHandleElId(Roo.id(hd2));
32485     }
32486     this.scroll = false;
32487 };
32488 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32489     maxDragWidth: 120,
32490     getDragData : function(e){
32491         var t = Roo.lib.Event.getTarget(e);
32492         var h = this.view.findHeaderCell(t);
32493         if(h){
32494             return {ddel: h.firstChild, header:h};
32495         }
32496         return false;
32497     },
32498
32499     onInitDrag : function(e){
32500         this.view.headersDisabled = true;
32501         var clone = this.dragData.ddel.cloneNode(true);
32502         clone.id = Roo.id();
32503         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32504         this.proxy.update(clone);
32505         return true;
32506     },
32507
32508     afterValidDrop : function(){
32509         var v = this.view;
32510         setTimeout(function(){
32511             v.headersDisabled = false;
32512         }, 50);
32513     },
32514
32515     afterInvalidDrop : function(){
32516         var v = this.view;
32517         setTimeout(function(){
32518             v.headersDisabled = false;
32519         }, 50);
32520     }
32521 });
32522 /*
32523  * Based on:
32524  * Ext JS Library 1.1.1
32525  * Copyright(c) 2006-2007, Ext JS, LLC.
32526  *
32527  * Originally Released Under LGPL - original licence link has changed is not relivant.
32528  *
32529  * Fork - LGPL
32530  * <script type="text/javascript">
32531  */
32532 // private
32533 // This is a support class used internally by the Grid components
32534 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32535     this.grid = grid;
32536     this.view = grid.getView();
32537     // split the proxies so they don't interfere with mouse events
32538     this.proxyTop = Roo.DomHelper.append(document.body, {
32539         cls:"col-move-top", html:"&#160;"
32540     }, true);
32541     this.proxyBottom = Roo.DomHelper.append(document.body, {
32542         cls:"col-move-bottom", html:"&#160;"
32543     }, true);
32544     this.proxyTop.hide = this.proxyBottom.hide = function(){
32545         this.setLeftTop(-100,-100);
32546         this.setStyle("visibility", "hidden");
32547     };
32548     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32549     // temporarily disabled
32550     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32551     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32552 };
32553 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32554     proxyOffsets : [-4, -9],
32555     fly: Roo.Element.fly,
32556
32557     getTargetFromEvent : function(e){
32558         var t = Roo.lib.Event.getTarget(e);
32559         var cindex = this.view.findCellIndex(t);
32560         if(cindex !== false){
32561             return this.view.getHeaderCell(cindex);
32562         }
32563         return null;
32564     },
32565
32566     nextVisible : function(h){
32567         var v = this.view, cm = this.grid.colModel;
32568         h = h.nextSibling;
32569         while(h){
32570             if(!cm.isHidden(v.getCellIndex(h))){
32571                 return h;
32572             }
32573             h = h.nextSibling;
32574         }
32575         return null;
32576     },
32577
32578     prevVisible : function(h){
32579         var v = this.view, cm = this.grid.colModel;
32580         h = h.prevSibling;
32581         while(h){
32582             if(!cm.isHidden(v.getCellIndex(h))){
32583                 return h;
32584             }
32585             h = h.prevSibling;
32586         }
32587         return null;
32588     },
32589
32590     positionIndicator : function(h, n, e){
32591         var x = Roo.lib.Event.getPageX(e);
32592         var r = Roo.lib.Dom.getRegion(n.firstChild);
32593         var px, pt, py = r.top + this.proxyOffsets[1];
32594         if((r.right - x) <= (r.right-r.left)/2){
32595             px = r.right+this.view.borderWidth;
32596             pt = "after";
32597         }else{
32598             px = r.left;
32599             pt = "before";
32600         }
32601         var oldIndex = this.view.getCellIndex(h);
32602         var newIndex = this.view.getCellIndex(n);
32603
32604         if(this.grid.colModel.isFixed(newIndex)){
32605             return false;
32606         }
32607
32608         var locked = this.grid.colModel.isLocked(newIndex);
32609
32610         if(pt == "after"){
32611             newIndex++;
32612         }
32613         if(oldIndex < newIndex){
32614             newIndex--;
32615         }
32616         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32617             return false;
32618         }
32619         px +=  this.proxyOffsets[0];
32620         this.proxyTop.setLeftTop(px, py);
32621         this.proxyTop.show();
32622         if(!this.bottomOffset){
32623             this.bottomOffset = this.view.mainHd.getHeight();
32624         }
32625         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32626         this.proxyBottom.show();
32627         return pt;
32628     },
32629
32630     onNodeEnter : function(n, dd, e, data){
32631         if(data.header != n){
32632             this.positionIndicator(data.header, n, e);
32633         }
32634     },
32635
32636     onNodeOver : function(n, dd, e, data){
32637         var result = false;
32638         if(data.header != n){
32639             result = this.positionIndicator(data.header, n, e);
32640         }
32641         if(!result){
32642             this.proxyTop.hide();
32643             this.proxyBottom.hide();
32644         }
32645         return result ? this.dropAllowed : this.dropNotAllowed;
32646     },
32647
32648     onNodeOut : function(n, dd, e, data){
32649         this.proxyTop.hide();
32650         this.proxyBottom.hide();
32651     },
32652
32653     onNodeDrop : function(n, dd, e, data){
32654         var h = data.header;
32655         if(h != n){
32656             var cm = this.grid.colModel;
32657             var x = Roo.lib.Event.getPageX(e);
32658             var r = Roo.lib.Dom.getRegion(n.firstChild);
32659             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32660             var oldIndex = this.view.getCellIndex(h);
32661             var newIndex = this.view.getCellIndex(n);
32662             var locked = cm.isLocked(newIndex);
32663             if(pt == "after"){
32664                 newIndex++;
32665             }
32666             if(oldIndex < newIndex){
32667                 newIndex--;
32668             }
32669             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32670                 return false;
32671             }
32672             cm.setLocked(oldIndex, locked, true);
32673             cm.moveColumn(oldIndex, newIndex);
32674             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32675             return true;
32676         }
32677         return false;
32678     }
32679 });
32680 /*
32681  * Based on:
32682  * Ext JS Library 1.1.1
32683  * Copyright(c) 2006-2007, Ext JS, LLC.
32684  *
32685  * Originally Released Under LGPL - original licence link has changed is not relivant.
32686  *
32687  * Fork - LGPL
32688  * <script type="text/javascript">
32689  */
32690   
32691 /**
32692  * @class Roo.grid.GridView
32693  * @extends Roo.util.Observable
32694  *
32695  * @constructor
32696  * @param {Object} config
32697  */
32698 Roo.grid.GridView = function(config){
32699     Roo.grid.GridView.superclass.constructor.call(this);
32700     this.el = null;
32701
32702     Roo.apply(this, config);
32703 };
32704
32705 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32706
32707     unselectable :  'unselectable="on"',
32708     unselectableCls :  'x-unselectable',
32709     
32710     
32711     rowClass : "x-grid-row",
32712
32713     cellClass : "x-grid-col",
32714
32715     tdClass : "x-grid-td",
32716
32717     hdClass : "x-grid-hd",
32718
32719     splitClass : "x-grid-split",
32720
32721     sortClasses : ["sort-asc", "sort-desc"],
32722
32723     enableMoveAnim : false,
32724
32725     hlColor: "C3DAF9",
32726
32727     dh : Roo.DomHelper,
32728
32729     fly : Roo.Element.fly,
32730
32731     css : Roo.util.CSS,
32732
32733     borderWidth: 1,
32734
32735     splitOffset: 3,
32736
32737     scrollIncrement : 22,
32738
32739     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32740
32741     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32742
32743     bind : function(ds, cm){
32744         if(this.ds){
32745             this.ds.un("load", this.onLoad, this);
32746             this.ds.un("datachanged", this.onDataChange, this);
32747             this.ds.un("add", this.onAdd, this);
32748             this.ds.un("remove", this.onRemove, this);
32749             this.ds.un("update", this.onUpdate, this);
32750             this.ds.un("clear", this.onClear, this);
32751         }
32752         if(ds){
32753             ds.on("load", this.onLoad, this);
32754             ds.on("datachanged", this.onDataChange, this);
32755             ds.on("add", this.onAdd, this);
32756             ds.on("remove", this.onRemove, this);
32757             ds.on("update", this.onUpdate, this);
32758             ds.on("clear", this.onClear, this);
32759         }
32760         this.ds = ds;
32761
32762         if(this.cm){
32763             this.cm.un("widthchange", this.onColWidthChange, this);
32764             this.cm.un("headerchange", this.onHeaderChange, this);
32765             this.cm.un("hiddenchange", this.onHiddenChange, this);
32766             this.cm.un("columnmoved", this.onColumnMove, this);
32767             this.cm.un("columnlockchange", this.onColumnLock, this);
32768         }
32769         if(cm){
32770             this.generateRules(cm);
32771             cm.on("widthchange", this.onColWidthChange, this);
32772             cm.on("headerchange", this.onHeaderChange, this);
32773             cm.on("hiddenchange", this.onHiddenChange, this);
32774             cm.on("columnmoved", this.onColumnMove, this);
32775             cm.on("columnlockchange", this.onColumnLock, this);
32776         }
32777         this.cm = cm;
32778     },
32779
32780     init: function(grid){
32781         Roo.grid.GridView.superclass.init.call(this, grid);
32782
32783         this.bind(grid.dataSource, grid.colModel);
32784
32785         grid.on("headerclick", this.handleHeaderClick, this);
32786
32787         if(grid.trackMouseOver){
32788             grid.on("mouseover", this.onRowOver, this);
32789             grid.on("mouseout", this.onRowOut, this);
32790         }
32791         grid.cancelTextSelection = function(){};
32792         this.gridId = grid.id;
32793
32794         var tpls = this.templates || {};
32795
32796         if(!tpls.master){
32797             tpls.master = new Roo.Template(
32798                '<div class="x-grid" hidefocus="true">',
32799                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32800                   '<div class="x-grid-topbar"></div>',
32801                   '<div class="x-grid-scroller"><div></div></div>',
32802                   '<div class="x-grid-locked">',
32803                       '<div class="x-grid-header">{lockedHeader}</div>',
32804                       '<div class="x-grid-body">{lockedBody}</div>',
32805                   "</div>",
32806                   '<div class="x-grid-viewport">',
32807                       '<div class="x-grid-header">{header}</div>',
32808                       '<div class="x-grid-body">{body}</div>',
32809                   "</div>",
32810                   '<div class="x-grid-bottombar"></div>',
32811                  
32812                   '<div class="x-grid-resize-proxy">&#160;</div>',
32813                "</div>"
32814             );
32815             tpls.master.disableformats = true;
32816         }
32817
32818         if(!tpls.header){
32819             tpls.header = new Roo.Template(
32820                '<table border="0" cellspacing="0" cellpadding="0">',
32821                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32822                "</table>{splits}"
32823             );
32824             tpls.header.disableformats = true;
32825         }
32826         tpls.header.compile();
32827
32828         if(!tpls.hcell){
32829             tpls.hcell = new Roo.Template(
32830                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32831                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32832                 "</div></td>"
32833              );
32834              tpls.hcell.disableFormats = true;
32835         }
32836         tpls.hcell.compile();
32837
32838         if(!tpls.hsplit){
32839             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32840                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32841             tpls.hsplit.disableFormats = true;
32842         }
32843         tpls.hsplit.compile();
32844
32845         if(!tpls.body){
32846             tpls.body = new Roo.Template(
32847                '<table border="0" cellspacing="0" cellpadding="0">',
32848                "<tbody>{rows}</tbody>",
32849                "</table>"
32850             );
32851             tpls.body.disableFormats = true;
32852         }
32853         tpls.body.compile();
32854
32855         if(!tpls.row){
32856             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32857             tpls.row.disableFormats = true;
32858         }
32859         tpls.row.compile();
32860
32861         if(!tpls.cell){
32862             tpls.cell = new Roo.Template(
32863                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32864                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32865                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32866                 "</td>"
32867             );
32868             tpls.cell.disableFormats = true;
32869         }
32870         tpls.cell.compile();
32871
32872         this.templates = tpls;
32873     },
32874
32875     // remap these for backwards compat
32876     onColWidthChange : function(){
32877         this.updateColumns.apply(this, arguments);
32878     },
32879     onHeaderChange : function(){
32880         this.updateHeaders.apply(this, arguments);
32881     }, 
32882     onHiddenChange : function(){
32883         this.handleHiddenChange.apply(this, arguments);
32884     },
32885     onColumnMove : function(){
32886         this.handleColumnMove.apply(this, arguments);
32887     },
32888     onColumnLock : function(){
32889         this.handleLockChange.apply(this, arguments);
32890     },
32891
32892     onDataChange : function(){
32893         this.refresh();
32894         this.updateHeaderSortState();
32895     },
32896
32897     onClear : function(){
32898         this.refresh();
32899     },
32900
32901     onUpdate : function(ds, record){
32902         this.refreshRow(record);
32903     },
32904
32905     refreshRow : function(record){
32906         var ds = this.ds, index;
32907         if(typeof record == 'number'){
32908             index = record;
32909             record = ds.getAt(index);
32910         }else{
32911             index = ds.indexOf(record);
32912         }
32913         this.insertRows(ds, index, index, true);
32914         this.onRemove(ds, record, index+1, true);
32915         this.syncRowHeights(index, index);
32916         this.layout();
32917         this.fireEvent("rowupdated", this, index, record);
32918     },
32919
32920     onAdd : function(ds, records, index){
32921         this.insertRows(ds, index, index + (records.length-1));
32922     },
32923
32924     onRemove : function(ds, record, index, isUpdate){
32925         if(isUpdate !== true){
32926             this.fireEvent("beforerowremoved", this, index, record);
32927         }
32928         var bt = this.getBodyTable(), lt = this.getLockedTable();
32929         if(bt.rows[index]){
32930             bt.firstChild.removeChild(bt.rows[index]);
32931         }
32932         if(lt.rows[index]){
32933             lt.firstChild.removeChild(lt.rows[index]);
32934         }
32935         if(isUpdate !== true){
32936             this.stripeRows(index);
32937             this.syncRowHeights(index, index);
32938             this.layout();
32939             this.fireEvent("rowremoved", this, index, record);
32940         }
32941     },
32942
32943     onLoad : function(){
32944         this.scrollToTop();
32945     },
32946
32947     /**
32948      * Scrolls the grid to the top
32949      */
32950     scrollToTop : function(){
32951         if(this.scroller){
32952             this.scroller.dom.scrollTop = 0;
32953             this.syncScroll();
32954         }
32955     },
32956
32957     /**
32958      * Gets a panel in the header of the grid that can be used for toolbars etc.
32959      * After modifying the contents of this panel a call to grid.autoSize() may be
32960      * required to register any changes in size.
32961      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32962      * @return Roo.Element
32963      */
32964     getHeaderPanel : function(doShow){
32965         if(doShow){
32966             this.headerPanel.show();
32967         }
32968         return this.headerPanel;
32969     },
32970
32971     /**
32972      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32973      * After modifying the contents of this panel a call to grid.autoSize() may be
32974      * required to register any changes in size.
32975      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32976      * @return Roo.Element
32977      */
32978     getFooterPanel : function(doShow){
32979         if(doShow){
32980             this.footerPanel.show();
32981         }
32982         return this.footerPanel;
32983     },
32984
32985     initElements : function(){
32986         var E = Roo.Element;
32987         var el = this.grid.getGridEl().dom.firstChild;
32988         var cs = el.childNodes;
32989
32990         this.el = new E(el);
32991         
32992          this.focusEl = new E(el.firstChild);
32993         this.focusEl.swallowEvent("click", true);
32994         
32995         this.headerPanel = new E(cs[1]);
32996         this.headerPanel.enableDisplayMode("block");
32997
32998         this.scroller = new E(cs[2]);
32999         this.scrollSizer = new E(this.scroller.dom.firstChild);
33000
33001         this.lockedWrap = new E(cs[3]);
33002         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33003         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33004
33005         this.mainWrap = new E(cs[4]);
33006         this.mainHd = new E(this.mainWrap.dom.firstChild);
33007         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33008
33009         this.footerPanel = new E(cs[5]);
33010         this.footerPanel.enableDisplayMode("block");
33011
33012         this.resizeProxy = new E(cs[6]);
33013
33014         this.headerSelector = String.format(
33015            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33016            this.lockedHd.id, this.mainHd.id
33017         );
33018
33019         this.splitterSelector = String.format(
33020            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33021            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33022         );
33023     },
33024     idToCssName : function(s)
33025     {
33026         return s.replace(/[^a-z0-9]+/ig, '-');
33027     },
33028
33029     getHeaderCell : function(index){
33030         return Roo.DomQuery.select(this.headerSelector)[index];
33031     },
33032
33033     getHeaderCellMeasure : function(index){
33034         return this.getHeaderCell(index).firstChild;
33035     },
33036
33037     getHeaderCellText : function(index){
33038         return this.getHeaderCell(index).firstChild.firstChild;
33039     },
33040
33041     getLockedTable : function(){
33042         return this.lockedBody.dom.firstChild;
33043     },
33044
33045     getBodyTable : function(){
33046         return this.mainBody.dom.firstChild;
33047     },
33048
33049     getLockedRow : function(index){
33050         return this.getLockedTable().rows[index];
33051     },
33052
33053     getRow : function(index){
33054         return this.getBodyTable().rows[index];
33055     },
33056
33057     getRowComposite : function(index){
33058         if(!this.rowEl){
33059             this.rowEl = new Roo.CompositeElementLite();
33060         }
33061         var els = [], lrow, mrow;
33062         if(lrow = this.getLockedRow(index)){
33063             els.push(lrow);
33064         }
33065         if(mrow = this.getRow(index)){
33066             els.push(mrow);
33067         }
33068         this.rowEl.elements = els;
33069         return this.rowEl;
33070     },
33071     /**
33072      * Gets the 'td' of the cell
33073      * 
33074      * @param {Integer} rowIndex row to select
33075      * @param {Integer} colIndex column to select
33076      * 
33077      * @return {Object} 
33078      */
33079     getCell : function(rowIndex, colIndex){
33080         var locked = this.cm.getLockedCount();
33081         var source;
33082         if(colIndex < locked){
33083             source = this.lockedBody.dom.firstChild;
33084         }else{
33085             source = this.mainBody.dom.firstChild;
33086             colIndex -= locked;
33087         }
33088         return source.rows[rowIndex].childNodes[colIndex];
33089     },
33090
33091     getCellText : function(rowIndex, colIndex){
33092         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33093     },
33094
33095     getCellBox : function(cell){
33096         var b = this.fly(cell).getBox();
33097         if(Roo.isOpera){ // opera fails to report the Y
33098             b.y = cell.offsetTop + this.mainBody.getY();
33099         }
33100         return b;
33101     },
33102
33103     getCellIndex : function(cell){
33104         var id = String(cell.className).match(this.cellRE);
33105         if(id){
33106             return parseInt(id[1], 10);
33107         }
33108         return 0;
33109     },
33110
33111     findHeaderIndex : function(n){
33112         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33113         return r ? this.getCellIndex(r) : false;
33114     },
33115
33116     findHeaderCell : function(n){
33117         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33118         return r ? r : false;
33119     },
33120
33121     findRowIndex : function(n){
33122         if(!n){
33123             return false;
33124         }
33125         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33126         return r ? r.rowIndex : false;
33127     },
33128
33129     findCellIndex : function(node){
33130         var stop = this.el.dom;
33131         while(node && node != stop){
33132             if(this.findRE.test(node.className)){
33133                 return this.getCellIndex(node);
33134             }
33135             node = node.parentNode;
33136         }
33137         return false;
33138     },
33139
33140     getColumnId : function(index){
33141         return this.cm.getColumnId(index);
33142     },
33143
33144     getSplitters : function()
33145     {
33146         if(this.splitterSelector){
33147            return Roo.DomQuery.select(this.splitterSelector);
33148         }else{
33149             return null;
33150       }
33151     },
33152
33153     getSplitter : function(index){
33154         return this.getSplitters()[index];
33155     },
33156
33157     onRowOver : function(e, t){
33158         var row;
33159         if((row = this.findRowIndex(t)) !== false){
33160             this.getRowComposite(row).addClass("x-grid-row-over");
33161         }
33162     },
33163
33164     onRowOut : function(e, t){
33165         var row;
33166         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33167             this.getRowComposite(row).removeClass("x-grid-row-over");
33168         }
33169     },
33170
33171     renderHeaders : function(){
33172         var cm = this.cm;
33173         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33174         var cb = [], lb = [], sb = [], lsb = [], p = {};
33175         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33176             p.cellId = "x-grid-hd-0-" + i;
33177             p.splitId = "x-grid-csplit-0-" + i;
33178             p.id = cm.getColumnId(i);
33179             p.value = cm.getColumnHeader(i) || "";
33180             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33181             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33182             if(!cm.isLocked(i)){
33183                 cb[cb.length] = ct.apply(p);
33184                 sb[sb.length] = st.apply(p);
33185             }else{
33186                 lb[lb.length] = ct.apply(p);
33187                 lsb[lsb.length] = st.apply(p);
33188             }
33189         }
33190         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33191                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33192     },
33193
33194     updateHeaders : function(){
33195         var html = this.renderHeaders();
33196         this.lockedHd.update(html[0]);
33197         this.mainHd.update(html[1]);
33198     },
33199
33200     /**
33201      * Focuses the specified row.
33202      * @param {Number} row The row index
33203      */
33204     focusRow : function(row)
33205     {
33206         //Roo.log('GridView.focusRow');
33207         var x = this.scroller.dom.scrollLeft;
33208         this.focusCell(row, 0, false);
33209         this.scroller.dom.scrollLeft = x;
33210     },
33211
33212     /**
33213      * Focuses the specified cell.
33214      * @param {Number} row The row index
33215      * @param {Number} col The column index
33216      * @param {Boolean} hscroll false to disable horizontal scrolling
33217      */
33218     focusCell : function(row, col, hscroll)
33219     {
33220         //Roo.log('GridView.focusCell');
33221         var el = this.ensureVisible(row, col, hscroll);
33222         this.focusEl.alignTo(el, "tl-tl");
33223         if(Roo.isGecko){
33224             this.focusEl.focus();
33225         }else{
33226             this.focusEl.focus.defer(1, this.focusEl);
33227         }
33228     },
33229
33230     /**
33231      * Scrolls the specified cell into view
33232      * @param {Number} row The row index
33233      * @param {Number} col The column index
33234      * @param {Boolean} hscroll false to disable horizontal scrolling
33235      */
33236     ensureVisible : function(row, col, hscroll)
33237     {
33238         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33239         //return null; //disable for testing.
33240         if(typeof row != "number"){
33241             row = row.rowIndex;
33242         }
33243         if(row < 0 && row >= this.ds.getCount()){
33244             return  null;
33245         }
33246         col = (col !== undefined ? col : 0);
33247         var cm = this.grid.colModel;
33248         while(cm.isHidden(col)){
33249             col++;
33250         }
33251
33252         var el = this.getCell(row, col);
33253         if(!el){
33254             return null;
33255         }
33256         var c = this.scroller.dom;
33257
33258         var ctop = parseInt(el.offsetTop, 10);
33259         var cleft = parseInt(el.offsetLeft, 10);
33260         var cbot = ctop + el.offsetHeight;
33261         var cright = cleft + el.offsetWidth;
33262         
33263         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33264         var stop = parseInt(c.scrollTop, 10);
33265         var sleft = parseInt(c.scrollLeft, 10);
33266         var sbot = stop + ch;
33267         var sright = sleft + c.clientWidth;
33268         /*
33269         Roo.log('GridView.ensureVisible:' +
33270                 ' ctop:' + ctop +
33271                 ' c.clientHeight:' + c.clientHeight +
33272                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33273                 ' stop:' + stop +
33274                 ' cbot:' + cbot +
33275                 ' sbot:' + sbot +
33276                 ' ch:' + ch  
33277                 );
33278         */
33279         if(ctop < stop){
33280              c.scrollTop = ctop;
33281             //Roo.log("set scrolltop to ctop DISABLE?");
33282         }else if(cbot > sbot){
33283             //Roo.log("set scrolltop to cbot-ch");
33284             c.scrollTop = cbot-ch;
33285         }
33286         
33287         if(hscroll !== false){
33288             if(cleft < sleft){
33289                 c.scrollLeft = cleft;
33290             }else if(cright > sright){
33291                 c.scrollLeft = cright-c.clientWidth;
33292             }
33293         }
33294          
33295         return el;
33296     },
33297
33298     updateColumns : function(){
33299         this.grid.stopEditing();
33300         var cm = this.grid.colModel, colIds = this.getColumnIds();
33301         //var totalWidth = cm.getTotalWidth();
33302         var pos = 0;
33303         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33304             //if(cm.isHidden(i)) continue;
33305             var w = cm.getColumnWidth(i);
33306             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33307             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33308         }
33309         this.updateSplitters();
33310     },
33311
33312     generateRules : function(cm){
33313         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33314         Roo.util.CSS.removeStyleSheet(rulesId);
33315         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33316             var cid = cm.getColumnId(i);
33317             var align = '';
33318             if(cm.config[i].align){
33319                 align = 'text-align:'+cm.config[i].align+';';
33320             }
33321             var hidden = '';
33322             if(cm.isHidden(i)){
33323                 hidden = 'display:none;';
33324             }
33325             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33326             ruleBuf.push(
33327                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33328                     this.hdSelector, cid, " {\n", align, width, "}\n",
33329                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33330                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33331         }
33332         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33333     },
33334
33335     updateSplitters : function(){
33336         var cm = this.cm, s = this.getSplitters();
33337         if(s){ // splitters not created yet
33338             var pos = 0, locked = true;
33339             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33340                 if(cm.isHidden(i)) {
33341                     continue;
33342                 }
33343                 var w = cm.getColumnWidth(i); // make sure it's a number
33344                 if(!cm.isLocked(i) && locked){
33345                     pos = 0;
33346                     locked = false;
33347                 }
33348                 pos += w;
33349                 s[i].style.left = (pos-this.splitOffset) + "px";
33350             }
33351         }
33352     },
33353
33354     handleHiddenChange : function(colModel, colIndex, hidden){
33355         if(hidden){
33356             this.hideColumn(colIndex);
33357         }else{
33358             this.unhideColumn(colIndex);
33359         }
33360     },
33361
33362     hideColumn : function(colIndex){
33363         var cid = this.getColumnId(colIndex);
33364         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33365         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33366         if(Roo.isSafari){
33367             this.updateHeaders();
33368         }
33369         this.updateSplitters();
33370         this.layout();
33371     },
33372
33373     unhideColumn : function(colIndex){
33374         var cid = this.getColumnId(colIndex);
33375         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33376         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33377
33378         if(Roo.isSafari){
33379             this.updateHeaders();
33380         }
33381         this.updateSplitters();
33382         this.layout();
33383     },
33384
33385     insertRows : function(dm, firstRow, lastRow, isUpdate){
33386         if(firstRow == 0 && lastRow == dm.getCount()-1){
33387             this.refresh();
33388         }else{
33389             if(!isUpdate){
33390                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33391             }
33392             var s = this.getScrollState();
33393             var markup = this.renderRows(firstRow, lastRow);
33394             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33395             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33396             this.restoreScroll(s);
33397             if(!isUpdate){
33398                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33399                 this.syncRowHeights(firstRow, lastRow);
33400                 this.stripeRows(firstRow);
33401                 this.layout();
33402             }
33403         }
33404     },
33405
33406     bufferRows : function(markup, target, index){
33407         var before = null, trows = target.rows, tbody = target.tBodies[0];
33408         if(index < trows.length){
33409             before = trows[index];
33410         }
33411         var b = document.createElement("div");
33412         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33413         var rows = b.firstChild.rows;
33414         for(var i = 0, len = rows.length; i < len; i++){
33415             if(before){
33416                 tbody.insertBefore(rows[0], before);
33417             }else{
33418                 tbody.appendChild(rows[0]);
33419             }
33420         }
33421         b.innerHTML = "";
33422         b = null;
33423     },
33424
33425     deleteRows : function(dm, firstRow, lastRow){
33426         if(dm.getRowCount()<1){
33427             this.fireEvent("beforerefresh", this);
33428             this.mainBody.update("");
33429             this.lockedBody.update("");
33430             this.fireEvent("refresh", this);
33431         }else{
33432             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33433             var bt = this.getBodyTable();
33434             var tbody = bt.firstChild;
33435             var rows = bt.rows;
33436             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33437                 tbody.removeChild(rows[firstRow]);
33438             }
33439             this.stripeRows(firstRow);
33440             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33441         }
33442     },
33443
33444     updateRows : function(dataSource, firstRow, lastRow){
33445         var s = this.getScrollState();
33446         this.refresh();
33447         this.restoreScroll(s);
33448     },
33449
33450     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33451         if(!noRefresh){
33452            this.refresh();
33453         }
33454         this.updateHeaderSortState();
33455     },
33456
33457     getScrollState : function(){
33458         
33459         var sb = this.scroller.dom;
33460         return {left: sb.scrollLeft, top: sb.scrollTop};
33461     },
33462
33463     stripeRows : function(startRow){
33464         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33465             return;
33466         }
33467         startRow = startRow || 0;
33468         var rows = this.getBodyTable().rows;
33469         var lrows = this.getLockedTable().rows;
33470         var cls = ' x-grid-row-alt ';
33471         for(var i = startRow, len = rows.length; i < len; i++){
33472             var row = rows[i], lrow = lrows[i];
33473             var isAlt = ((i+1) % 2 == 0);
33474             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33475             if(isAlt == hasAlt){
33476                 continue;
33477             }
33478             if(isAlt){
33479                 row.className += " x-grid-row-alt";
33480             }else{
33481                 row.className = row.className.replace("x-grid-row-alt", "");
33482             }
33483             if(lrow){
33484                 lrow.className = row.className;
33485             }
33486         }
33487     },
33488
33489     restoreScroll : function(state){
33490         //Roo.log('GridView.restoreScroll');
33491         var sb = this.scroller.dom;
33492         sb.scrollLeft = state.left;
33493         sb.scrollTop = state.top;
33494         this.syncScroll();
33495     },
33496
33497     syncScroll : function(){
33498         //Roo.log('GridView.syncScroll');
33499         var sb = this.scroller.dom;
33500         var sh = this.mainHd.dom;
33501         var bs = this.mainBody.dom;
33502         var lv = this.lockedBody.dom;
33503         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33504         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33505     },
33506
33507     handleScroll : function(e){
33508         this.syncScroll();
33509         var sb = this.scroller.dom;
33510         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33511         e.stopEvent();
33512     },
33513
33514     handleWheel : function(e){
33515         var d = e.getWheelDelta();
33516         this.scroller.dom.scrollTop -= d*22;
33517         // set this here to prevent jumpy scrolling on large tables
33518         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33519         e.stopEvent();
33520     },
33521
33522     renderRows : function(startRow, endRow){
33523         // pull in all the crap needed to render rows
33524         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33525         var colCount = cm.getColumnCount();
33526
33527         if(ds.getCount() < 1){
33528             return ["", ""];
33529         }
33530
33531         // build a map for all the columns
33532         var cs = [];
33533         for(var i = 0; i < colCount; i++){
33534             var name = cm.getDataIndex(i);
33535             cs[i] = {
33536                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33537                 renderer : cm.getRenderer(i),
33538                 id : cm.getColumnId(i),
33539                 locked : cm.isLocked(i),
33540                 has_editor : cm.isCellEditable(i)
33541             };
33542         }
33543
33544         startRow = startRow || 0;
33545         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33546
33547         // records to render
33548         var rs = ds.getRange(startRow, endRow);
33549
33550         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33551     },
33552
33553     // As much as I hate to duplicate code, this was branched because FireFox really hates
33554     // [].join("") on strings. The performance difference was substantial enough to
33555     // branch this function
33556     doRender : Roo.isGecko ?
33557             function(cs, rs, ds, startRow, colCount, stripe){
33558                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33559                 // buffers
33560                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33561                 
33562                 var hasListener = this.grid.hasListener('rowclass');
33563                 var rowcfg = {};
33564                 for(var j = 0, len = rs.length; j < len; j++){
33565                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33566                     for(var i = 0; i < colCount; i++){
33567                         c = cs[i];
33568                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33569                         p.id = c.id;
33570                         p.css = p.attr = "";
33571                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33572                         if(p.value == undefined || p.value === "") {
33573                             p.value = "&#160;";
33574                         }
33575                         if(c.has_editor){
33576                             p.css += ' x-grid-editable-cell';
33577                         }
33578                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33579                             p.css +=  ' x-grid-dirty-cell';
33580                         }
33581                         var markup = ct.apply(p);
33582                         if(!c.locked){
33583                             cb+= markup;
33584                         }else{
33585                             lcb+= markup;
33586                         }
33587                     }
33588                     var alt = [];
33589                     if(stripe && ((rowIndex+1) % 2 == 0)){
33590                         alt.push("x-grid-row-alt")
33591                     }
33592                     if(r.dirty){
33593                         alt.push(  " x-grid-dirty-row");
33594                     }
33595                     rp.cells = lcb;
33596                     if(this.getRowClass){
33597                         alt.push(this.getRowClass(r, rowIndex));
33598                     }
33599                     if (hasListener) {
33600                         rowcfg = {
33601                              
33602                             record: r,
33603                             rowIndex : rowIndex,
33604                             rowClass : ''
33605                         };
33606                         this.grid.fireEvent('rowclass', this, rowcfg);
33607                         alt.push(rowcfg.rowClass);
33608                     }
33609                     rp.alt = alt.join(" ");
33610                     lbuf+= rt.apply(rp);
33611                     rp.cells = cb;
33612                     buf+=  rt.apply(rp);
33613                 }
33614                 return [lbuf, buf];
33615             } :
33616             function(cs, rs, ds, startRow, colCount, stripe){
33617                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33618                 // buffers
33619                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33620                 var hasListener = this.grid.hasListener('rowclass');
33621  
33622                 var rowcfg = {};
33623                 for(var j = 0, len = rs.length; j < len; j++){
33624                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33625                     for(var i = 0; i < colCount; i++){
33626                         c = cs[i];
33627                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33628                         p.id = c.id;
33629                         p.css = p.attr = "";
33630                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33631                         if(p.value == undefined || p.value === "") {
33632                             p.value = "&#160;";
33633                         }
33634                         //Roo.log(c);
33635                          if(c.has_editor){
33636                             p.css += ' x-grid-editable-cell';
33637                         }
33638                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33639                             p.css += ' x-grid-dirty-cell' 
33640                         }
33641                         
33642                         var markup = ct.apply(p);
33643                         if(!c.locked){
33644                             cb[cb.length] = markup;
33645                         }else{
33646                             lcb[lcb.length] = markup;
33647                         }
33648                     }
33649                     var alt = [];
33650                     if(stripe && ((rowIndex+1) % 2 == 0)){
33651                         alt.push( "x-grid-row-alt");
33652                     }
33653                     if(r.dirty){
33654                         alt.push(" x-grid-dirty-row");
33655                     }
33656                     rp.cells = lcb;
33657                     if(this.getRowClass){
33658                         alt.push( this.getRowClass(r, rowIndex));
33659                     }
33660                     if (hasListener) {
33661                         rowcfg = {
33662                              
33663                             record: r,
33664                             rowIndex : rowIndex,
33665                             rowClass : ''
33666                         };
33667                         this.grid.fireEvent('rowclass', this, rowcfg);
33668                         alt.push(rowcfg.rowClass);
33669                     }
33670                     
33671                     rp.alt = alt.join(" ");
33672                     rp.cells = lcb.join("");
33673                     lbuf[lbuf.length] = rt.apply(rp);
33674                     rp.cells = cb.join("");
33675                     buf[buf.length] =  rt.apply(rp);
33676                 }
33677                 return [lbuf.join(""), buf.join("")];
33678             },
33679
33680     renderBody : function(){
33681         var markup = this.renderRows();
33682         var bt = this.templates.body;
33683         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33684     },
33685
33686     /**
33687      * Refreshes the grid
33688      * @param {Boolean} headersToo
33689      */
33690     refresh : function(headersToo){
33691         this.fireEvent("beforerefresh", this);
33692         this.grid.stopEditing();
33693         var result = this.renderBody();
33694         this.lockedBody.update(result[0]);
33695         this.mainBody.update(result[1]);
33696         if(headersToo === true){
33697             this.updateHeaders();
33698             this.updateColumns();
33699             this.updateSplitters();
33700             this.updateHeaderSortState();
33701         }
33702         this.syncRowHeights();
33703         this.layout();
33704         this.fireEvent("refresh", this);
33705     },
33706
33707     handleColumnMove : function(cm, oldIndex, newIndex){
33708         this.indexMap = null;
33709         var s = this.getScrollState();
33710         this.refresh(true);
33711         this.restoreScroll(s);
33712         this.afterMove(newIndex);
33713     },
33714
33715     afterMove : function(colIndex){
33716         if(this.enableMoveAnim && Roo.enableFx){
33717             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33718         }
33719         // if multisort - fix sortOrder, and reload..
33720         if (this.grid.dataSource.multiSort) {
33721             // the we can call sort again..
33722             var dm = this.grid.dataSource;
33723             var cm = this.grid.colModel;
33724             var so = [];
33725             for(var i = 0; i < cm.config.length; i++ ) {
33726                 
33727                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33728                     continue; // dont' bother, it's not in sort list or being set.
33729                 }
33730                 
33731                 so.push(cm.config[i].dataIndex);
33732             };
33733             dm.sortOrder = so;
33734             dm.load(dm.lastOptions);
33735             
33736             
33737         }
33738         
33739     },
33740
33741     updateCell : function(dm, rowIndex, dataIndex){
33742         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33743         if(typeof colIndex == "undefined"){ // not present in grid
33744             return;
33745         }
33746         var cm = this.grid.colModel;
33747         var cell = this.getCell(rowIndex, colIndex);
33748         var cellText = this.getCellText(rowIndex, colIndex);
33749
33750         var p = {
33751             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33752             id : cm.getColumnId(colIndex),
33753             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33754         };
33755         var renderer = cm.getRenderer(colIndex);
33756         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33757         if(typeof val == "undefined" || val === "") {
33758             val = "&#160;";
33759         }
33760         cellText.innerHTML = val;
33761         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33762         this.syncRowHeights(rowIndex, rowIndex);
33763     },
33764
33765     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33766         var maxWidth = 0;
33767         if(this.grid.autoSizeHeaders){
33768             var h = this.getHeaderCellMeasure(colIndex);
33769             maxWidth = Math.max(maxWidth, h.scrollWidth);
33770         }
33771         var tb, index;
33772         if(this.cm.isLocked(colIndex)){
33773             tb = this.getLockedTable();
33774             index = colIndex;
33775         }else{
33776             tb = this.getBodyTable();
33777             index = colIndex - this.cm.getLockedCount();
33778         }
33779         if(tb && tb.rows){
33780             var rows = tb.rows;
33781             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33782             for(var i = 0; i < stopIndex; i++){
33783                 var cell = rows[i].childNodes[index].firstChild;
33784                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33785             }
33786         }
33787         return maxWidth + /*margin for error in IE*/ 5;
33788     },
33789     /**
33790      * Autofit a column to its content.
33791      * @param {Number} colIndex
33792      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33793      */
33794      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33795          if(this.cm.isHidden(colIndex)){
33796              return; // can't calc a hidden column
33797          }
33798         if(forceMinSize){
33799             var cid = this.cm.getColumnId(colIndex);
33800             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33801            if(this.grid.autoSizeHeaders){
33802                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33803            }
33804         }
33805         var newWidth = this.calcColumnWidth(colIndex);
33806         this.cm.setColumnWidth(colIndex,
33807             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33808         if(!suppressEvent){
33809             this.grid.fireEvent("columnresize", colIndex, newWidth);
33810         }
33811     },
33812
33813     /**
33814      * Autofits all columns to their content and then expands to fit any extra space in the grid
33815      */
33816      autoSizeColumns : function(){
33817         var cm = this.grid.colModel;
33818         var colCount = cm.getColumnCount();
33819         for(var i = 0; i < colCount; i++){
33820             this.autoSizeColumn(i, true, true);
33821         }
33822         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33823             this.fitColumns();
33824         }else{
33825             this.updateColumns();
33826             this.layout();
33827         }
33828     },
33829
33830     /**
33831      * Autofits all columns to the grid's width proportionate with their current size
33832      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33833      */
33834     fitColumns : function(reserveScrollSpace){
33835         var cm = this.grid.colModel;
33836         var colCount = cm.getColumnCount();
33837         var cols = [];
33838         var width = 0;
33839         var i, w;
33840         for (i = 0; i < colCount; i++){
33841             if(!cm.isHidden(i) && !cm.isFixed(i)){
33842                 w = cm.getColumnWidth(i);
33843                 cols.push(i);
33844                 cols.push(w);
33845                 width += w;
33846             }
33847         }
33848         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33849         if(reserveScrollSpace){
33850             avail -= 17;
33851         }
33852         var frac = (avail - cm.getTotalWidth())/width;
33853         while (cols.length){
33854             w = cols.pop();
33855             i = cols.pop();
33856             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33857         }
33858         this.updateColumns();
33859         this.layout();
33860     },
33861
33862     onRowSelect : function(rowIndex){
33863         var row = this.getRowComposite(rowIndex);
33864         row.addClass("x-grid-row-selected");
33865     },
33866
33867     onRowDeselect : function(rowIndex){
33868         var row = this.getRowComposite(rowIndex);
33869         row.removeClass("x-grid-row-selected");
33870     },
33871
33872     onCellSelect : function(row, col){
33873         var cell = this.getCell(row, col);
33874         if(cell){
33875             Roo.fly(cell).addClass("x-grid-cell-selected");
33876         }
33877     },
33878
33879     onCellDeselect : function(row, col){
33880         var cell = this.getCell(row, col);
33881         if(cell){
33882             Roo.fly(cell).removeClass("x-grid-cell-selected");
33883         }
33884     },
33885
33886     updateHeaderSortState : function(){
33887         
33888         // sort state can be single { field: xxx, direction : yyy}
33889         // or   { xxx=>ASC , yyy : DESC ..... }
33890         
33891         var mstate = {};
33892         if (!this.ds.multiSort) { 
33893             var state = this.ds.getSortState();
33894             if(!state){
33895                 return;
33896             }
33897             mstate[state.field] = state.direction;
33898             // FIXME... - this is not used here.. but might be elsewhere..
33899             this.sortState = state;
33900             
33901         } else {
33902             mstate = this.ds.sortToggle;
33903         }
33904         //remove existing sort classes..
33905         
33906         var sc = this.sortClasses;
33907         var hds = this.el.select(this.headerSelector).removeClass(sc);
33908         
33909         for(var f in mstate) {
33910         
33911             var sortColumn = this.cm.findColumnIndex(f);
33912             
33913             if(sortColumn != -1){
33914                 var sortDir = mstate[f];        
33915                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33916             }
33917         }
33918         
33919          
33920         
33921     },
33922
33923
33924     handleHeaderClick : function(g, index,e){
33925         
33926         Roo.log("header click");
33927         
33928         if (Roo.isTouch) {
33929             // touch events on header are handled by context
33930             this.handleHdCtx(g,index,e);
33931             return;
33932         }
33933         
33934         
33935         if(this.headersDisabled){
33936             return;
33937         }
33938         var dm = g.dataSource, cm = g.colModel;
33939         if(!cm.isSortable(index)){
33940             return;
33941         }
33942         g.stopEditing();
33943         
33944         if (dm.multiSort) {
33945             // update the sortOrder
33946             var so = [];
33947             for(var i = 0; i < cm.config.length; i++ ) {
33948                 
33949                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33950                     continue; // dont' bother, it's not in sort list or being set.
33951                 }
33952                 
33953                 so.push(cm.config[i].dataIndex);
33954             };
33955             dm.sortOrder = so;
33956         }
33957         
33958         
33959         dm.sort(cm.getDataIndex(index));
33960     },
33961
33962
33963     destroy : function(){
33964         if(this.colMenu){
33965             this.colMenu.removeAll();
33966             Roo.menu.MenuMgr.unregister(this.colMenu);
33967             this.colMenu.getEl().remove();
33968             delete this.colMenu;
33969         }
33970         if(this.hmenu){
33971             this.hmenu.removeAll();
33972             Roo.menu.MenuMgr.unregister(this.hmenu);
33973             this.hmenu.getEl().remove();
33974             delete this.hmenu;
33975         }
33976         if(this.grid.enableColumnMove){
33977             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33978             if(dds){
33979                 for(var dd in dds){
33980                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33981                         var elid = dds[dd].dragElId;
33982                         dds[dd].unreg();
33983                         Roo.get(elid).remove();
33984                     } else if(dds[dd].config.isTarget){
33985                         dds[dd].proxyTop.remove();
33986                         dds[dd].proxyBottom.remove();
33987                         dds[dd].unreg();
33988                     }
33989                     if(Roo.dd.DDM.locationCache[dd]){
33990                         delete Roo.dd.DDM.locationCache[dd];
33991                     }
33992                 }
33993                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33994             }
33995         }
33996         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33997         this.bind(null, null);
33998         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33999     },
34000
34001     handleLockChange : function(){
34002         this.refresh(true);
34003     },
34004
34005     onDenyColumnLock : function(){
34006
34007     },
34008
34009     onDenyColumnHide : function(){
34010
34011     },
34012
34013     handleHdMenuClick : function(item){
34014         var index = this.hdCtxIndex;
34015         var cm = this.cm, ds = this.ds;
34016         switch(item.id){
34017             case "asc":
34018                 ds.sort(cm.getDataIndex(index), "ASC");
34019                 break;
34020             case "desc":
34021                 ds.sort(cm.getDataIndex(index), "DESC");
34022                 break;
34023             case "lock":
34024                 var lc = cm.getLockedCount();
34025                 if(cm.getColumnCount(true) <= lc+1){
34026                     this.onDenyColumnLock();
34027                     return;
34028                 }
34029                 if(lc != index){
34030                     cm.setLocked(index, true, true);
34031                     cm.moveColumn(index, lc);
34032                     this.grid.fireEvent("columnmove", index, lc);
34033                 }else{
34034                     cm.setLocked(index, true);
34035                 }
34036             break;
34037             case "unlock":
34038                 var lc = cm.getLockedCount();
34039                 if((lc-1) != index){
34040                     cm.setLocked(index, false, true);
34041                     cm.moveColumn(index, lc-1);
34042                     this.grid.fireEvent("columnmove", index, lc-1);
34043                 }else{
34044                     cm.setLocked(index, false);
34045                 }
34046             break;
34047             case 'wider': // used to expand cols on touch..
34048             case 'narrow':
34049                 var cw = cm.getColumnWidth(index);
34050                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34051                 cw = Math.max(0, cw);
34052                 cw = Math.min(cw,4000);
34053                 cm.setColumnWidth(index, cw);
34054                 break;
34055                 
34056             default:
34057                 index = cm.getIndexById(item.id.substr(4));
34058                 if(index != -1){
34059                     if(item.checked && cm.getColumnCount(true) <= 1){
34060                         this.onDenyColumnHide();
34061                         return false;
34062                     }
34063                     cm.setHidden(index, item.checked);
34064                 }
34065         }
34066         return true;
34067     },
34068
34069     beforeColMenuShow : function(){
34070         var cm = this.cm,  colCount = cm.getColumnCount();
34071         this.colMenu.removeAll();
34072         for(var i = 0; i < colCount; i++){
34073             this.colMenu.add(new Roo.menu.CheckItem({
34074                 id: "col-"+cm.getColumnId(i),
34075                 text: cm.getColumnHeader(i),
34076                 checked: !cm.isHidden(i),
34077                 hideOnClick:false
34078             }));
34079         }
34080     },
34081
34082     handleHdCtx : function(g, index, e){
34083         e.stopEvent();
34084         var hd = this.getHeaderCell(index);
34085         this.hdCtxIndex = index;
34086         var ms = this.hmenu.items, cm = this.cm;
34087         ms.get("asc").setDisabled(!cm.isSortable(index));
34088         ms.get("desc").setDisabled(!cm.isSortable(index));
34089         if(this.grid.enableColLock !== false){
34090             ms.get("lock").setDisabled(cm.isLocked(index));
34091             ms.get("unlock").setDisabled(!cm.isLocked(index));
34092         }
34093         this.hmenu.show(hd, "tl-bl");
34094     },
34095
34096     handleHdOver : function(e){
34097         var hd = this.findHeaderCell(e.getTarget());
34098         if(hd && !this.headersDisabled){
34099             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34100                this.fly(hd).addClass("x-grid-hd-over");
34101             }
34102         }
34103     },
34104
34105     handleHdOut : function(e){
34106         var hd = this.findHeaderCell(e.getTarget());
34107         if(hd){
34108             this.fly(hd).removeClass("x-grid-hd-over");
34109         }
34110     },
34111
34112     handleSplitDblClick : function(e, t){
34113         var i = this.getCellIndex(t);
34114         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34115             this.autoSizeColumn(i, true);
34116             this.layout();
34117         }
34118     },
34119
34120     render : function(){
34121
34122         var cm = this.cm;
34123         var colCount = cm.getColumnCount();
34124
34125         if(this.grid.monitorWindowResize === true){
34126             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34127         }
34128         var header = this.renderHeaders();
34129         var body = this.templates.body.apply({rows:""});
34130         var html = this.templates.master.apply({
34131             lockedBody: body,
34132             body: body,
34133             lockedHeader: header[0],
34134             header: header[1]
34135         });
34136
34137         //this.updateColumns();
34138
34139         this.grid.getGridEl().dom.innerHTML = html;
34140
34141         this.initElements();
34142         
34143         // a kludge to fix the random scolling effect in webkit
34144         this.el.on("scroll", function() {
34145             this.el.dom.scrollTop=0; // hopefully not recursive..
34146         },this);
34147
34148         this.scroller.on("scroll", this.handleScroll, this);
34149         this.lockedBody.on("mousewheel", this.handleWheel, this);
34150         this.mainBody.on("mousewheel", this.handleWheel, this);
34151
34152         this.mainHd.on("mouseover", this.handleHdOver, this);
34153         this.mainHd.on("mouseout", this.handleHdOut, this);
34154         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34155                 {delegate: "."+this.splitClass});
34156
34157         this.lockedHd.on("mouseover", this.handleHdOver, this);
34158         this.lockedHd.on("mouseout", this.handleHdOut, this);
34159         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34160                 {delegate: "."+this.splitClass});
34161
34162         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34163             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34164         }
34165
34166         this.updateSplitters();
34167
34168         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34169             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34170             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34171         }
34172
34173         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34174             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34175             this.hmenu.add(
34176                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34177                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34178             );
34179             if(this.grid.enableColLock !== false){
34180                 this.hmenu.add('-',
34181                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34182                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34183                 );
34184             }
34185             if (Roo.isTouch) {
34186                  this.hmenu.add('-',
34187                     {id:"wider", text: this.columnsWiderText},
34188                     {id:"narrow", text: this.columnsNarrowText }
34189                 );
34190                 
34191                  
34192             }
34193             
34194             if(this.grid.enableColumnHide !== false){
34195
34196                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34197                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34198                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34199
34200                 this.hmenu.add('-',
34201                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34202                 );
34203             }
34204             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34205
34206             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34207         }
34208
34209         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34210             this.dd = new Roo.grid.GridDragZone(this.grid, {
34211                 ddGroup : this.grid.ddGroup || 'GridDD'
34212             });
34213             
34214         }
34215
34216         /*
34217         for(var i = 0; i < colCount; i++){
34218             if(cm.isHidden(i)){
34219                 this.hideColumn(i);
34220             }
34221             if(cm.config[i].align){
34222                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34223                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34224             }
34225         }*/
34226         
34227         this.updateHeaderSortState();
34228
34229         this.beforeInitialResize();
34230         this.layout(true);
34231
34232         // two part rendering gives faster view to the user
34233         this.renderPhase2.defer(1, this);
34234     },
34235
34236     renderPhase2 : function(){
34237         // render the rows now
34238         this.refresh();
34239         if(this.grid.autoSizeColumns){
34240             this.autoSizeColumns();
34241         }
34242     },
34243
34244     beforeInitialResize : function(){
34245
34246     },
34247
34248     onColumnSplitterMoved : function(i, w){
34249         this.userResized = true;
34250         var cm = this.grid.colModel;
34251         cm.setColumnWidth(i, w, true);
34252         var cid = cm.getColumnId(i);
34253         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34254         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34255         this.updateSplitters();
34256         this.layout();
34257         this.grid.fireEvent("columnresize", i, w);
34258     },
34259
34260     syncRowHeights : function(startIndex, endIndex){
34261         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34262             startIndex = startIndex || 0;
34263             var mrows = this.getBodyTable().rows;
34264             var lrows = this.getLockedTable().rows;
34265             var len = mrows.length-1;
34266             endIndex = Math.min(endIndex || len, len);
34267             for(var i = startIndex; i <= endIndex; i++){
34268                 var m = mrows[i], l = lrows[i];
34269                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34270                 m.style.height = l.style.height = h + "px";
34271             }
34272         }
34273     },
34274
34275     layout : function(initialRender, is2ndPass){
34276         var g = this.grid;
34277         var auto = g.autoHeight;
34278         var scrollOffset = 16;
34279         var c = g.getGridEl(), cm = this.cm,
34280                 expandCol = g.autoExpandColumn,
34281                 gv = this;
34282         //c.beginMeasure();
34283
34284         if(!c.dom.offsetWidth){ // display:none?
34285             if(initialRender){
34286                 this.lockedWrap.show();
34287                 this.mainWrap.show();
34288             }
34289             return;
34290         }
34291
34292         var hasLock = this.cm.isLocked(0);
34293
34294         var tbh = this.headerPanel.getHeight();
34295         var bbh = this.footerPanel.getHeight();
34296
34297         if(auto){
34298             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34299             var newHeight = ch + c.getBorderWidth("tb");
34300             if(g.maxHeight){
34301                 newHeight = Math.min(g.maxHeight, newHeight);
34302             }
34303             c.setHeight(newHeight);
34304         }
34305
34306         if(g.autoWidth){
34307             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34308         }
34309
34310         var s = this.scroller;
34311
34312         var csize = c.getSize(true);
34313
34314         this.el.setSize(csize.width, csize.height);
34315
34316         this.headerPanel.setWidth(csize.width);
34317         this.footerPanel.setWidth(csize.width);
34318
34319         var hdHeight = this.mainHd.getHeight();
34320         var vw = csize.width;
34321         var vh = csize.height - (tbh + bbh);
34322
34323         s.setSize(vw, vh);
34324
34325         var bt = this.getBodyTable();
34326         
34327         if(cm.getLockedCount() == cm.config.length){
34328             bt = this.getLockedTable();
34329         }
34330         
34331         var ltWidth = hasLock ?
34332                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34333
34334         var scrollHeight = bt.offsetHeight;
34335         var scrollWidth = ltWidth + bt.offsetWidth;
34336         var vscroll = false, hscroll = false;
34337
34338         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34339
34340         var lw = this.lockedWrap, mw = this.mainWrap;
34341         var lb = this.lockedBody, mb = this.mainBody;
34342
34343         setTimeout(function(){
34344             var t = s.dom.offsetTop;
34345             var w = s.dom.clientWidth,
34346                 h = s.dom.clientHeight;
34347
34348             lw.setTop(t);
34349             lw.setSize(ltWidth, h);
34350
34351             mw.setLeftTop(ltWidth, t);
34352             mw.setSize(w-ltWidth, h);
34353
34354             lb.setHeight(h-hdHeight);
34355             mb.setHeight(h-hdHeight);
34356
34357             if(is2ndPass !== true && !gv.userResized && expandCol){
34358                 // high speed resize without full column calculation
34359                 
34360                 var ci = cm.getIndexById(expandCol);
34361                 if (ci < 0) {
34362                     ci = cm.findColumnIndex(expandCol);
34363                 }
34364                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34365                 var expandId = cm.getColumnId(ci);
34366                 var  tw = cm.getTotalWidth(false);
34367                 var currentWidth = cm.getColumnWidth(ci);
34368                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34369                 if(currentWidth != cw){
34370                     cm.setColumnWidth(ci, cw, true);
34371                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34372                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34373                     gv.updateSplitters();
34374                     gv.layout(false, true);
34375                 }
34376             }
34377
34378             if(initialRender){
34379                 lw.show();
34380                 mw.show();
34381             }
34382             //c.endMeasure();
34383         }, 10);
34384     },
34385
34386     onWindowResize : function(){
34387         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34388             return;
34389         }
34390         this.layout();
34391     },
34392
34393     appendFooter : function(parentEl){
34394         return null;
34395     },
34396
34397     sortAscText : "Sort Ascending",
34398     sortDescText : "Sort Descending",
34399     lockText : "Lock Column",
34400     unlockText : "Unlock Column",
34401     columnsText : "Columns",
34402  
34403     columnsWiderText : "Wider",
34404     columnsNarrowText : "Thinner"
34405 });
34406
34407
34408 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34409     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34410     this.proxy.el.addClass('x-grid3-col-dd');
34411 };
34412
34413 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34414     handleMouseDown : function(e){
34415
34416     },
34417
34418     callHandleMouseDown : function(e){
34419         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34420     }
34421 });
34422 /*
34423  * Based on:
34424  * Ext JS Library 1.1.1
34425  * Copyright(c) 2006-2007, Ext JS, LLC.
34426  *
34427  * Originally Released Under LGPL - original licence link has changed is not relivant.
34428  *
34429  * Fork - LGPL
34430  * <script type="text/javascript">
34431  */
34432  
34433 // private
34434 // This is a support class used internally by the Grid components
34435 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34436     this.grid = grid;
34437     this.view = grid.getView();
34438     this.proxy = this.view.resizeProxy;
34439     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34440         "gridSplitters" + this.grid.getGridEl().id, {
34441         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34442     });
34443     this.setHandleElId(Roo.id(hd));
34444     this.setOuterHandleElId(Roo.id(hd2));
34445     this.scroll = false;
34446 };
34447 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34448     fly: Roo.Element.fly,
34449
34450     b4StartDrag : function(x, y){
34451         this.view.headersDisabled = true;
34452         this.proxy.setHeight(this.view.mainWrap.getHeight());
34453         var w = this.cm.getColumnWidth(this.cellIndex);
34454         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34455         this.resetConstraints();
34456         this.setXConstraint(minw, 1000);
34457         this.setYConstraint(0, 0);
34458         this.minX = x - minw;
34459         this.maxX = x + 1000;
34460         this.startPos = x;
34461         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34462     },
34463
34464
34465     handleMouseDown : function(e){
34466         ev = Roo.EventObject.setEvent(e);
34467         var t = this.fly(ev.getTarget());
34468         if(t.hasClass("x-grid-split")){
34469             this.cellIndex = this.view.getCellIndex(t.dom);
34470             this.split = t.dom;
34471             this.cm = this.grid.colModel;
34472             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34473                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34474             }
34475         }
34476     },
34477
34478     endDrag : function(e){
34479         this.view.headersDisabled = false;
34480         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34481         var diff = endX - this.startPos;
34482         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34483     },
34484
34485     autoOffset : function(){
34486         this.setDelta(0,0);
34487     }
34488 });/*
34489  * Based on:
34490  * Ext JS Library 1.1.1
34491  * Copyright(c) 2006-2007, Ext JS, LLC.
34492  *
34493  * Originally Released Under LGPL - original licence link has changed is not relivant.
34494  *
34495  * Fork - LGPL
34496  * <script type="text/javascript">
34497  */
34498  
34499 // private
34500 // This is a support class used internally by the Grid components
34501 Roo.grid.GridDragZone = function(grid, config){
34502     this.view = grid.getView();
34503     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34504     if(this.view.lockedBody){
34505         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34506         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34507     }
34508     this.scroll = false;
34509     this.grid = grid;
34510     this.ddel = document.createElement('div');
34511     this.ddel.className = 'x-grid-dd-wrap';
34512 };
34513
34514 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34515     ddGroup : "GridDD",
34516
34517     getDragData : function(e){
34518         var t = Roo.lib.Event.getTarget(e);
34519         var rowIndex = this.view.findRowIndex(t);
34520         var sm = this.grid.selModel;
34521             
34522         //Roo.log(rowIndex);
34523         
34524         if (sm.getSelectedCell) {
34525             // cell selection..
34526             if (!sm.getSelectedCell()) {
34527                 return false;
34528             }
34529             if (rowIndex != sm.getSelectedCell()[0]) {
34530                 return false;
34531             }
34532         
34533         }
34534         
34535         if(rowIndex !== false){
34536             
34537             // if editorgrid.. 
34538             
34539             
34540             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34541                
34542             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34543               //  
34544             //}
34545             if (e.hasModifier()){
34546                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34547             }
34548             
34549             Roo.log("getDragData");
34550             
34551             return {
34552                 grid: this.grid,
34553                 ddel: this.ddel,
34554                 rowIndex: rowIndex,
34555                 selections:sm.getSelections ? sm.getSelections() : (
34556                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34557                 )
34558             };
34559         }
34560         return false;
34561     },
34562
34563     onInitDrag : function(e){
34564         var data = this.dragData;
34565         this.ddel.innerHTML = this.grid.getDragDropText();
34566         this.proxy.update(this.ddel);
34567         // fire start drag?
34568     },
34569
34570     afterRepair : function(){
34571         this.dragging = false;
34572     },
34573
34574     getRepairXY : function(e, data){
34575         return false;
34576     },
34577
34578     onEndDrag : function(data, e){
34579         // fire end drag?
34580     },
34581
34582     onValidDrop : function(dd, e, id){
34583         // fire drag drop?
34584         this.hideProxy();
34585     },
34586
34587     beforeInvalidDrop : function(e, id){
34588
34589     }
34590 });/*
34591  * Based on:
34592  * Ext JS Library 1.1.1
34593  * Copyright(c) 2006-2007, Ext JS, LLC.
34594  *
34595  * Originally Released Under LGPL - original licence link has changed is not relivant.
34596  *
34597  * Fork - LGPL
34598  * <script type="text/javascript">
34599  */
34600  
34601
34602 /**
34603  * @class Roo.grid.ColumnModel
34604  * @extends Roo.util.Observable
34605  * This is the default implementation of a ColumnModel used by the Grid. It defines
34606  * the columns in the grid.
34607  * <br>Usage:<br>
34608  <pre><code>
34609  var colModel = new Roo.grid.ColumnModel([
34610         {header: "Ticker", width: 60, sortable: true, locked: true},
34611         {header: "Company Name", width: 150, sortable: true},
34612         {header: "Market Cap.", width: 100, sortable: true},
34613         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34614         {header: "Employees", width: 100, sortable: true, resizable: false}
34615  ]);
34616  </code></pre>
34617  * <p>
34618  
34619  * The config options listed for this class are options which may appear in each
34620  * individual column definition.
34621  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34622  * @constructor
34623  * @param {Object} config An Array of column config objects. See this class's
34624  * config objects for details.
34625 */
34626 Roo.grid.ColumnModel = function(config){
34627         /**
34628      * The config passed into the constructor
34629      */
34630     this.config = config;
34631     this.lookup = {};
34632
34633     // if no id, create one
34634     // if the column does not have a dataIndex mapping,
34635     // map it to the order it is in the config
34636     for(var i = 0, len = config.length; i < len; i++){
34637         var c = config[i];
34638         if(typeof c.dataIndex == "undefined"){
34639             c.dataIndex = i;
34640         }
34641         if(typeof c.renderer == "string"){
34642             c.renderer = Roo.util.Format[c.renderer];
34643         }
34644         if(typeof c.id == "undefined"){
34645             c.id = Roo.id();
34646         }
34647         if(c.editor && c.editor.xtype){
34648             c.editor  = Roo.factory(c.editor, Roo.grid);
34649         }
34650         if(c.editor && c.editor.isFormField){
34651             c.editor = new Roo.grid.GridEditor(c.editor);
34652         }
34653         this.lookup[c.id] = c;
34654     }
34655
34656     /**
34657      * The width of columns which have no width specified (defaults to 100)
34658      * @type Number
34659      */
34660     this.defaultWidth = 100;
34661
34662     /**
34663      * Default sortable of columns which have no sortable specified (defaults to false)
34664      * @type Boolean
34665      */
34666     this.defaultSortable = false;
34667
34668     this.addEvents({
34669         /**
34670              * @event widthchange
34671              * Fires when the width of a column changes.
34672              * @param {ColumnModel} this
34673              * @param {Number} columnIndex The column index
34674              * @param {Number} newWidth The new width
34675              */
34676             "widthchange": true,
34677         /**
34678              * @event headerchange
34679              * Fires when the text of a header changes.
34680              * @param {ColumnModel} this
34681              * @param {Number} columnIndex The column index
34682              * @param {Number} newText The new header text
34683              */
34684             "headerchange": true,
34685         /**
34686              * @event hiddenchange
34687              * Fires when a column is hidden or "unhidden".
34688              * @param {ColumnModel} this
34689              * @param {Number} columnIndex The column index
34690              * @param {Boolean} hidden true if hidden, false otherwise
34691              */
34692             "hiddenchange": true,
34693             /**
34694          * @event columnmoved
34695          * Fires when a column is moved.
34696          * @param {ColumnModel} this
34697          * @param {Number} oldIndex
34698          * @param {Number} newIndex
34699          */
34700         "columnmoved" : true,
34701         /**
34702          * @event columlockchange
34703          * Fires when a column's locked state is changed
34704          * @param {ColumnModel} this
34705          * @param {Number} colIndex
34706          * @param {Boolean} locked true if locked
34707          */
34708         "columnlockchange" : true
34709     });
34710     Roo.grid.ColumnModel.superclass.constructor.call(this);
34711 };
34712 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34713     /**
34714      * @cfg {String} header The header text to display in the Grid view.
34715      */
34716     /**
34717      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34718      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34719      * specified, the column's index is used as an index into the Record's data Array.
34720      */
34721     /**
34722      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34723      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34724      */
34725     /**
34726      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34727      * Defaults to the value of the {@link #defaultSortable} property.
34728      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34729      */
34730     /**
34731      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34732      */
34733     /**
34734      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34735      */
34736     /**
34737      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34738      */
34739     /**
34740      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34741      */
34742     /**
34743      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34744      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34745      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34746      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34747      */
34748        /**
34749      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34750      */
34751     /**
34752      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34753      */
34754     /**
34755      * @cfg {String} cursor (Optional)
34756      */
34757     /**
34758      * @cfg {String} tooltip (Optional)
34759      */
34760     /**
34761      * @cfg {Number} xs (Optional)
34762      */
34763     /**
34764      * @cfg {Number} sm (Optional)
34765      */
34766     /**
34767      * @cfg {Number} md (Optional)
34768      */
34769     /**
34770      * @cfg {Number} lg (Optional)
34771      */
34772     /**
34773      * Returns the id of the column at the specified index.
34774      * @param {Number} index The column index
34775      * @return {String} the id
34776      */
34777     getColumnId : function(index){
34778         return this.config[index].id;
34779     },
34780
34781     /**
34782      * Returns the column for a specified id.
34783      * @param {String} id The column id
34784      * @return {Object} the column
34785      */
34786     getColumnById : function(id){
34787         return this.lookup[id];
34788     },
34789
34790     
34791     /**
34792      * Returns the column for a specified dataIndex.
34793      * @param {String} dataIndex The column dataIndex
34794      * @return {Object|Boolean} the column or false if not found
34795      */
34796     getColumnByDataIndex: function(dataIndex){
34797         var index = this.findColumnIndex(dataIndex);
34798         return index > -1 ? this.config[index] : false;
34799     },
34800     
34801     /**
34802      * Returns the index for a specified column id.
34803      * @param {String} id The column id
34804      * @return {Number} the index, or -1 if not found
34805      */
34806     getIndexById : function(id){
34807         for(var i = 0, len = this.config.length; i < len; i++){
34808             if(this.config[i].id == id){
34809                 return i;
34810             }
34811         }
34812         return -1;
34813     },
34814     
34815     /**
34816      * Returns the index for a specified column dataIndex.
34817      * @param {String} dataIndex The column dataIndex
34818      * @return {Number} the index, or -1 if not found
34819      */
34820     
34821     findColumnIndex : function(dataIndex){
34822         for(var i = 0, len = this.config.length; i < len; i++){
34823             if(this.config[i].dataIndex == dataIndex){
34824                 return i;
34825             }
34826         }
34827         return -1;
34828     },
34829     
34830     
34831     moveColumn : function(oldIndex, newIndex){
34832         var c = this.config[oldIndex];
34833         this.config.splice(oldIndex, 1);
34834         this.config.splice(newIndex, 0, c);
34835         this.dataMap = null;
34836         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34837     },
34838
34839     isLocked : function(colIndex){
34840         return this.config[colIndex].locked === true;
34841     },
34842
34843     setLocked : function(colIndex, value, suppressEvent){
34844         if(this.isLocked(colIndex) == value){
34845             return;
34846         }
34847         this.config[colIndex].locked = value;
34848         if(!suppressEvent){
34849             this.fireEvent("columnlockchange", this, colIndex, value);
34850         }
34851     },
34852
34853     getTotalLockedWidth : function(){
34854         var totalWidth = 0;
34855         for(var i = 0; i < this.config.length; i++){
34856             if(this.isLocked(i) && !this.isHidden(i)){
34857                 this.totalWidth += this.getColumnWidth(i);
34858             }
34859         }
34860         return totalWidth;
34861     },
34862
34863     getLockedCount : function(){
34864         for(var i = 0, len = this.config.length; i < len; i++){
34865             if(!this.isLocked(i)){
34866                 return i;
34867             }
34868         }
34869         
34870         return this.config.length;
34871     },
34872
34873     /**
34874      * Returns the number of columns.
34875      * @return {Number}
34876      */
34877     getColumnCount : function(visibleOnly){
34878         if(visibleOnly === true){
34879             var c = 0;
34880             for(var i = 0, len = this.config.length; i < len; i++){
34881                 if(!this.isHidden(i)){
34882                     c++;
34883                 }
34884             }
34885             return c;
34886         }
34887         return this.config.length;
34888     },
34889
34890     /**
34891      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34892      * @param {Function} fn
34893      * @param {Object} scope (optional)
34894      * @return {Array} result
34895      */
34896     getColumnsBy : function(fn, scope){
34897         var r = [];
34898         for(var i = 0, len = this.config.length; i < len; i++){
34899             var c = this.config[i];
34900             if(fn.call(scope||this, c, i) === true){
34901                 r[r.length] = c;
34902             }
34903         }
34904         return r;
34905     },
34906
34907     /**
34908      * Returns true if the specified column is sortable.
34909      * @param {Number} col The column index
34910      * @return {Boolean}
34911      */
34912     isSortable : function(col){
34913         if(typeof this.config[col].sortable == "undefined"){
34914             return this.defaultSortable;
34915         }
34916         return this.config[col].sortable;
34917     },
34918
34919     /**
34920      * Returns the rendering (formatting) function defined for the column.
34921      * @param {Number} col The column index.
34922      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34923      */
34924     getRenderer : function(col){
34925         if(!this.config[col].renderer){
34926             return Roo.grid.ColumnModel.defaultRenderer;
34927         }
34928         return this.config[col].renderer;
34929     },
34930
34931     /**
34932      * Sets the rendering (formatting) function for a column.
34933      * @param {Number} col The column index
34934      * @param {Function} fn The function to use to process the cell's raw data
34935      * to return HTML markup for the grid view. The render function is called with
34936      * the following parameters:<ul>
34937      * <li>Data value.</li>
34938      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34939      * <li>css A CSS style string to apply to the table cell.</li>
34940      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34941      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34942      * <li>Row index</li>
34943      * <li>Column index</li>
34944      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34945      */
34946     setRenderer : function(col, fn){
34947         this.config[col].renderer = fn;
34948     },
34949
34950     /**
34951      * Returns the width for the specified column.
34952      * @param {Number} col The column index
34953      * @return {Number}
34954      */
34955     getColumnWidth : function(col){
34956         return this.config[col].width * 1 || this.defaultWidth;
34957     },
34958
34959     /**
34960      * Sets the width for a column.
34961      * @param {Number} col The column index
34962      * @param {Number} width The new width
34963      */
34964     setColumnWidth : function(col, width, suppressEvent){
34965         this.config[col].width = width;
34966         this.totalWidth = null;
34967         if(!suppressEvent){
34968              this.fireEvent("widthchange", this, col, width);
34969         }
34970     },
34971
34972     /**
34973      * Returns the total width of all columns.
34974      * @param {Boolean} includeHidden True to include hidden column widths
34975      * @return {Number}
34976      */
34977     getTotalWidth : function(includeHidden){
34978         if(!this.totalWidth){
34979             this.totalWidth = 0;
34980             for(var i = 0, len = this.config.length; i < len; i++){
34981                 if(includeHidden || !this.isHidden(i)){
34982                     this.totalWidth += this.getColumnWidth(i);
34983                 }
34984             }
34985         }
34986         return this.totalWidth;
34987     },
34988
34989     /**
34990      * Returns the header for the specified column.
34991      * @param {Number} col The column index
34992      * @return {String}
34993      */
34994     getColumnHeader : function(col){
34995         return this.config[col].header;
34996     },
34997
34998     /**
34999      * Sets the header for a column.
35000      * @param {Number} col The column index
35001      * @param {String} header The new header
35002      */
35003     setColumnHeader : function(col, header){
35004         this.config[col].header = header;
35005         this.fireEvent("headerchange", this, col, header);
35006     },
35007
35008     /**
35009      * Returns the tooltip for the specified column.
35010      * @param {Number} col The column index
35011      * @return {String}
35012      */
35013     getColumnTooltip : function(col){
35014             return this.config[col].tooltip;
35015     },
35016     /**
35017      * Sets the tooltip for a column.
35018      * @param {Number} col The column index
35019      * @param {String} tooltip The new tooltip
35020      */
35021     setColumnTooltip : function(col, tooltip){
35022             this.config[col].tooltip = tooltip;
35023     },
35024
35025     /**
35026      * Returns the dataIndex for the specified column.
35027      * @param {Number} col The column index
35028      * @return {Number}
35029      */
35030     getDataIndex : function(col){
35031         return this.config[col].dataIndex;
35032     },
35033
35034     /**
35035      * Sets the dataIndex for a column.
35036      * @param {Number} col The column index
35037      * @param {Number} dataIndex The new dataIndex
35038      */
35039     setDataIndex : function(col, dataIndex){
35040         this.config[col].dataIndex = dataIndex;
35041     },
35042
35043     
35044     
35045     /**
35046      * Returns true if the cell is editable.
35047      * @param {Number} colIndex The column index
35048      * @param {Number} rowIndex The row index - this is nto actually used..?
35049      * @return {Boolean}
35050      */
35051     isCellEditable : function(colIndex, rowIndex){
35052         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35053     },
35054
35055     /**
35056      * Returns the editor defined for the cell/column.
35057      * return false or null to disable editing.
35058      * @param {Number} colIndex The column index
35059      * @param {Number} rowIndex The row index
35060      * @return {Object}
35061      */
35062     getCellEditor : function(colIndex, rowIndex){
35063         return this.config[colIndex].editor;
35064     },
35065
35066     /**
35067      * Sets if a column is editable.
35068      * @param {Number} col The column index
35069      * @param {Boolean} editable True if the column is editable
35070      */
35071     setEditable : function(col, editable){
35072         this.config[col].editable = editable;
35073     },
35074
35075
35076     /**
35077      * Returns true if the column is hidden.
35078      * @param {Number} colIndex The column index
35079      * @return {Boolean}
35080      */
35081     isHidden : function(colIndex){
35082         return this.config[colIndex].hidden;
35083     },
35084
35085
35086     /**
35087      * Returns true if the column width cannot be changed
35088      */
35089     isFixed : function(colIndex){
35090         return this.config[colIndex].fixed;
35091     },
35092
35093     /**
35094      * Returns true if the column can be resized
35095      * @return {Boolean}
35096      */
35097     isResizable : function(colIndex){
35098         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35099     },
35100     /**
35101      * Sets if a column is hidden.
35102      * @param {Number} colIndex The column index
35103      * @param {Boolean} hidden True if the column is hidden
35104      */
35105     setHidden : function(colIndex, hidden){
35106         this.config[colIndex].hidden = hidden;
35107         this.totalWidth = null;
35108         this.fireEvent("hiddenchange", this, colIndex, hidden);
35109     },
35110
35111     /**
35112      * Sets the editor for a column.
35113      * @param {Number} col The column index
35114      * @param {Object} editor The editor object
35115      */
35116     setEditor : function(col, editor){
35117         this.config[col].editor = editor;
35118     }
35119 });
35120
35121 Roo.grid.ColumnModel.defaultRenderer = function(value)
35122 {
35123     if(typeof value == "object") {
35124         return value;
35125     }
35126         if(typeof value == "string" && value.length < 1){
35127             return "&#160;";
35128         }
35129     
35130         return String.format("{0}", value);
35131 };
35132
35133 // Alias for backwards compatibility
35134 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35135 /*
35136  * Based on:
35137  * Ext JS Library 1.1.1
35138  * Copyright(c) 2006-2007, Ext JS, LLC.
35139  *
35140  * Originally Released Under LGPL - original licence link has changed is not relivant.
35141  *
35142  * Fork - LGPL
35143  * <script type="text/javascript">
35144  */
35145
35146 /**
35147  * @class Roo.grid.AbstractSelectionModel
35148  * @extends Roo.util.Observable
35149  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35150  * implemented by descendant classes.  This class should not be directly instantiated.
35151  * @constructor
35152  */
35153 Roo.grid.AbstractSelectionModel = function(){
35154     this.locked = false;
35155     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35156 };
35157
35158 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35159     /** @ignore Called by the grid automatically. Do not call directly. */
35160     init : function(grid){
35161         this.grid = grid;
35162         this.initEvents();
35163     },
35164
35165     /**
35166      * Locks the selections.
35167      */
35168     lock : function(){
35169         this.locked = true;
35170     },
35171
35172     /**
35173      * Unlocks the selections.
35174      */
35175     unlock : function(){
35176         this.locked = false;
35177     },
35178
35179     /**
35180      * Returns true if the selections are locked.
35181      * @return {Boolean}
35182      */
35183     isLocked : function(){
35184         return this.locked;
35185     }
35186 });/*
35187  * Based on:
35188  * Ext JS Library 1.1.1
35189  * Copyright(c) 2006-2007, Ext JS, LLC.
35190  *
35191  * Originally Released Under LGPL - original licence link has changed is not relivant.
35192  *
35193  * Fork - LGPL
35194  * <script type="text/javascript">
35195  */
35196 /**
35197  * @extends Roo.grid.AbstractSelectionModel
35198  * @class Roo.grid.RowSelectionModel
35199  * The default SelectionModel used by {@link Roo.grid.Grid}.
35200  * It supports multiple selections and keyboard selection/navigation. 
35201  * @constructor
35202  * @param {Object} config
35203  */
35204 Roo.grid.RowSelectionModel = function(config){
35205     Roo.apply(this, config);
35206     this.selections = new Roo.util.MixedCollection(false, function(o){
35207         return o.id;
35208     });
35209
35210     this.last = false;
35211     this.lastActive = false;
35212
35213     this.addEvents({
35214         /**
35215              * @event selectionchange
35216              * Fires when the selection changes
35217              * @param {SelectionModel} this
35218              */
35219             "selectionchange" : true,
35220         /**
35221              * @event afterselectionchange
35222              * Fires after the selection changes (eg. by key press or clicking)
35223              * @param {SelectionModel} this
35224              */
35225             "afterselectionchange" : true,
35226         /**
35227              * @event beforerowselect
35228              * Fires when a row is selected being selected, return false to cancel.
35229              * @param {SelectionModel} this
35230              * @param {Number} rowIndex The selected index
35231              * @param {Boolean} keepExisting False if other selections will be cleared
35232              */
35233             "beforerowselect" : true,
35234         /**
35235              * @event rowselect
35236              * Fires when a row is selected.
35237              * @param {SelectionModel} this
35238              * @param {Number} rowIndex The selected index
35239              * @param {Roo.data.Record} r The record
35240              */
35241             "rowselect" : true,
35242         /**
35243              * @event rowdeselect
35244              * Fires when a row is deselected.
35245              * @param {SelectionModel} this
35246              * @param {Number} rowIndex The selected index
35247              */
35248         "rowdeselect" : true
35249     });
35250     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35251     this.locked = false;
35252 };
35253
35254 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35255     /**
35256      * @cfg {Boolean} singleSelect
35257      * True to allow selection of only one row at a time (defaults to false)
35258      */
35259     singleSelect : false,
35260
35261     // private
35262     initEvents : function(){
35263
35264         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35265             this.grid.on("mousedown", this.handleMouseDown, this);
35266         }else{ // allow click to work like normal
35267             this.grid.on("rowclick", this.handleDragableRowClick, this);
35268         }
35269
35270         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35271             "up" : function(e){
35272                 if(!e.shiftKey){
35273                     this.selectPrevious(e.shiftKey);
35274                 }else if(this.last !== false && this.lastActive !== false){
35275                     var last = this.last;
35276                     this.selectRange(this.last,  this.lastActive-1);
35277                     this.grid.getView().focusRow(this.lastActive);
35278                     if(last !== false){
35279                         this.last = last;
35280                     }
35281                 }else{
35282                     this.selectFirstRow();
35283                 }
35284                 this.fireEvent("afterselectionchange", this);
35285             },
35286             "down" : function(e){
35287                 if(!e.shiftKey){
35288                     this.selectNext(e.shiftKey);
35289                 }else if(this.last !== false && this.lastActive !== false){
35290                     var last = this.last;
35291                     this.selectRange(this.last,  this.lastActive+1);
35292                     this.grid.getView().focusRow(this.lastActive);
35293                     if(last !== false){
35294                         this.last = last;
35295                     }
35296                 }else{
35297                     this.selectFirstRow();
35298                 }
35299                 this.fireEvent("afterselectionchange", this);
35300             },
35301             scope: this
35302         });
35303
35304         var view = this.grid.view;
35305         view.on("refresh", this.onRefresh, this);
35306         view.on("rowupdated", this.onRowUpdated, this);
35307         view.on("rowremoved", this.onRemove, this);
35308     },
35309
35310     // private
35311     onRefresh : function(){
35312         var ds = this.grid.dataSource, i, v = this.grid.view;
35313         var s = this.selections;
35314         s.each(function(r){
35315             if((i = ds.indexOfId(r.id)) != -1){
35316                 v.onRowSelect(i);
35317                 s.add(ds.getAt(i)); // updating the selection relate data
35318             }else{
35319                 s.remove(r);
35320             }
35321         });
35322     },
35323
35324     // private
35325     onRemove : function(v, index, r){
35326         this.selections.remove(r);
35327     },
35328
35329     // private
35330     onRowUpdated : function(v, index, r){
35331         if(this.isSelected(r)){
35332             v.onRowSelect(index);
35333         }
35334     },
35335
35336     /**
35337      * Select records.
35338      * @param {Array} records The records to select
35339      * @param {Boolean} keepExisting (optional) True to keep existing selections
35340      */
35341     selectRecords : function(records, keepExisting){
35342         if(!keepExisting){
35343             this.clearSelections();
35344         }
35345         var ds = this.grid.dataSource;
35346         for(var i = 0, len = records.length; i < len; i++){
35347             this.selectRow(ds.indexOf(records[i]), true);
35348         }
35349     },
35350
35351     /**
35352      * Gets the number of selected rows.
35353      * @return {Number}
35354      */
35355     getCount : function(){
35356         return this.selections.length;
35357     },
35358
35359     /**
35360      * Selects the first row in the grid.
35361      */
35362     selectFirstRow : function(){
35363         this.selectRow(0);
35364     },
35365
35366     /**
35367      * Select the last row.
35368      * @param {Boolean} keepExisting (optional) True to keep existing selections
35369      */
35370     selectLastRow : function(keepExisting){
35371         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35372     },
35373
35374     /**
35375      * Selects the row immediately following the last selected row.
35376      * @param {Boolean} keepExisting (optional) True to keep existing selections
35377      */
35378     selectNext : function(keepExisting){
35379         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35380             this.selectRow(this.last+1, keepExisting);
35381             this.grid.getView().focusRow(this.last);
35382         }
35383     },
35384
35385     /**
35386      * Selects the row that precedes the last selected row.
35387      * @param {Boolean} keepExisting (optional) True to keep existing selections
35388      */
35389     selectPrevious : function(keepExisting){
35390         if(this.last){
35391             this.selectRow(this.last-1, keepExisting);
35392             this.grid.getView().focusRow(this.last);
35393         }
35394     },
35395
35396     /**
35397      * Returns the selected records
35398      * @return {Array} Array of selected records
35399      */
35400     getSelections : function(){
35401         return [].concat(this.selections.items);
35402     },
35403
35404     /**
35405      * Returns the first selected record.
35406      * @return {Record}
35407      */
35408     getSelected : function(){
35409         return this.selections.itemAt(0);
35410     },
35411
35412
35413     /**
35414      * Clears all selections.
35415      */
35416     clearSelections : function(fast){
35417         if(this.locked) {
35418             return;
35419         }
35420         if(fast !== true){
35421             var ds = this.grid.dataSource;
35422             var s = this.selections;
35423             s.each(function(r){
35424                 this.deselectRow(ds.indexOfId(r.id));
35425             }, this);
35426             s.clear();
35427         }else{
35428             this.selections.clear();
35429         }
35430         this.last = false;
35431     },
35432
35433
35434     /**
35435      * Selects all rows.
35436      */
35437     selectAll : function(){
35438         if(this.locked) {
35439             return;
35440         }
35441         this.selections.clear();
35442         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35443             this.selectRow(i, true);
35444         }
35445     },
35446
35447     /**
35448      * Returns True if there is a selection.
35449      * @return {Boolean}
35450      */
35451     hasSelection : function(){
35452         return this.selections.length > 0;
35453     },
35454
35455     /**
35456      * Returns True if the specified row is selected.
35457      * @param {Number/Record} record The record or index of the record to check
35458      * @return {Boolean}
35459      */
35460     isSelected : function(index){
35461         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35462         return (r && this.selections.key(r.id) ? true : false);
35463     },
35464
35465     /**
35466      * Returns True if the specified record id is selected.
35467      * @param {String} id The id of record to check
35468      * @return {Boolean}
35469      */
35470     isIdSelected : function(id){
35471         return (this.selections.key(id) ? true : false);
35472     },
35473
35474     // private
35475     handleMouseDown : function(e, t){
35476         var view = this.grid.getView(), rowIndex;
35477         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35478             return;
35479         };
35480         if(e.shiftKey && this.last !== false){
35481             var last = this.last;
35482             this.selectRange(last, rowIndex, e.ctrlKey);
35483             this.last = last; // reset the last
35484             view.focusRow(rowIndex);
35485         }else{
35486             var isSelected = this.isSelected(rowIndex);
35487             if(e.button !== 0 && isSelected){
35488                 view.focusRow(rowIndex);
35489             }else if(e.ctrlKey && isSelected){
35490                 this.deselectRow(rowIndex);
35491             }else if(!isSelected){
35492                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35493                 view.focusRow(rowIndex);
35494             }
35495         }
35496         this.fireEvent("afterselectionchange", this);
35497     },
35498     // private
35499     handleDragableRowClick :  function(grid, rowIndex, e) 
35500     {
35501         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35502             this.selectRow(rowIndex, false);
35503             grid.view.focusRow(rowIndex);
35504              this.fireEvent("afterselectionchange", this);
35505         }
35506     },
35507     
35508     /**
35509      * Selects multiple rows.
35510      * @param {Array} rows Array of the indexes of the row to select
35511      * @param {Boolean} keepExisting (optional) True to keep existing selections
35512      */
35513     selectRows : function(rows, keepExisting){
35514         if(!keepExisting){
35515             this.clearSelections();
35516         }
35517         for(var i = 0, len = rows.length; i < len; i++){
35518             this.selectRow(rows[i], true);
35519         }
35520     },
35521
35522     /**
35523      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35524      * @param {Number} startRow The index of the first row in the range
35525      * @param {Number} endRow The index of the last row in the range
35526      * @param {Boolean} keepExisting (optional) True to retain existing selections
35527      */
35528     selectRange : function(startRow, endRow, keepExisting){
35529         if(this.locked) {
35530             return;
35531         }
35532         if(!keepExisting){
35533             this.clearSelections();
35534         }
35535         if(startRow <= endRow){
35536             for(var i = startRow; i <= endRow; i++){
35537                 this.selectRow(i, true);
35538             }
35539         }else{
35540             for(var i = startRow; i >= endRow; i--){
35541                 this.selectRow(i, true);
35542             }
35543         }
35544     },
35545
35546     /**
35547      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35548      * @param {Number} startRow The index of the first row in the range
35549      * @param {Number} endRow The index of the last row in the range
35550      */
35551     deselectRange : function(startRow, endRow, preventViewNotify){
35552         if(this.locked) {
35553             return;
35554         }
35555         for(var i = startRow; i <= endRow; i++){
35556             this.deselectRow(i, preventViewNotify);
35557         }
35558     },
35559
35560     /**
35561      * Selects a row.
35562      * @param {Number} row The index of the row to select
35563      * @param {Boolean} keepExisting (optional) True to keep existing selections
35564      */
35565     selectRow : function(index, keepExisting, preventViewNotify){
35566         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35567             return;
35568         }
35569         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35570             if(!keepExisting || this.singleSelect){
35571                 this.clearSelections();
35572             }
35573             var r = this.grid.dataSource.getAt(index);
35574             this.selections.add(r);
35575             this.last = this.lastActive = index;
35576             if(!preventViewNotify){
35577                 this.grid.getView().onRowSelect(index);
35578             }
35579             this.fireEvent("rowselect", this, index, r);
35580             this.fireEvent("selectionchange", this);
35581         }
35582     },
35583
35584     /**
35585      * Deselects a row.
35586      * @param {Number} row The index of the row to deselect
35587      */
35588     deselectRow : function(index, preventViewNotify){
35589         if(this.locked) {
35590             return;
35591         }
35592         if(this.last == index){
35593             this.last = false;
35594         }
35595         if(this.lastActive == index){
35596             this.lastActive = false;
35597         }
35598         var r = this.grid.dataSource.getAt(index);
35599         this.selections.remove(r);
35600         if(!preventViewNotify){
35601             this.grid.getView().onRowDeselect(index);
35602         }
35603         this.fireEvent("rowdeselect", this, index);
35604         this.fireEvent("selectionchange", this);
35605     },
35606
35607     // private
35608     restoreLast : function(){
35609         if(this._last){
35610             this.last = this._last;
35611         }
35612     },
35613
35614     // private
35615     acceptsNav : function(row, col, cm){
35616         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35617     },
35618
35619     // private
35620     onEditorKey : function(field, e){
35621         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35622         if(k == e.TAB){
35623             e.stopEvent();
35624             ed.completeEdit();
35625             if(e.shiftKey){
35626                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35627             }else{
35628                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35629             }
35630         }else if(k == e.ENTER && !e.ctrlKey){
35631             e.stopEvent();
35632             ed.completeEdit();
35633             if(e.shiftKey){
35634                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35635             }else{
35636                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35637             }
35638         }else if(k == e.ESC){
35639             ed.cancelEdit();
35640         }
35641         if(newCell){
35642             g.startEditing(newCell[0], newCell[1]);
35643         }
35644     }
35645 });/*
35646  * Based on:
35647  * Ext JS Library 1.1.1
35648  * Copyright(c) 2006-2007, Ext JS, LLC.
35649  *
35650  * Originally Released Under LGPL - original licence link has changed is not relivant.
35651  *
35652  * Fork - LGPL
35653  * <script type="text/javascript">
35654  */
35655 /**
35656  * @class Roo.grid.CellSelectionModel
35657  * @extends Roo.grid.AbstractSelectionModel
35658  * This class provides the basic implementation for cell selection in a grid.
35659  * @constructor
35660  * @param {Object} config The object containing the configuration of this model.
35661  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35662  */
35663 Roo.grid.CellSelectionModel = function(config){
35664     Roo.apply(this, config);
35665
35666     this.selection = null;
35667
35668     this.addEvents({
35669         /**
35670              * @event beforerowselect
35671              * Fires before a cell is selected.
35672              * @param {SelectionModel} this
35673              * @param {Number} rowIndex The selected row index
35674              * @param {Number} colIndex The selected cell index
35675              */
35676             "beforecellselect" : true,
35677         /**
35678              * @event cellselect
35679              * Fires when a cell is selected.
35680              * @param {SelectionModel} this
35681              * @param {Number} rowIndex The selected row index
35682              * @param {Number} colIndex The selected cell index
35683              */
35684             "cellselect" : true,
35685         /**
35686              * @event selectionchange
35687              * Fires when the active selection changes.
35688              * @param {SelectionModel} this
35689              * @param {Object} selection null for no selection or an object (o) with two properties
35690                 <ul>
35691                 <li>o.record: the record object for the row the selection is in</li>
35692                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35693                 </ul>
35694              */
35695             "selectionchange" : true,
35696         /**
35697              * @event tabend
35698              * Fires when the tab (or enter) was pressed on the last editable cell
35699              * You can use this to trigger add new row.
35700              * @param {SelectionModel} this
35701              */
35702             "tabend" : true,
35703          /**
35704              * @event beforeeditnext
35705              * Fires before the next editable sell is made active
35706              * You can use this to skip to another cell or fire the tabend
35707              *    if you set cell to false
35708              * @param {Object} eventdata object : { cell : [ row, col ] } 
35709              */
35710             "beforeeditnext" : true
35711     });
35712     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35713 };
35714
35715 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35716     
35717     enter_is_tab: false,
35718
35719     /** @ignore */
35720     initEvents : function(){
35721         this.grid.on("mousedown", this.handleMouseDown, this);
35722         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35723         var view = this.grid.view;
35724         view.on("refresh", this.onViewChange, this);
35725         view.on("rowupdated", this.onRowUpdated, this);
35726         view.on("beforerowremoved", this.clearSelections, this);
35727         view.on("beforerowsinserted", this.clearSelections, this);
35728         if(this.grid.isEditor){
35729             this.grid.on("beforeedit", this.beforeEdit,  this);
35730         }
35731     },
35732
35733         //private
35734     beforeEdit : function(e){
35735         this.select(e.row, e.column, false, true, e.record);
35736     },
35737
35738         //private
35739     onRowUpdated : function(v, index, r){
35740         if(this.selection && this.selection.record == r){
35741             v.onCellSelect(index, this.selection.cell[1]);
35742         }
35743     },
35744
35745         //private
35746     onViewChange : function(){
35747         this.clearSelections(true);
35748     },
35749
35750         /**
35751          * Returns the currently selected cell,.
35752          * @return {Array} The selected cell (row, column) or null if none selected.
35753          */
35754     getSelectedCell : function(){
35755         return this.selection ? this.selection.cell : null;
35756     },
35757
35758     /**
35759      * Clears all selections.
35760      * @param {Boolean} true to prevent the gridview from being notified about the change.
35761      */
35762     clearSelections : function(preventNotify){
35763         var s = this.selection;
35764         if(s){
35765             if(preventNotify !== true){
35766                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35767             }
35768             this.selection = null;
35769             this.fireEvent("selectionchange", this, null);
35770         }
35771     },
35772
35773     /**
35774      * Returns true if there is a selection.
35775      * @return {Boolean}
35776      */
35777     hasSelection : function(){
35778         return this.selection ? true : false;
35779     },
35780
35781     /** @ignore */
35782     handleMouseDown : function(e, t){
35783         var v = this.grid.getView();
35784         if(this.isLocked()){
35785             return;
35786         };
35787         var row = v.findRowIndex(t);
35788         var cell = v.findCellIndex(t);
35789         if(row !== false && cell !== false){
35790             this.select(row, cell);
35791         }
35792     },
35793
35794     /**
35795      * Selects a cell.
35796      * @param {Number} rowIndex
35797      * @param {Number} collIndex
35798      */
35799     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35800         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35801             this.clearSelections();
35802             r = r || this.grid.dataSource.getAt(rowIndex);
35803             this.selection = {
35804                 record : r,
35805                 cell : [rowIndex, colIndex]
35806             };
35807             if(!preventViewNotify){
35808                 var v = this.grid.getView();
35809                 v.onCellSelect(rowIndex, colIndex);
35810                 if(preventFocus !== true){
35811                     v.focusCell(rowIndex, colIndex);
35812                 }
35813             }
35814             this.fireEvent("cellselect", this, rowIndex, colIndex);
35815             this.fireEvent("selectionchange", this, this.selection);
35816         }
35817     },
35818
35819         //private
35820     isSelectable : function(rowIndex, colIndex, cm){
35821         return !cm.isHidden(colIndex);
35822     },
35823
35824     /** @ignore */
35825     handleKeyDown : function(e){
35826         //Roo.log('Cell Sel Model handleKeyDown');
35827         if(!e.isNavKeyPress()){
35828             return;
35829         }
35830         var g = this.grid, s = this.selection;
35831         if(!s){
35832             e.stopEvent();
35833             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35834             if(cell){
35835                 this.select(cell[0], cell[1]);
35836             }
35837             return;
35838         }
35839         var sm = this;
35840         var walk = function(row, col, step){
35841             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35842         };
35843         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35844         var newCell;
35845
35846       
35847
35848         switch(k){
35849             case e.TAB:
35850                 // handled by onEditorKey
35851                 if (g.isEditor && g.editing) {
35852                     return;
35853                 }
35854                 if(e.shiftKey) {
35855                     newCell = walk(r, c-1, -1);
35856                 } else {
35857                     newCell = walk(r, c+1, 1);
35858                 }
35859                 break;
35860             
35861             case e.DOWN:
35862                newCell = walk(r+1, c, 1);
35863                 break;
35864             
35865             case e.UP:
35866                 newCell = walk(r-1, c, -1);
35867                 break;
35868             
35869             case e.RIGHT:
35870                 newCell = walk(r, c+1, 1);
35871                 break;
35872             
35873             case e.LEFT:
35874                 newCell = walk(r, c-1, -1);
35875                 break;
35876             
35877             case e.ENTER:
35878                 
35879                 if(g.isEditor && !g.editing){
35880                    g.startEditing(r, c);
35881                    e.stopEvent();
35882                    return;
35883                 }
35884                 
35885                 
35886              break;
35887         };
35888         if(newCell){
35889             this.select(newCell[0], newCell[1]);
35890             e.stopEvent();
35891             
35892         }
35893     },
35894
35895     acceptsNav : function(row, col, cm){
35896         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35897     },
35898     /**
35899      * Selects a cell.
35900      * @param {Number} field (not used) - as it's normally used as a listener
35901      * @param {Number} e - event - fake it by using
35902      *
35903      * var e = Roo.EventObjectImpl.prototype;
35904      * e.keyCode = e.TAB
35905      *
35906      * 
35907      */
35908     onEditorKey : function(field, e){
35909         
35910         var k = e.getKey(),
35911             newCell,
35912             g = this.grid,
35913             ed = g.activeEditor,
35914             forward = false;
35915         ///Roo.log('onEditorKey' + k);
35916         
35917         
35918         if (this.enter_is_tab && k == e.ENTER) {
35919             k = e.TAB;
35920         }
35921         
35922         if(k == e.TAB){
35923             if(e.shiftKey){
35924                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35925             }else{
35926                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35927                 forward = true;
35928             }
35929             
35930             e.stopEvent();
35931             
35932         } else if(k == e.ENTER &&  !e.ctrlKey){
35933             ed.completeEdit();
35934             e.stopEvent();
35935             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35936         
35937                 } else if(k == e.ESC){
35938             ed.cancelEdit();
35939         }
35940                 
35941         if (newCell) {
35942             var ecall = { cell : newCell, forward : forward };
35943             this.fireEvent('beforeeditnext', ecall );
35944             newCell = ecall.cell;
35945                         forward = ecall.forward;
35946         }
35947                 
35948         if(newCell){
35949             //Roo.log('next cell after edit');
35950             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35951         } else if (forward) {
35952             // tabbed past last
35953             this.fireEvent.defer(100, this, ['tabend',this]);
35954         }
35955     }
35956 });/*
35957  * Based on:
35958  * Ext JS Library 1.1.1
35959  * Copyright(c) 2006-2007, Ext JS, LLC.
35960  *
35961  * Originally Released Under LGPL - original licence link has changed is not relivant.
35962  *
35963  * Fork - LGPL
35964  * <script type="text/javascript">
35965  */
35966  
35967 /**
35968  * @class Roo.grid.EditorGrid
35969  * @extends Roo.grid.Grid
35970  * Class for creating and editable grid.
35971  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35972  * The container MUST have some type of size defined for the grid to fill. The container will be 
35973  * automatically set to position relative if it isn't already.
35974  * @param {Object} dataSource The data model to bind to
35975  * @param {Object} colModel The column model with info about this grid's columns
35976  */
35977 Roo.grid.EditorGrid = function(container, config){
35978     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35979     this.getGridEl().addClass("xedit-grid");
35980
35981     if(!this.selModel){
35982         this.selModel = new Roo.grid.CellSelectionModel();
35983     }
35984
35985     this.activeEditor = null;
35986
35987         this.addEvents({
35988             /**
35989              * @event beforeedit
35990              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35991              * <ul style="padding:5px;padding-left:16px;">
35992              * <li>grid - This grid</li>
35993              * <li>record - The record being edited</li>
35994              * <li>field - The field name being edited</li>
35995              * <li>value - The value for the field being edited.</li>
35996              * <li>row - The grid row index</li>
35997              * <li>column - The grid column index</li>
35998              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35999              * </ul>
36000              * @param {Object} e An edit event (see above for description)
36001              */
36002             "beforeedit" : true,
36003             /**
36004              * @event afteredit
36005              * Fires after a cell is edited. <br />
36006              * <ul style="padding:5px;padding-left:16px;">
36007              * <li>grid - This grid</li>
36008              * <li>record - The record being edited</li>
36009              * <li>field - The field name being edited</li>
36010              * <li>value - The value being set</li>
36011              * <li>originalValue - The original value for the field, before the edit.</li>
36012              * <li>row - The grid row index</li>
36013              * <li>column - The grid column index</li>
36014              * </ul>
36015              * @param {Object} e An edit event (see above for description)
36016              */
36017             "afteredit" : true,
36018             /**
36019              * @event validateedit
36020              * Fires after a cell is edited, but before the value is set in the record. 
36021          * You can use this to modify the value being set in the field, Return false
36022              * to cancel the change. The edit event object has the following properties <br />
36023              * <ul style="padding:5px;padding-left:16px;">
36024          * <li>editor - This editor</li>
36025              * <li>grid - This grid</li>
36026              * <li>record - The record being edited</li>
36027              * <li>field - The field name being edited</li>
36028              * <li>value - The value being set</li>
36029              * <li>originalValue - The original value for the field, before the edit.</li>
36030              * <li>row - The grid row index</li>
36031              * <li>column - The grid column index</li>
36032              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36033              * </ul>
36034              * @param {Object} e An edit event (see above for description)
36035              */
36036             "validateedit" : true
36037         });
36038     this.on("bodyscroll", this.stopEditing,  this);
36039     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36040 };
36041
36042 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36043     /**
36044      * @cfg {Number} clicksToEdit
36045      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36046      */
36047     clicksToEdit: 2,
36048
36049     // private
36050     isEditor : true,
36051     // private
36052     trackMouseOver: false, // causes very odd FF errors
36053
36054     onCellDblClick : function(g, row, col){
36055         this.startEditing(row, col);
36056     },
36057
36058     onEditComplete : function(ed, value, startValue){
36059         this.editing = false;
36060         this.activeEditor = null;
36061         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36062         var r = ed.record;
36063         var field = this.colModel.getDataIndex(ed.col);
36064         var e = {
36065             grid: this,
36066             record: r,
36067             field: field,
36068             originalValue: startValue,
36069             value: value,
36070             row: ed.row,
36071             column: ed.col,
36072             cancel:false,
36073             editor: ed
36074         };
36075         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36076         cell.show();
36077           
36078         if(String(value) !== String(startValue)){
36079             
36080             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36081                 r.set(field, e.value);
36082                 // if we are dealing with a combo box..
36083                 // then we also set the 'name' colum to be the displayField
36084                 if (ed.field.displayField && ed.field.name) {
36085                     r.set(ed.field.name, ed.field.el.dom.value);
36086                 }
36087                 
36088                 delete e.cancel; //?? why!!!
36089                 this.fireEvent("afteredit", e);
36090             }
36091         } else {
36092             this.fireEvent("afteredit", e); // always fire it!
36093         }
36094         this.view.focusCell(ed.row, ed.col);
36095     },
36096
36097     /**
36098      * Starts editing the specified for the specified row/column
36099      * @param {Number} rowIndex
36100      * @param {Number} colIndex
36101      */
36102     startEditing : function(row, col){
36103         this.stopEditing();
36104         if(this.colModel.isCellEditable(col, row)){
36105             this.view.ensureVisible(row, col, true);
36106           
36107             var r = this.dataSource.getAt(row);
36108             var field = this.colModel.getDataIndex(col);
36109             var cell = Roo.get(this.view.getCell(row,col));
36110             var e = {
36111                 grid: this,
36112                 record: r,
36113                 field: field,
36114                 value: r.data[field],
36115                 row: row,
36116                 column: col,
36117                 cancel:false 
36118             };
36119             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36120                 this.editing = true;
36121                 var ed = this.colModel.getCellEditor(col, row);
36122                 
36123                 if (!ed) {
36124                     return;
36125                 }
36126                 if(!ed.rendered){
36127                     ed.render(ed.parentEl || document.body);
36128                 }
36129                 ed.field.reset();
36130                
36131                 cell.hide();
36132                 
36133                 (function(){ // complex but required for focus issues in safari, ie and opera
36134                     ed.row = row;
36135                     ed.col = col;
36136                     ed.record = r;
36137                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36138                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36139                     this.activeEditor = ed;
36140                     var v = r.data[field];
36141                     ed.startEdit(this.view.getCell(row, col), v);
36142                     // combo's with 'displayField and name set
36143                     if (ed.field.displayField && ed.field.name) {
36144                         ed.field.el.dom.value = r.data[ed.field.name];
36145                     }
36146                     
36147                     
36148                 }).defer(50, this);
36149             }
36150         }
36151     },
36152         
36153     /**
36154      * Stops any active editing
36155      */
36156     stopEditing : function(){
36157         if(this.activeEditor){
36158             this.activeEditor.completeEdit();
36159         }
36160         this.activeEditor = null;
36161     },
36162         
36163          /**
36164      * Called to get grid's drag proxy text, by default returns this.ddText.
36165      * @return {String}
36166      */
36167     getDragDropText : function(){
36168         var count = this.selModel.getSelectedCell() ? 1 : 0;
36169         return String.format(this.ddText, count, count == 1 ? '' : 's');
36170     }
36171         
36172 });/*
36173  * Based on:
36174  * Ext JS Library 1.1.1
36175  * Copyright(c) 2006-2007, Ext JS, LLC.
36176  *
36177  * Originally Released Under LGPL - original licence link has changed is not relivant.
36178  *
36179  * Fork - LGPL
36180  * <script type="text/javascript">
36181  */
36182
36183 // private - not really -- you end up using it !
36184 // This is a support class used internally by the Grid components
36185
36186 /**
36187  * @class Roo.grid.GridEditor
36188  * @extends Roo.Editor
36189  * Class for creating and editable grid elements.
36190  * @param {Object} config any settings (must include field)
36191  */
36192 Roo.grid.GridEditor = function(field, config){
36193     if (!config && field.field) {
36194         config = field;
36195         field = Roo.factory(config.field, Roo.form);
36196     }
36197     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36198     field.monitorTab = false;
36199 };
36200
36201 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36202     
36203     /**
36204      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36205      */
36206     
36207     alignment: "tl-tl",
36208     autoSize: "width",
36209     hideEl : false,
36210     cls: "x-small-editor x-grid-editor",
36211     shim:false,
36212     shadow:"frame"
36213 });/*
36214  * Based on:
36215  * Ext JS Library 1.1.1
36216  * Copyright(c) 2006-2007, Ext JS, LLC.
36217  *
36218  * Originally Released Under LGPL - original licence link has changed is not relivant.
36219  *
36220  * Fork - LGPL
36221  * <script type="text/javascript">
36222  */
36223   
36224
36225   
36226 Roo.grid.PropertyRecord = Roo.data.Record.create([
36227     {name:'name',type:'string'},  'value'
36228 ]);
36229
36230
36231 Roo.grid.PropertyStore = function(grid, source){
36232     this.grid = grid;
36233     this.store = new Roo.data.Store({
36234         recordType : Roo.grid.PropertyRecord
36235     });
36236     this.store.on('update', this.onUpdate,  this);
36237     if(source){
36238         this.setSource(source);
36239     }
36240     Roo.grid.PropertyStore.superclass.constructor.call(this);
36241 };
36242
36243
36244
36245 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36246     setSource : function(o){
36247         this.source = o;
36248         this.store.removeAll();
36249         var data = [];
36250         for(var k in o){
36251             if(this.isEditableValue(o[k])){
36252                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36253             }
36254         }
36255         this.store.loadRecords({records: data}, {}, true);
36256     },
36257
36258     onUpdate : function(ds, record, type){
36259         if(type == Roo.data.Record.EDIT){
36260             var v = record.data['value'];
36261             var oldValue = record.modified['value'];
36262             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36263                 this.source[record.id] = v;
36264                 record.commit();
36265                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36266             }else{
36267                 record.reject();
36268             }
36269         }
36270     },
36271
36272     getProperty : function(row){
36273        return this.store.getAt(row);
36274     },
36275
36276     isEditableValue: function(val){
36277         if(val && val instanceof Date){
36278             return true;
36279         }else if(typeof val == 'object' || typeof val == 'function'){
36280             return false;
36281         }
36282         return true;
36283     },
36284
36285     setValue : function(prop, value){
36286         this.source[prop] = value;
36287         this.store.getById(prop).set('value', value);
36288     },
36289
36290     getSource : function(){
36291         return this.source;
36292     }
36293 });
36294
36295 Roo.grid.PropertyColumnModel = function(grid, store){
36296     this.grid = grid;
36297     var g = Roo.grid;
36298     g.PropertyColumnModel.superclass.constructor.call(this, [
36299         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36300         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36301     ]);
36302     this.store = store;
36303     this.bselect = Roo.DomHelper.append(document.body, {
36304         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36305             {tag: 'option', value: 'true', html: 'true'},
36306             {tag: 'option', value: 'false', html: 'false'}
36307         ]
36308     });
36309     Roo.id(this.bselect);
36310     var f = Roo.form;
36311     this.editors = {
36312         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36313         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36314         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36315         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36316         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36317     };
36318     this.renderCellDelegate = this.renderCell.createDelegate(this);
36319     this.renderPropDelegate = this.renderProp.createDelegate(this);
36320 };
36321
36322 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36323     
36324     
36325     nameText : 'Name',
36326     valueText : 'Value',
36327     
36328     dateFormat : 'm/j/Y',
36329     
36330     
36331     renderDate : function(dateVal){
36332         return dateVal.dateFormat(this.dateFormat);
36333     },
36334
36335     renderBool : function(bVal){
36336         return bVal ? 'true' : 'false';
36337     },
36338
36339     isCellEditable : function(colIndex, rowIndex){
36340         return colIndex == 1;
36341     },
36342
36343     getRenderer : function(col){
36344         return col == 1 ?
36345             this.renderCellDelegate : this.renderPropDelegate;
36346     },
36347
36348     renderProp : function(v){
36349         return this.getPropertyName(v);
36350     },
36351
36352     renderCell : function(val){
36353         var rv = val;
36354         if(val instanceof Date){
36355             rv = this.renderDate(val);
36356         }else if(typeof val == 'boolean'){
36357             rv = this.renderBool(val);
36358         }
36359         return Roo.util.Format.htmlEncode(rv);
36360     },
36361
36362     getPropertyName : function(name){
36363         var pn = this.grid.propertyNames;
36364         return pn && pn[name] ? pn[name] : name;
36365     },
36366
36367     getCellEditor : function(colIndex, rowIndex){
36368         var p = this.store.getProperty(rowIndex);
36369         var n = p.data['name'], val = p.data['value'];
36370         
36371         if(typeof(this.grid.customEditors[n]) == 'string'){
36372             return this.editors[this.grid.customEditors[n]];
36373         }
36374         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36375             return this.grid.customEditors[n];
36376         }
36377         if(val instanceof Date){
36378             return this.editors['date'];
36379         }else if(typeof val == 'number'){
36380             return this.editors['number'];
36381         }else if(typeof val == 'boolean'){
36382             return this.editors['boolean'];
36383         }else{
36384             return this.editors['string'];
36385         }
36386     }
36387 });
36388
36389 /**
36390  * @class Roo.grid.PropertyGrid
36391  * @extends Roo.grid.EditorGrid
36392  * This class represents the  interface of a component based property grid control.
36393  * <br><br>Usage:<pre><code>
36394  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36395       
36396  });
36397  // set any options
36398  grid.render();
36399  * </code></pre>
36400   
36401  * @constructor
36402  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36403  * The container MUST have some type of size defined for the grid to fill. The container will be
36404  * automatically set to position relative if it isn't already.
36405  * @param {Object} config A config object that sets properties on this grid.
36406  */
36407 Roo.grid.PropertyGrid = function(container, config){
36408     config = config || {};
36409     var store = new Roo.grid.PropertyStore(this);
36410     this.store = store;
36411     var cm = new Roo.grid.PropertyColumnModel(this, store);
36412     store.store.sort('name', 'ASC');
36413     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36414         ds: store.store,
36415         cm: cm,
36416         enableColLock:false,
36417         enableColumnMove:false,
36418         stripeRows:false,
36419         trackMouseOver: false,
36420         clicksToEdit:1
36421     }, config));
36422     this.getGridEl().addClass('x-props-grid');
36423     this.lastEditRow = null;
36424     this.on('columnresize', this.onColumnResize, this);
36425     this.addEvents({
36426          /**
36427              * @event beforepropertychange
36428              * Fires before a property changes (return false to stop?)
36429              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36430              * @param {String} id Record Id
36431              * @param {String} newval New Value
36432          * @param {String} oldval Old Value
36433              */
36434         "beforepropertychange": true,
36435         /**
36436              * @event propertychange
36437              * Fires after a property changes
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         "propertychange": true
36444     });
36445     this.customEditors = this.customEditors || {};
36446 };
36447 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36448     
36449      /**
36450      * @cfg {Object} customEditors map of colnames=> custom editors.
36451      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36452      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36453      * false disables editing of the field.
36454          */
36455     
36456       /**
36457      * @cfg {Object} propertyNames map of property Names to their displayed value
36458          */
36459     
36460     render : function(){
36461         Roo.grid.PropertyGrid.superclass.render.call(this);
36462         this.autoSize.defer(100, this);
36463     },
36464
36465     autoSize : function(){
36466         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36467         if(this.view){
36468             this.view.fitColumns();
36469         }
36470     },
36471
36472     onColumnResize : function(){
36473         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36474         this.autoSize();
36475     },
36476     /**
36477      * Sets the data for the Grid
36478      * accepts a Key => Value object of all the elements avaiable.
36479      * @param {Object} data  to appear in grid.
36480      */
36481     setSource : function(source){
36482         this.store.setSource(source);
36483         //this.autoSize();
36484     },
36485     /**
36486      * Gets all the data from the grid.
36487      * @return {Object} data  data stored in grid
36488      */
36489     getSource : function(){
36490         return this.store.getSource();
36491     }
36492 });/*
36493   
36494  * Licence LGPL
36495  
36496  */
36497  
36498 /**
36499  * @class Roo.grid.Calendar
36500  * @extends Roo.util.Grid
36501  * This class extends the Grid to provide a calendar widget
36502  * <br><br>Usage:<pre><code>
36503  var grid = new Roo.grid.Calendar("my-container-id", {
36504      ds: myDataStore,
36505      cm: myColModel,
36506      selModel: mySelectionModel,
36507      autoSizeColumns: true,
36508      monitorWindowResize: false,
36509      trackMouseOver: true
36510      eventstore : real data store..
36511  });
36512  // set any options
36513  grid.render();
36514   
36515   * @constructor
36516  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36517  * The container MUST have some type of size defined for the grid to fill. The container will be
36518  * automatically set to position relative if it isn't already.
36519  * @param {Object} config A config object that sets properties on this grid.
36520  */
36521 Roo.grid.Calendar = function(container, config){
36522         // initialize the container
36523         this.container = Roo.get(container);
36524         this.container.update("");
36525         this.container.setStyle("overflow", "hidden");
36526     this.container.addClass('x-grid-container');
36527
36528     this.id = this.container.id;
36529
36530     Roo.apply(this, config);
36531     // check and correct shorthanded configs
36532     
36533     var rows = [];
36534     var d =1;
36535     for (var r = 0;r < 6;r++) {
36536         
36537         rows[r]=[];
36538         for (var c =0;c < 7;c++) {
36539             rows[r][c]= '';
36540         }
36541     }
36542     if (this.eventStore) {
36543         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36544         this.eventStore.on('load',this.onLoad, this);
36545         this.eventStore.on('beforeload',this.clearEvents, this);
36546          
36547     }
36548     
36549     this.dataSource = new Roo.data.Store({
36550             proxy: new Roo.data.MemoryProxy(rows),
36551             reader: new Roo.data.ArrayReader({}, [
36552                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36553     });
36554
36555     this.dataSource.load();
36556     this.ds = this.dataSource;
36557     this.ds.xmodule = this.xmodule || false;
36558     
36559     
36560     var cellRender = function(v,x,r)
36561     {
36562         return String.format(
36563             '<div class="fc-day  fc-widget-content"><div>' +
36564                 '<div class="fc-event-container"></div>' +
36565                 '<div class="fc-day-number">{0}</div>'+
36566                 
36567                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36568             '</div></div>', v);
36569     
36570     }
36571     
36572     
36573     this.colModel = new Roo.grid.ColumnModel( [
36574         {
36575             xtype: 'ColumnModel',
36576             xns: Roo.grid,
36577             dataIndex : 'weekday0',
36578             header : 'Sunday',
36579             renderer : cellRender
36580         },
36581         {
36582             xtype: 'ColumnModel',
36583             xns: Roo.grid,
36584             dataIndex : 'weekday1',
36585             header : 'Monday',
36586             renderer : cellRender
36587         },
36588         {
36589             xtype: 'ColumnModel',
36590             xns: Roo.grid,
36591             dataIndex : 'weekday2',
36592             header : 'Tuesday',
36593             renderer : cellRender
36594         },
36595         {
36596             xtype: 'ColumnModel',
36597             xns: Roo.grid,
36598             dataIndex : 'weekday3',
36599             header : 'Wednesday',
36600             renderer : cellRender
36601         },
36602         {
36603             xtype: 'ColumnModel',
36604             xns: Roo.grid,
36605             dataIndex : 'weekday4',
36606             header : 'Thursday',
36607             renderer : cellRender
36608         },
36609         {
36610             xtype: 'ColumnModel',
36611             xns: Roo.grid,
36612             dataIndex : 'weekday5',
36613             header : 'Friday',
36614             renderer : cellRender
36615         },
36616         {
36617             xtype: 'ColumnModel',
36618             xns: Roo.grid,
36619             dataIndex : 'weekday6',
36620             header : 'Saturday',
36621             renderer : cellRender
36622         }
36623     ]);
36624     this.cm = this.colModel;
36625     this.cm.xmodule = this.xmodule || false;
36626  
36627         
36628           
36629     //this.selModel = new Roo.grid.CellSelectionModel();
36630     //this.sm = this.selModel;
36631     //this.selModel.init(this);
36632     
36633     
36634     if(this.width){
36635         this.container.setWidth(this.width);
36636     }
36637
36638     if(this.height){
36639         this.container.setHeight(this.height);
36640     }
36641     /** @private */
36642         this.addEvents({
36643         // raw events
36644         /**
36645          * @event click
36646          * The raw click event for the entire grid.
36647          * @param {Roo.EventObject} e
36648          */
36649         "click" : true,
36650         /**
36651          * @event dblclick
36652          * The raw dblclick event for the entire grid.
36653          * @param {Roo.EventObject} e
36654          */
36655         "dblclick" : true,
36656         /**
36657          * @event contextmenu
36658          * The raw contextmenu event for the entire grid.
36659          * @param {Roo.EventObject} e
36660          */
36661         "contextmenu" : true,
36662         /**
36663          * @event mousedown
36664          * The raw mousedown event for the entire grid.
36665          * @param {Roo.EventObject} e
36666          */
36667         "mousedown" : true,
36668         /**
36669          * @event mouseup
36670          * The raw mouseup event for the entire grid.
36671          * @param {Roo.EventObject} e
36672          */
36673         "mouseup" : true,
36674         /**
36675          * @event mouseover
36676          * The raw mouseover event for the entire grid.
36677          * @param {Roo.EventObject} e
36678          */
36679         "mouseover" : true,
36680         /**
36681          * @event mouseout
36682          * The raw mouseout event for the entire grid.
36683          * @param {Roo.EventObject} e
36684          */
36685         "mouseout" : true,
36686         /**
36687          * @event keypress
36688          * The raw keypress event for the entire grid.
36689          * @param {Roo.EventObject} e
36690          */
36691         "keypress" : true,
36692         /**
36693          * @event keydown
36694          * The raw keydown event for the entire grid.
36695          * @param {Roo.EventObject} e
36696          */
36697         "keydown" : true,
36698
36699         // custom events
36700
36701         /**
36702          * @event cellclick
36703          * Fires when a cell is clicked
36704          * @param {Grid} this
36705          * @param {Number} rowIndex
36706          * @param {Number} columnIndex
36707          * @param {Roo.EventObject} e
36708          */
36709         "cellclick" : true,
36710         /**
36711          * @event celldblclick
36712          * Fires when a cell is double clicked
36713          * @param {Grid} this
36714          * @param {Number} rowIndex
36715          * @param {Number} columnIndex
36716          * @param {Roo.EventObject} e
36717          */
36718         "celldblclick" : true,
36719         /**
36720          * @event rowclick
36721          * Fires when a row is clicked
36722          * @param {Grid} this
36723          * @param {Number} rowIndex
36724          * @param {Roo.EventObject} e
36725          */
36726         "rowclick" : true,
36727         /**
36728          * @event rowdblclick
36729          * Fires when a row is double clicked
36730          * @param {Grid} this
36731          * @param {Number} rowIndex
36732          * @param {Roo.EventObject} e
36733          */
36734         "rowdblclick" : true,
36735         /**
36736          * @event headerclick
36737          * Fires when a header is clicked
36738          * @param {Grid} this
36739          * @param {Number} columnIndex
36740          * @param {Roo.EventObject} e
36741          */
36742         "headerclick" : true,
36743         /**
36744          * @event headerdblclick
36745          * Fires when a header cell is double clicked
36746          * @param {Grid} this
36747          * @param {Number} columnIndex
36748          * @param {Roo.EventObject} e
36749          */
36750         "headerdblclick" : true,
36751         /**
36752          * @event rowcontextmenu
36753          * Fires when a row is right clicked
36754          * @param {Grid} this
36755          * @param {Number} rowIndex
36756          * @param {Roo.EventObject} e
36757          */
36758         "rowcontextmenu" : true,
36759         /**
36760          * @event cellcontextmenu
36761          * Fires when a cell is right clicked
36762          * @param {Grid} this
36763          * @param {Number} rowIndex
36764          * @param {Number} cellIndex
36765          * @param {Roo.EventObject} e
36766          */
36767          "cellcontextmenu" : true,
36768         /**
36769          * @event headercontextmenu
36770          * Fires when a header is right clicked
36771          * @param {Grid} this
36772          * @param {Number} columnIndex
36773          * @param {Roo.EventObject} e
36774          */
36775         "headercontextmenu" : true,
36776         /**
36777          * @event bodyscroll
36778          * Fires when the body element is scrolled
36779          * @param {Number} scrollLeft
36780          * @param {Number} scrollTop
36781          */
36782         "bodyscroll" : true,
36783         /**
36784          * @event columnresize
36785          * Fires when the user resizes a column
36786          * @param {Number} columnIndex
36787          * @param {Number} newSize
36788          */
36789         "columnresize" : true,
36790         /**
36791          * @event columnmove
36792          * Fires when the user moves a column
36793          * @param {Number} oldIndex
36794          * @param {Number} newIndex
36795          */
36796         "columnmove" : true,
36797         /**
36798          * @event startdrag
36799          * Fires when row(s) start being dragged
36800          * @param {Grid} this
36801          * @param {Roo.GridDD} dd The drag drop object
36802          * @param {event} e The raw browser event
36803          */
36804         "startdrag" : true,
36805         /**
36806          * @event enddrag
36807          * Fires when a drag operation is complete
36808          * @param {Grid} this
36809          * @param {Roo.GridDD} dd The drag drop object
36810          * @param {event} e The raw browser event
36811          */
36812         "enddrag" : true,
36813         /**
36814          * @event dragdrop
36815          * Fires when dragged row(s) are dropped on a valid DD target
36816          * @param {Grid} this
36817          * @param {Roo.GridDD} dd The drag drop object
36818          * @param {String} targetId The target drag drop object
36819          * @param {event} e The raw browser event
36820          */
36821         "dragdrop" : true,
36822         /**
36823          * @event dragover
36824          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
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         "dragover" : true,
36831         /**
36832          * @event dragenter
36833          *  Fires when the dragged row(s) first cross another DD target while being dragged
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         "dragenter" : true,
36840         /**
36841          * @event dragout
36842          * Fires when the dragged row(s) leave 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         "dragout" : true,
36849         /**
36850          * @event rowclass
36851          * Fires when a row is rendered, so you can change add a style to it.
36852          * @param {GridView} gridview   The grid view
36853          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36854          */
36855         'rowclass' : true,
36856
36857         /**
36858          * @event render
36859          * Fires when the grid is rendered
36860          * @param {Grid} grid
36861          */
36862         'render' : true,
36863             /**
36864              * @event select
36865              * Fires when a date is selected
36866              * @param {DatePicker} this
36867              * @param {Date} date The selected date
36868              */
36869         'select': true,
36870         /**
36871              * @event monthchange
36872              * Fires when the displayed month changes 
36873              * @param {DatePicker} this
36874              * @param {Date} date The selected month
36875              */
36876         'monthchange': true,
36877         /**
36878              * @event evententer
36879              * Fires when mouse over an event
36880              * @param {Calendar} this
36881              * @param {event} Event
36882              */
36883         'evententer': true,
36884         /**
36885              * @event eventleave
36886              * Fires when the mouse leaves an
36887              * @param {Calendar} this
36888              * @param {event}
36889              */
36890         'eventleave': true,
36891         /**
36892              * @event eventclick
36893              * Fires when the mouse click an
36894              * @param {Calendar} this
36895              * @param {event}
36896              */
36897         'eventclick': true,
36898         /**
36899              * @event eventrender
36900              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36901              * @param {Calendar} this
36902              * @param {data} data to be modified
36903              */
36904         'eventrender': true
36905         
36906     });
36907
36908     Roo.grid.Grid.superclass.constructor.call(this);
36909     this.on('render', function() {
36910         this.view.el.addClass('x-grid-cal'); 
36911         
36912         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36913
36914     },this);
36915     
36916     if (!Roo.grid.Calendar.style) {
36917         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36918             
36919             
36920             '.x-grid-cal .x-grid-col' :  {
36921                 height: 'auto !important',
36922                 'vertical-align': 'top'
36923             },
36924             '.x-grid-cal  .fc-event-hori' : {
36925                 height: '14px'
36926             }
36927              
36928             
36929         }, Roo.id());
36930     }
36931
36932     
36933     
36934 };
36935 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36936     /**
36937      * @cfg {Store} eventStore The store that loads events.
36938      */
36939     eventStore : 25,
36940
36941      
36942     activeDate : false,
36943     startDay : 0,
36944     autoWidth : true,
36945     monitorWindowResize : false,
36946
36947     
36948     resizeColumns : function() {
36949         var col = (this.view.el.getWidth() / 7) - 3;
36950         // loop through cols, and setWidth
36951         for(var i =0 ; i < 7 ; i++){
36952             this.cm.setColumnWidth(i, col);
36953         }
36954     },
36955      setDate :function(date) {
36956         
36957         Roo.log('setDate?');
36958         
36959         this.resizeColumns();
36960         var vd = this.activeDate;
36961         this.activeDate = date;
36962 //        if(vd && this.el){
36963 //            var t = date.getTime();
36964 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36965 //                Roo.log('using add remove');
36966 //                
36967 //                this.fireEvent('monthchange', this, date);
36968 //                
36969 //                this.cells.removeClass("fc-state-highlight");
36970 //                this.cells.each(function(c){
36971 //                   if(c.dateValue == t){
36972 //                       c.addClass("fc-state-highlight");
36973 //                       setTimeout(function(){
36974 //                            try{c.dom.firstChild.focus();}catch(e){}
36975 //                       }, 50);
36976 //                       return false;
36977 //                   }
36978 //                   return true;
36979 //                });
36980 //                return;
36981 //            }
36982 //        }
36983         
36984         var days = date.getDaysInMonth();
36985         
36986         var firstOfMonth = date.getFirstDateOfMonth();
36987         var startingPos = firstOfMonth.getDay()-this.startDay;
36988         
36989         if(startingPos < this.startDay){
36990             startingPos += 7;
36991         }
36992         
36993         var pm = date.add(Date.MONTH, -1);
36994         var prevStart = pm.getDaysInMonth()-startingPos;
36995 //        
36996         
36997         
36998         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
36999         
37000         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37001         //this.cells.addClassOnOver('fc-state-hover');
37002         
37003         var cells = this.cells.elements;
37004         var textEls = this.textNodes;
37005         
37006         //Roo.each(cells, function(cell){
37007         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37008         //});
37009         
37010         days += startingPos;
37011
37012         // convert everything to numbers so it's fast
37013         var day = 86400000;
37014         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37015         //Roo.log(d);
37016         //Roo.log(pm);
37017         //Roo.log(prevStart);
37018         
37019         var today = new Date().clearTime().getTime();
37020         var sel = date.clearTime().getTime();
37021         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37022         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37023         var ddMatch = this.disabledDatesRE;
37024         var ddText = this.disabledDatesText;
37025         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37026         var ddaysText = this.disabledDaysText;
37027         var format = this.format;
37028         
37029         var setCellClass = function(cal, cell){
37030             
37031             //Roo.log('set Cell Class');
37032             cell.title = "";
37033             var t = d.getTime();
37034             
37035             //Roo.log(d);
37036             
37037             
37038             cell.dateValue = t;
37039             if(t == today){
37040                 cell.className += " fc-today";
37041                 cell.className += " fc-state-highlight";
37042                 cell.title = cal.todayText;
37043             }
37044             if(t == sel){
37045                 // disable highlight in other month..
37046                 cell.className += " fc-state-highlight";
37047                 
37048             }
37049             // disabling
37050             if(t < min) {
37051                 //cell.className = " fc-state-disabled";
37052                 cell.title = cal.minText;
37053                 return;
37054             }
37055             if(t > max) {
37056                 //cell.className = " fc-state-disabled";
37057                 cell.title = cal.maxText;
37058                 return;
37059             }
37060             if(ddays){
37061                 if(ddays.indexOf(d.getDay()) != -1){
37062                     // cell.title = ddaysText;
37063                    // cell.className = " fc-state-disabled";
37064                 }
37065             }
37066             if(ddMatch && format){
37067                 var fvalue = d.dateFormat(format);
37068                 if(ddMatch.test(fvalue)){
37069                     cell.title = ddText.replace("%0", fvalue);
37070                    cell.className = " fc-state-disabled";
37071                 }
37072             }
37073             
37074             if (!cell.initialClassName) {
37075                 cell.initialClassName = cell.dom.className;
37076             }
37077             
37078             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37079         };
37080
37081         var i = 0;
37082         
37083         for(; i < startingPos; i++) {
37084             cells[i].dayName =  (++prevStart);
37085             Roo.log(textEls[i]);
37086             d.setDate(d.getDate()+1);
37087             
37088             //cells[i].className = "fc-past fc-other-month";
37089             setCellClass(this, cells[i]);
37090         }
37091         
37092         var intDay = 0;
37093         
37094         for(; i < days; i++){
37095             intDay = i - startingPos + 1;
37096             cells[i].dayName =  (intDay);
37097             d.setDate(d.getDate()+1);
37098             
37099             cells[i].className = ''; // "x-date-active";
37100             setCellClass(this, cells[i]);
37101         }
37102         var extraDays = 0;
37103         
37104         for(; i < 42; i++) {
37105             //textEls[i].innerHTML = (++extraDays);
37106             
37107             d.setDate(d.getDate()+1);
37108             cells[i].dayName = (++extraDays);
37109             cells[i].className = "fc-future fc-other-month";
37110             setCellClass(this, cells[i]);
37111         }
37112         
37113         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37114         
37115         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37116         
37117         // this will cause all the cells to mis
37118         var rows= [];
37119         var i =0;
37120         for (var r = 0;r < 6;r++) {
37121             for (var c =0;c < 7;c++) {
37122                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37123             }    
37124         }
37125         
37126         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37127         for(i=0;i<cells.length;i++) {
37128             
37129             this.cells.elements[i].dayName = cells[i].dayName ;
37130             this.cells.elements[i].className = cells[i].className;
37131             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37132             this.cells.elements[i].title = cells[i].title ;
37133             this.cells.elements[i].dateValue = cells[i].dateValue ;
37134         }
37135         
37136         
37137         
37138         
37139         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37140         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37141         
37142         ////if(totalRows != 6){
37143             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37144            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37145        // }
37146         
37147         this.fireEvent('monthchange', this, date);
37148         
37149         
37150     },
37151  /**
37152      * Returns the grid's SelectionModel.
37153      * @return {SelectionModel}
37154      */
37155     getSelectionModel : function(){
37156         if(!this.selModel){
37157             this.selModel = new Roo.grid.CellSelectionModel();
37158         }
37159         return this.selModel;
37160     },
37161
37162     load: function() {
37163         this.eventStore.load()
37164         
37165         
37166         
37167     },
37168     
37169     findCell : function(dt) {
37170         dt = dt.clearTime().getTime();
37171         var ret = false;
37172         this.cells.each(function(c){
37173             //Roo.log("check " +c.dateValue + '?=' + dt);
37174             if(c.dateValue == dt){
37175                 ret = c;
37176                 return false;
37177             }
37178             return true;
37179         });
37180         
37181         return ret;
37182     },
37183     
37184     findCells : function(rec) {
37185         var s = rec.data.start_dt.clone().clearTime().getTime();
37186        // Roo.log(s);
37187         var e= rec.data.end_dt.clone().clearTime().getTime();
37188        // Roo.log(e);
37189         var ret = [];
37190         this.cells.each(function(c){
37191              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37192             
37193             if(c.dateValue > e){
37194                 return ;
37195             }
37196             if(c.dateValue < s){
37197                 return ;
37198             }
37199             ret.push(c);
37200         });
37201         
37202         return ret;    
37203     },
37204     
37205     findBestRow: function(cells)
37206     {
37207         var ret = 0;
37208         
37209         for (var i =0 ; i < cells.length;i++) {
37210             ret  = Math.max(cells[i].rows || 0,ret);
37211         }
37212         return ret;
37213         
37214     },
37215     
37216     
37217     addItem : function(rec)
37218     {
37219         // look for vertical location slot in
37220         var cells = this.findCells(rec);
37221         
37222         rec.row = this.findBestRow(cells);
37223         
37224         // work out the location.
37225         
37226         var crow = false;
37227         var rows = [];
37228         for(var i =0; i < cells.length; i++) {
37229             if (!crow) {
37230                 crow = {
37231                     start : cells[i],
37232                     end :  cells[i]
37233                 };
37234                 continue;
37235             }
37236             if (crow.start.getY() == cells[i].getY()) {
37237                 // on same row.
37238                 crow.end = cells[i];
37239                 continue;
37240             }
37241             // different row.
37242             rows.push(crow);
37243             crow = {
37244                 start: cells[i],
37245                 end : cells[i]
37246             };
37247             
37248         }
37249         
37250         rows.push(crow);
37251         rec.els = [];
37252         rec.rows = rows;
37253         rec.cells = cells;
37254         for (var i = 0; i < cells.length;i++) {
37255             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37256             
37257         }
37258         
37259         
37260     },
37261     
37262     clearEvents: function() {
37263         
37264         if (!this.eventStore.getCount()) {
37265             return;
37266         }
37267         // reset number of rows in cells.
37268         Roo.each(this.cells.elements, function(c){
37269             c.rows = 0;
37270         });
37271         
37272         this.eventStore.each(function(e) {
37273             this.clearEvent(e);
37274         },this);
37275         
37276     },
37277     
37278     clearEvent : function(ev)
37279     {
37280         if (ev.els) {
37281             Roo.each(ev.els, function(el) {
37282                 el.un('mouseenter' ,this.onEventEnter, this);
37283                 el.un('mouseleave' ,this.onEventLeave, this);
37284                 el.remove();
37285             },this);
37286             ev.els = [];
37287         }
37288     },
37289     
37290     
37291     renderEvent : function(ev,ctr) {
37292         if (!ctr) {
37293              ctr = this.view.el.select('.fc-event-container',true).first();
37294         }
37295         
37296          
37297         this.clearEvent(ev);
37298             //code
37299        
37300         
37301         
37302         ev.els = [];
37303         var cells = ev.cells;
37304         var rows = ev.rows;
37305         this.fireEvent('eventrender', this, ev);
37306         
37307         for(var i =0; i < rows.length; i++) {
37308             
37309             cls = '';
37310             if (i == 0) {
37311                 cls += ' fc-event-start';
37312             }
37313             if ((i+1) == rows.length) {
37314                 cls += ' fc-event-end';
37315             }
37316             
37317             //Roo.log(ev.data);
37318             // how many rows should it span..
37319             var cg = this.eventTmpl.append(ctr,Roo.apply({
37320                 fccls : cls
37321                 
37322             }, ev.data) , true);
37323             
37324             
37325             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37326             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37327             cg.on('click', this.onEventClick, this, ev);
37328             
37329             ev.els.push(cg);
37330             
37331             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37332             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37333             //Roo.log(cg);
37334              
37335             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37336             cg.setWidth(ebox.right - sbox.x -2);
37337         }
37338     },
37339     
37340     renderEvents: function()
37341     {   
37342         // first make sure there is enough space..
37343         
37344         if (!this.eventTmpl) {
37345             this.eventTmpl = new Roo.Template(
37346                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37347                     '<div class="fc-event-inner">' +
37348                         '<span class="fc-event-time">{time}</span>' +
37349                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37350                     '</div>' +
37351                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37352                 '</div>'
37353             );
37354                 
37355         }
37356                
37357         
37358         
37359         this.cells.each(function(c) {
37360             //Roo.log(c.select('.fc-day-content div',true).first());
37361             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37362         });
37363         
37364         var ctr = this.view.el.select('.fc-event-container',true).first();
37365         
37366         var cls;
37367         this.eventStore.each(function(ev){
37368             
37369             this.renderEvent(ev);
37370              
37371              
37372         }, this);
37373         this.view.layout();
37374         
37375     },
37376     
37377     onEventEnter: function (e, el,event,d) {
37378         this.fireEvent('evententer', this, el, event);
37379     },
37380     
37381     onEventLeave: function (e, el,event,d) {
37382         this.fireEvent('eventleave', this, el, event);
37383     },
37384     
37385     onEventClick: function (e, el,event,d) {
37386         this.fireEvent('eventclick', this, el, event);
37387     },
37388     
37389     onMonthChange: function () {
37390         this.store.load();
37391     },
37392     
37393     onLoad: function () {
37394         
37395         //Roo.log('calendar onload');
37396 //         
37397         if(this.eventStore.getCount() > 0){
37398             
37399            
37400             
37401             this.eventStore.each(function(d){
37402                 
37403                 
37404                 // FIXME..
37405                 var add =   d.data;
37406                 if (typeof(add.end_dt) == 'undefined')  {
37407                     Roo.log("Missing End time in calendar data: ");
37408                     Roo.log(d);
37409                     return;
37410                 }
37411                 if (typeof(add.start_dt) == 'undefined')  {
37412                     Roo.log("Missing Start time in calendar data: ");
37413                     Roo.log(d);
37414                     return;
37415                 }
37416                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37417                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37418                 add.id = add.id || d.id;
37419                 add.title = add.title || '??';
37420                 
37421                 this.addItem(d);
37422                 
37423              
37424             },this);
37425         }
37426         
37427         this.renderEvents();
37428     }
37429     
37430
37431 });
37432 /*
37433  grid : {
37434                 xtype: 'Grid',
37435                 xns: Roo.grid,
37436                 listeners : {
37437                     render : function ()
37438                     {
37439                         _this.grid = this;
37440                         
37441                         if (!this.view.el.hasClass('course-timesheet')) {
37442                             this.view.el.addClass('course-timesheet');
37443                         }
37444                         if (this.tsStyle) {
37445                             this.ds.load({});
37446                             return; 
37447                         }
37448                         Roo.log('width');
37449                         Roo.log(_this.grid.view.el.getWidth());
37450                         
37451                         
37452                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37453                             '.course-timesheet .x-grid-row' : {
37454                                 height: '80px'
37455                             },
37456                             '.x-grid-row td' : {
37457                                 'vertical-align' : 0
37458                             },
37459                             '.course-edit-link' : {
37460                                 'color' : 'blue',
37461                                 'text-overflow' : 'ellipsis',
37462                                 'overflow' : 'hidden',
37463                                 'white-space' : 'nowrap',
37464                                 'cursor' : 'pointer'
37465                             },
37466                             '.sub-link' : {
37467                                 'color' : 'green'
37468                             },
37469                             '.de-act-sup-link' : {
37470                                 'color' : 'purple',
37471                                 'text-decoration' : 'line-through'
37472                             },
37473                             '.de-act-link' : {
37474                                 'color' : 'red',
37475                                 'text-decoration' : 'line-through'
37476                             },
37477                             '.course-timesheet .course-highlight' : {
37478                                 'border-top-style': 'dashed !important',
37479                                 'border-bottom-bottom': 'dashed !important'
37480                             },
37481                             '.course-timesheet .course-item' : {
37482                                 'font-family'   : 'tahoma, arial, helvetica',
37483                                 'font-size'     : '11px',
37484                                 'overflow'      : 'hidden',
37485                                 'padding-left'  : '10px',
37486                                 'padding-right' : '10px',
37487                                 'padding-top' : '10px' 
37488                             }
37489                             
37490                         }, Roo.id());
37491                                 this.ds.load({});
37492                     }
37493                 },
37494                 autoWidth : true,
37495                 monitorWindowResize : false,
37496                 cellrenderer : function(v,x,r)
37497                 {
37498                     return v;
37499                 },
37500                 sm : {
37501                     xtype: 'CellSelectionModel',
37502                     xns: Roo.grid
37503                 },
37504                 dataSource : {
37505                     xtype: 'Store',
37506                     xns: Roo.data,
37507                     listeners : {
37508                         beforeload : function (_self, options)
37509                         {
37510                             options.params = options.params || {};
37511                             options.params._month = _this.monthField.getValue();
37512                             options.params.limit = 9999;
37513                             options.params['sort'] = 'when_dt';    
37514                             options.params['dir'] = 'ASC';    
37515                             this.proxy.loadResponse = this.loadResponse;
37516                             Roo.log("load?");
37517                             //this.addColumns();
37518                         },
37519                         load : function (_self, records, options)
37520                         {
37521                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37522                                 // if you click on the translation.. you can edit it...
37523                                 var el = Roo.get(this);
37524                                 var id = el.dom.getAttribute('data-id');
37525                                 var d = el.dom.getAttribute('data-date');
37526                                 var t = el.dom.getAttribute('data-time');
37527                                 //var id = this.child('span').dom.textContent;
37528                                 
37529                                 //Roo.log(this);
37530                                 Pman.Dialog.CourseCalendar.show({
37531                                     id : id,
37532                                     when_d : d,
37533                                     when_t : t,
37534                                     productitem_active : id ? 1 : 0
37535                                 }, function() {
37536                                     _this.grid.ds.load({});
37537                                 });
37538                            
37539                            });
37540                            
37541                            _this.panel.fireEvent('resize', [ '', '' ]);
37542                         }
37543                     },
37544                     loadResponse : function(o, success, response){
37545                             // this is overridden on before load..
37546                             
37547                             Roo.log("our code?");       
37548                             //Roo.log(success);
37549                             //Roo.log(response)
37550                             delete this.activeRequest;
37551                             if(!success){
37552                                 this.fireEvent("loadexception", this, o, response);
37553                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37554                                 return;
37555                             }
37556                             var result;
37557                             try {
37558                                 result = o.reader.read(response);
37559                             }catch(e){
37560                                 Roo.log("load exception?");
37561                                 this.fireEvent("loadexception", this, o, response, e);
37562                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37563                                 return;
37564                             }
37565                             Roo.log("ready...");        
37566                             // loop through result.records;
37567                             // and set this.tdate[date] = [] << array of records..
37568                             _this.tdata  = {};
37569                             Roo.each(result.records, function(r){
37570                                 //Roo.log(r.data);
37571                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37572                                     _this.tdata[r.data.when_dt.format('j')] = [];
37573                                 }
37574                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37575                             });
37576                             
37577                             //Roo.log(_this.tdata);
37578                             
37579                             result.records = [];
37580                             result.totalRecords = 6;
37581                     
37582                             // let's generate some duumy records for the rows.
37583                             //var st = _this.dateField.getValue();
37584                             
37585                             // work out monday..
37586                             //st = st.add(Date.DAY, -1 * st.format('w'));
37587                             
37588                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37589                             
37590                             var firstOfMonth = date.getFirstDayOfMonth();
37591                             var days = date.getDaysInMonth();
37592                             var d = 1;
37593                             var firstAdded = false;
37594                             for (var i = 0; i < result.totalRecords ; i++) {
37595                                 //var d= st.add(Date.DAY, i);
37596                                 var row = {};
37597                                 var added = 0;
37598                                 for(var w = 0 ; w < 7 ; w++){
37599                                     if(!firstAdded && firstOfMonth != w){
37600                                         continue;
37601                                     }
37602                                     if(d > days){
37603                                         continue;
37604                                     }
37605                                     firstAdded = true;
37606                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37607                                     row['weekday'+w] = String.format(
37608                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37609                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37610                                                     d,
37611                                                     date.format('Y-m-')+dd
37612                                                 );
37613                                     added++;
37614                                     if(typeof(_this.tdata[d]) != 'undefined'){
37615                                         Roo.each(_this.tdata[d], function(r){
37616                                             var is_sub = '';
37617                                             var deactive = '';
37618                                             var id = r.id;
37619                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37620                                             if(r.parent_id*1>0){
37621                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37622                                                 id = r.parent_id;
37623                                             }
37624                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37625                                                 deactive = 'de-act-link';
37626                                             }
37627                                             
37628                                             row['weekday'+w] += String.format(
37629                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37630                                                     id, //0
37631                                                     r.product_id_name, //1
37632                                                     r.when_dt.format('h:ia'), //2
37633                                                     is_sub, //3
37634                                                     deactive, //4
37635                                                     desc // 5
37636                                             );
37637                                         });
37638                                     }
37639                                     d++;
37640                                 }
37641                                 
37642                                 // only do this if something added..
37643                                 if(added > 0){ 
37644                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37645                                 }
37646                                 
37647                                 
37648                                 // push it twice. (second one with an hour..
37649                                 
37650                             }
37651                             //Roo.log(result);
37652                             this.fireEvent("load", this, o, o.request.arg);
37653                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37654                         },
37655                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37656                     proxy : {
37657                         xtype: 'HttpProxy',
37658                         xns: Roo.data,
37659                         method : 'GET',
37660                         url : baseURL + '/Roo/Shop_course.php'
37661                     },
37662                     reader : {
37663                         xtype: 'JsonReader',
37664                         xns: Roo.data,
37665                         id : 'id',
37666                         fields : [
37667                             {
37668                                 'name': 'id',
37669                                 'type': 'int'
37670                             },
37671                             {
37672                                 'name': 'when_dt',
37673                                 'type': 'string'
37674                             },
37675                             {
37676                                 'name': 'end_dt',
37677                                 'type': 'string'
37678                             },
37679                             {
37680                                 'name': 'parent_id',
37681                                 'type': 'int'
37682                             },
37683                             {
37684                                 'name': 'product_id',
37685                                 'type': 'int'
37686                             },
37687                             {
37688                                 'name': 'productitem_id',
37689                                 'type': 'int'
37690                             },
37691                             {
37692                                 'name': 'guid',
37693                                 'type': 'int'
37694                             }
37695                         ]
37696                     }
37697                 },
37698                 toolbar : {
37699                     xtype: 'Toolbar',
37700                     xns: Roo,
37701                     items : [
37702                         {
37703                             xtype: 'Button',
37704                             xns: Roo.Toolbar,
37705                             listeners : {
37706                                 click : function (_self, e)
37707                                 {
37708                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37709                                     sd.setMonth(sd.getMonth()-1);
37710                                     _this.monthField.setValue(sd.format('Y-m-d'));
37711                                     _this.grid.ds.load({});
37712                                 }
37713                             },
37714                             text : "Back"
37715                         },
37716                         {
37717                             xtype: 'Separator',
37718                             xns: Roo.Toolbar
37719                         },
37720                         {
37721                             xtype: 'MonthField',
37722                             xns: Roo.form,
37723                             listeners : {
37724                                 render : function (_self)
37725                                 {
37726                                     _this.monthField = _self;
37727                                    // _this.monthField.set  today
37728                                 },
37729                                 select : function (combo, date)
37730                                 {
37731                                     _this.grid.ds.load({});
37732                                 }
37733                             },
37734                             value : (function() { return new Date(); })()
37735                         },
37736                         {
37737                             xtype: 'Separator',
37738                             xns: Roo.Toolbar
37739                         },
37740                         {
37741                             xtype: 'TextItem',
37742                             xns: Roo.Toolbar,
37743                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37744                         },
37745                         {
37746                             xtype: 'Fill',
37747                             xns: Roo.Toolbar
37748                         },
37749                         {
37750                             xtype: 'Button',
37751                             xns: Roo.Toolbar,
37752                             listeners : {
37753                                 click : function (_self, e)
37754                                 {
37755                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37756                                     sd.setMonth(sd.getMonth()+1);
37757                                     _this.monthField.setValue(sd.format('Y-m-d'));
37758                                     _this.grid.ds.load({});
37759                                 }
37760                             },
37761                             text : "Next"
37762                         }
37763                     ]
37764                 },
37765                  
37766             }
37767         };
37768         
37769         *//*
37770  * Based on:
37771  * Ext JS Library 1.1.1
37772  * Copyright(c) 2006-2007, Ext JS, LLC.
37773  *
37774  * Originally Released Under LGPL - original licence link has changed is not relivant.
37775  *
37776  * Fork - LGPL
37777  * <script type="text/javascript">
37778  */
37779  
37780 /**
37781  * @class Roo.LoadMask
37782  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37783  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37784  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37785  * element's UpdateManager load indicator and will be destroyed after the initial load.
37786  * @constructor
37787  * Create a new LoadMask
37788  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37789  * @param {Object} config The config object
37790  */
37791 Roo.LoadMask = function(el, config){
37792     this.el = Roo.get(el);
37793     Roo.apply(this, config);
37794     if(this.store){
37795         this.store.on('beforeload', this.onBeforeLoad, this);
37796         this.store.on('load', this.onLoad, this);
37797         this.store.on('loadexception', this.onLoadException, this);
37798         this.removeMask = false;
37799     }else{
37800         var um = this.el.getUpdateManager();
37801         um.showLoadIndicator = false; // disable the default indicator
37802         um.on('beforeupdate', this.onBeforeLoad, this);
37803         um.on('update', this.onLoad, this);
37804         um.on('failure', this.onLoad, this);
37805         this.removeMask = true;
37806     }
37807 };
37808
37809 Roo.LoadMask.prototype = {
37810     /**
37811      * @cfg {Boolean} removeMask
37812      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37813      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37814      */
37815     /**
37816      * @cfg {String} msg
37817      * The text to display in a centered loading message box (defaults to 'Loading...')
37818      */
37819     msg : 'Loading...',
37820     /**
37821      * @cfg {String} msgCls
37822      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37823      */
37824     msgCls : 'x-mask-loading',
37825
37826     /**
37827      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37828      * @type Boolean
37829      */
37830     disabled: false,
37831
37832     /**
37833      * Disables the mask to prevent it from being displayed
37834      */
37835     disable : function(){
37836        this.disabled = true;
37837     },
37838
37839     /**
37840      * Enables the mask so that it can be displayed
37841      */
37842     enable : function(){
37843         this.disabled = false;
37844     },
37845     
37846     onLoadException : function()
37847     {
37848         Roo.log(arguments);
37849         
37850         if (typeof(arguments[3]) != 'undefined') {
37851             Roo.MessageBox.alert("Error loading",arguments[3]);
37852         } 
37853         /*
37854         try {
37855             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37856                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37857             }   
37858         } catch(e) {
37859             
37860         }
37861         */
37862     
37863         
37864         
37865         this.el.unmask(this.removeMask);
37866     },
37867     // private
37868     onLoad : function()
37869     {
37870         this.el.unmask(this.removeMask);
37871     },
37872
37873     // private
37874     onBeforeLoad : function(){
37875         if(!this.disabled){
37876             this.el.mask(this.msg, this.msgCls);
37877         }
37878     },
37879
37880     // private
37881     destroy : function(){
37882         if(this.store){
37883             this.store.un('beforeload', this.onBeforeLoad, this);
37884             this.store.un('load', this.onLoad, this);
37885             this.store.un('loadexception', this.onLoadException, this);
37886         }else{
37887             var um = this.el.getUpdateManager();
37888             um.un('beforeupdate', this.onBeforeLoad, this);
37889             um.un('update', this.onLoad, this);
37890             um.un('failure', this.onLoad, this);
37891         }
37892     }
37893 };/*
37894  * Based on:
37895  * Ext JS Library 1.1.1
37896  * Copyright(c) 2006-2007, Ext JS, LLC.
37897  *
37898  * Originally Released Under LGPL - original licence link has changed is not relivant.
37899  *
37900  * Fork - LGPL
37901  * <script type="text/javascript">
37902  */
37903
37904
37905 /**
37906  * @class Roo.XTemplate
37907  * @extends Roo.Template
37908  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37909 <pre><code>
37910 var t = new Roo.XTemplate(
37911         '&lt;select name="{name}"&gt;',
37912                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37913         '&lt;/select&gt;'
37914 );
37915  
37916 // then append, applying the master template values
37917  </code></pre>
37918  *
37919  * Supported features:
37920  *
37921  *  Tags:
37922
37923 <pre><code>
37924       {a_variable} - output encoded.
37925       {a_variable.format:("Y-m-d")} - call a method on the variable
37926       {a_variable:raw} - unencoded output
37927       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37928       {a_variable:this.method_on_template(...)} - call a method on the template object.
37929  
37930 </code></pre>
37931  *  The tpl tag:
37932 <pre><code>
37933         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37934         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37935         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37936         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37937   
37938         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37939         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37940 </code></pre>
37941  *      
37942  */
37943 Roo.XTemplate = function()
37944 {
37945     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37946     if (this.html) {
37947         this.compile();
37948     }
37949 };
37950
37951
37952 Roo.extend(Roo.XTemplate, Roo.Template, {
37953
37954     /**
37955      * The various sub templates
37956      */
37957     tpls : false,
37958     /**
37959      *
37960      * basic tag replacing syntax
37961      * WORD:WORD()
37962      *
37963      * // you can fake an object call by doing this
37964      *  x.t:(test,tesT) 
37965      * 
37966      */
37967     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37968
37969     /**
37970      * compile the template
37971      *
37972      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37973      *
37974      */
37975     compile: function()
37976     {
37977         var s = this.html;
37978      
37979         s = ['<tpl>', s, '</tpl>'].join('');
37980     
37981         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
37982             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
37983             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
37984             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
37985             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
37986             m,
37987             id     = 0,
37988             tpls   = [];
37989     
37990         while(true == !!(m = s.match(re))){
37991             var forMatch   = m[0].match(nameRe),
37992                 ifMatch   = m[0].match(ifRe),
37993                 execMatch   = m[0].match(execRe),
37994                 namedMatch   = m[0].match(namedRe),
37995                 
37996                 exp  = null, 
37997                 fn   = null,
37998                 exec = null,
37999                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38000                 
38001             if (ifMatch) {
38002                 // if - puts fn into test..
38003                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38004                 if(exp){
38005                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38006                 }
38007             }
38008             
38009             if (execMatch) {
38010                 // exec - calls a function... returns empty if true is  returned.
38011                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38012                 if(exp){
38013                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38014                 }
38015             }
38016             
38017             
38018             if (name) {
38019                 // for = 
38020                 switch(name){
38021                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38022                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38023                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38024                 }
38025             }
38026             var uid = namedMatch ? namedMatch[1] : id;
38027             
38028             
38029             tpls.push({
38030                 id:     namedMatch ? namedMatch[1] : id,
38031                 target: name,
38032                 exec:   exec,
38033                 test:   fn,
38034                 body:   m[1] || ''
38035             });
38036             if (namedMatch) {
38037                 s = s.replace(m[0], '');
38038             } else { 
38039                 s = s.replace(m[0], '{xtpl'+ id + '}');
38040             }
38041             ++id;
38042         }
38043         this.tpls = [];
38044         for(var i = tpls.length-1; i >= 0; --i){
38045             this.compileTpl(tpls[i]);
38046             this.tpls[tpls[i].id] = tpls[i];
38047         }
38048         this.master = tpls[tpls.length-1];
38049         return this;
38050     },
38051     /**
38052      * same as applyTemplate, except it's done to one of the subTemplates
38053      * when using named templates, you can do:
38054      *
38055      * var str = pl.applySubTemplate('your-name', values);
38056      *
38057      * 
38058      * @param {Number} id of the template
38059      * @param {Object} values to apply to template
38060      * @param {Object} parent (normaly the instance of this object)
38061      */
38062     applySubTemplate : function(id, values, parent)
38063     {
38064         
38065         
38066         var t = this.tpls[id];
38067         
38068         
38069         try { 
38070             if(t.test && !t.test.call(this, values, parent)){
38071                 return '';
38072             }
38073         } catch(e) {
38074             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38075             Roo.log(e.toString());
38076             Roo.log(t.test);
38077             return ''
38078         }
38079         try { 
38080             
38081             if(t.exec && t.exec.call(this, values, parent)){
38082                 return '';
38083             }
38084         } catch(e) {
38085             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38086             Roo.log(e.toString());
38087             Roo.log(t.exec);
38088             return ''
38089         }
38090         try {
38091             var vs = t.target ? t.target.call(this, values, parent) : values;
38092             parent = t.target ? values : parent;
38093             if(t.target && vs instanceof Array){
38094                 var buf = [];
38095                 for(var i = 0, len = vs.length; i < len; i++){
38096                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38097                 }
38098                 return buf.join('');
38099             }
38100             return t.compiled.call(this, vs, parent);
38101         } catch (e) {
38102             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38103             Roo.log(e.toString());
38104             Roo.log(t.compiled);
38105             return '';
38106         }
38107     },
38108
38109     compileTpl : function(tpl)
38110     {
38111         var fm = Roo.util.Format;
38112         var useF = this.disableFormats !== true;
38113         var sep = Roo.isGecko ? "+" : ",";
38114         var undef = function(str) {
38115             Roo.log("Property not found :"  + str);
38116             return '';
38117         };
38118         
38119         var fn = function(m, name, format, args)
38120         {
38121             //Roo.log(arguments);
38122             args = args ? args.replace(/\\'/g,"'") : args;
38123             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38124             if (typeof(format) == 'undefined') {
38125                 format= 'htmlEncode';
38126             }
38127             if (format == 'raw' ) {
38128                 format = false;
38129             }
38130             
38131             if(name.substr(0, 4) == 'xtpl'){
38132                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38133             }
38134             
38135             // build an array of options to determine if value is undefined..
38136             
38137             // basically get 'xxxx.yyyy' then do
38138             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38139             //    (function () { Roo.log("Property not found"); return ''; })() :
38140             //    ......
38141             
38142             var udef_ar = [];
38143             var lookfor = '';
38144             Roo.each(name.split('.'), function(st) {
38145                 lookfor += (lookfor.length ? '.': '') + st;
38146                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38147             });
38148             
38149             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38150             
38151             
38152             if(format && useF){
38153                 
38154                 args = args ? ',' + args : "";
38155                  
38156                 if(format.substr(0, 5) != "this."){
38157                     format = "fm." + format + '(';
38158                 }else{
38159                     format = 'this.call("'+ format.substr(5) + '", ';
38160                     args = ", values";
38161                 }
38162                 
38163                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38164             }
38165              
38166             if (args.length) {
38167                 // called with xxyx.yuu:(test,test)
38168                 // change to ()
38169                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38170             }
38171             // raw.. - :raw modifier..
38172             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38173             
38174         };
38175         var body;
38176         // branched to use + in gecko and [].join() in others
38177         if(Roo.isGecko){
38178             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38179                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38180                     "';};};";
38181         }else{
38182             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38183             body.push(tpl.body.replace(/(\r\n|\n)/g,
38184                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38185             body.push("'].join('');};};");
38186             body = body.join('');
38187         }
38188         
38189         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38190        
38191         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38192         eval(body);
38193         
38194         return this;
38195     },
38196
38197     applyTemplate : function(values){
38198         return this.master.compiled.call(this, values, {});
38199         //var s = this.subs;
38200     },
38201
38202     apply : function(){
38203         return this.applyTemplate.apply(this, arguments);
38204     }
38205
38206  });
38207
38208 Roo.XTemplate.from = function(el){
38209     el = Roo.getDom(el);
38210     return new Roo.XTemplate(el.value || el.innerHTML);
38211 };