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         if(!this.activated){
20933             return;
20934         }
20935         /*
20936         if(Roo.isIE){
20937             this.win.focus();
20938             var r = this.doc.selection.createRange();
20939             if(r){
20940                 r.collapse(true);
20941                 r.pasteHTML(text);
20942                 this.syncValue();
20943                 this.deferFocus();
20944             
20945             }
20946             return;
20947         }
20948         */
20949         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20950             this.win.focus();
20951             
20952             
20953             // from jquery ui (MIT licenced)
20954             var range, node;
20955             var win = this.win;
20956             
20957             if (win.getSelection && win.getSelection().getRangeAt) {
20958                 range = win.getSelection().getRangeAt(0);
20959                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20960                 range.insertNode(node);
20961             } else if (win.document.selection && win.document.selection.createRange) {
20962                 // no firefox support
20963                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20964                 win.document.selection.createRange().pasteHTML(txt);
20965             } else {
20966                 // no firefox support
20967                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20968                 this.execCmd('InsertHTML', txt);
20969             } 
20970             
20971             this.syncValue();
20972             
20973             this.deferFocus();
20974         }
20975     },
20976  // private
20977     mozKeyPress : function(e){
20978         if(e.ctrlKey){
20979             var c = e.getCharCode(), cmd;
20980           
20981             if(c > 0){
20982                 c = String.fromCharCode(c).toLowerCase();
20983                 switch(c){
20984                     case 'b':
20985                         cmd = 'bold';
20986                         break;
20987                     case 'i':
20988                         cmd = 'italic';
20989                         break;
20990                     
20991                     case 'u':
20992                         cmd = 'underline';
20993                         break;
20994                     
20995                     case 'v':
20996                         this.cleanUpPaste.defer(100, this);
20997                         return;
20998                         
20999                 }
21000                 if(cmd){
21001                     this.win.focus();
21002                     this.execCmd(cmd);
21003                     this.deferFocus();
21004                     e.preventDefault();
21005                 }
21006                 
21007             }
21008         }
21009     },
21010
21011     // private
21012     fixKeys : function(){ // load time branching for fastest keydown performance
21013         if(Roo.isIE){
21014             return function(e){
21015                 var k = e.getKey(), r;
21016                 if(k == e.TAB){
21017                     e.stopEvent();
21018                     r = this.doc.selection.createRange();
21019                     if(r){
21020                         r.collapse(true);
21021                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21022                         this.deferFocus();
21023                     }
21024                     return;
21025                 }
21026                 
21027                 if(k == e.ENTER){
21028                     r = this.doc.selection.createRange();
21029                     if(r){
21030                         var target = r.parentElement();
21031                         if(!target || target.tagName.toLowerCase() != 'li'){
21032                             e.stopEvent();
21033                             r.pasteHTML('<br />');
21034                             r.collapse(false);
21035                             r.select();
21036                         }
21037                     }
21038                 }
21039                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21040                     this.cleanUpPaste.defer(100, this);
21041                     return;
21042                 }
21043                 
21044                 
21045             };
21046         }else if(Roo.isOpera){
21047             return function(e){
21048                 var k = e.getKey();
21049                 if(k == e.TAB){
21050                     e.stopEvent();
21051                     this.win.focus();
21052                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21053                     this.deferFocus();
21054                 }
21055                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21056                     this.cleanUpPaste.defer(100, this);
21057                     return;
21058                 }
21059                 
21060             };
21061         }else if(Roo.isSafari){
21062             return function(e){
21063                 var k = e.getKey();
21064                 
21065                 if(k == e.TAB){
21066                     e.stopEvent();
21067                     this.execCmd('InsertText','\t');
21068                     this.deferFocus();
21069                     return;
21070                 }
21071                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21072                     this.cleanUpPaste.defer(100, this);
21073                     return;
21074                 }
21075                 
21076              };
21077         }
21078     }(),
21079     
21080     getAllAncestors: function()
21081     {
21082         var p = this.getSelectedNode();
21083         var a = [];
21084         if (!p) {
21085             a.push(p); // push blank onto stack..
21086             p = this.getParentElement();
21087         }
21088         
21089         
21090         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21091             a.push(p);
21092             p = p.parentNode;
21093         }
21094         a.push(this.doc.body);
21095         return a;
21096     },
21097     lastSel : false,
21098     lastSelNode : false,
21099     
21100     
21101     getSelection : function() 
21102     {
21103         this.assignDocWin();
21104         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21105     },
21106     
21107     getSelectedNode: function() 
21108     {
21109         // this may only work on Gecko!!!
21110         
21111         // should we cache this!!!!
21112         
21113         
21114         
21115          
21116         var range = this.createRange(this.getSelection()).cloneRange();
21117         
21118         if (Roo.isIE) {
21119             var parent = range.parentElement();
21120             while (true) {
21121                 var testRange = range.duplicate();
21122                 testRange.moveToElementText(parent);
21123                 if (testRange.inRange(range)) {
21124                     break;
21125                 }
21126                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21127                     break;
21128                 }
21129                 parent = parent.parentElement;
21130             }
21131             return parent;
21132         }
21133         
21134         // is ancestor a text element.
21135         var ac =  range.commonAncestorContainer;
21136         if (ac.nodeType == 3) {
21137             ac = ac.parentNode;
21138         }
21139         
21140         var ar = ac.childNodes;
21141          
21142         var nodes = [];
21143         var other_nodes = [];
21144         var has_other_nodes = false;
21145         for (var i=0;i<ar.length;i++) {
21146             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21147                 continue;
21148             }
21149             // fullly contained node.
21150             
21151             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21152                 nodes.push(ar[i]);
21153                 continue;
21154             }
21155             
21156             // probably selected..
21157             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21158                 other_nodes.push(ar[i]);
21159                 continue;
21160             }
21161             // outer..
21162             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21163                 continue;
21164             }
21165             
21166             
21167             has_other_nodes = true;
21168         }
21169         if (!nodes.length && other_nodes.length) {
21170             nodes= other_nodes;
21171         }
21172         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21173             return false;
21174         }
21175         
21176         return nodes[0];
21177     },
21178     createRange: function(sel)
21179     {
21180         // this has strange effects when using with 
21181         // top toolbar - not sure if it's a great idea.
21182         //this.editor.contentWindow.focus();
21183         if (typeof sel != "undefined") {
21184             try {
21185                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21186             } catch(e) {
21187                 return this.doc.createRange();
21188             }
21189         } else {
21190             return this.doc.createRange();
21191         }
21192     },
21193     getParentElement: function()
21194     {
21195         
21196         this.assignDocWin();
21197         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21198         
21199         var range = this.createRange(sel);
21200          
21201         try {
21202             var p = range.commonAncestorContainer;
21203             while (p.nodeType == 3) { // text node
21204                 p = p.parentNode;
21205             }
21206             return p;
21207         } catch (e) {
21208             return null;
21209         }
21210     
21211     },
21212     /***
21213      *
21214      * Range intersection.. the hard stuff...
21215      *  '-1' = before
21216      *  '0' = hits..
21217      *  '1' = after.
21218      *         [ -- selected range --- ]
21219      *   [fail]                        [fail]
21220      *
21221      *    basically..
21222      *      if end is before start or  hits it. fail.
21223      *      if start is after end or hits it fail.
21224      *
21225      *   if either hits (but other is outside. - then it's not 
21226      *   
21227      *    
21228      **/
21229     
21230     
21231     // @see http://www.thismuchiknow.co.uk/?p=64.
21232     rangeIntersectsNode : function(range, node)
21233     {
21234         var nodeRange = node.ownerDocument.createRange();
21235         try {
21236             nodeRange.selectNode(node);
21237         } catch (e) {
21238             nodeRange.selectNodeContents(node);
21239         }
21240     
21241         var rangeStartRange = range.cloneRange();
21242         rangeStartRange.collapse(true);
21243     
21244         var rangeEndRange = range.cloneRange();
21245         rangeEndRange.collapse(false);
21246     
21247         var nodeStartRange = nodeRange.cloneRange();
21248         nodeStartRange.collapse(true);
21249     
21250         var nodeEndRange = nodeRange.cloneRange();
21251         nodeEndRange.collapse(false);
21252     
21253         return rangeStartRange.compareBoundaryPoints(
21254                  Range.START_TO_START, nodeEndRange) == -1 &&
21255                rangeEndRange.compareBoundaryPoints(
21256                  Range.START_TO_START, nodeStartRange) == 1;
21257         
21258          
21259     },
21260     rangeCompareNode : function(range, node)
21261     {
21262         var nodeRange = node.ownerDocument.createRange();
21263         try {
21264             nodeRange.selectNode(node);
21265         } catch (e) {
21266             nodeRange.selectNodeContents(node);
21267         }
21268         
21269         
21270         range.collapse(true);
21271     
21272         nodeRange.collapse(true);
21273      
21274         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21275         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21276          
21277         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21278         
21279         var nodeIsBefore   =  ss == 1;
21280         var nodeIsAfter    = ee == -1;
21281         
21282         if (nodeIsBefore && nodeIsAfter) {
21283             return 0; // outer
21284         }
21285         if (!nodeIsBefore && nodeIsAfter) {
21286             return 1; //right trailed.
21287         }
21288         
21289         if (nodeIsBefore && !nodeIsAfter) {
21290             return 2;  // left trailed.
21291         }
21292         // fully contined.
21293         return 3;
21294     },
21295
21296     // private? - in a new class?
21297     cleanUpPaste :  function()
21298     {
21299         // cleans up the whole document..
21300         Roo.log('cleanuppaste');
21301         
21302         this.cleanUpChildren(this.doc.body);
21303         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21304         if (clean != this.doc.body.innerHTML) {
21305             this.doc.body.innerHTML = clean;
21306         }
21307         
21308     },
21309     
21310     cleanWordChars : function(input) {// change the chars to hex code
21311         var he = Roo.HtmlEditorCore;
21312         
21313         var output = input;
21314         Roo.each(he.swapCodes, function(sw) { 
21315             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21316             
21317             output = output.replace(swapper, sw[1]);
21318         });
21319         
21320         return output;
21321     },
21322     
21323     
21324     cleanUpChildren : function (n)
21325     {
21326         if (!n.childNodes.length) {
21327             return;
21328         }
21329         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21330            this.cleanUpChild(n.childNodes[i]);
21331         }
21332     },
21333     
21334     
21335         
21336     
21337     cleanUpChild : function (node)
21338     {
21339         var ed = this;
21340         //console.log(node);
21341         if (node.nodeName == "#text") {
21342             // clean up silly Windows -- stuff?
21343             return; 
21344         }
21345         if (node.nodeName == "#comment") {
21346             node.parentNode.removeChild(node);
21347             // clean up silly Windows -- stuff?
21348             return; 
21349         }
21350         var lcname = node.tagName.toLowerCase();
21351         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21352         // whitelist of tags..
21353         
21354         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21355             // remove node.
21356             node.parentNode.removeChild(node);
21357             return;
21358             
21359         }
21360         
21361         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21362         
21363         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21364         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21365         
21366         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21367         //    remove_keep_children = true;
21368         //}
21369         
21370         if (remove_keep_children) {
21371             this.cleanUpChildren(node);
21372             // inserts everything just before this node...
21373             while (node.childNodes.length) {
21374                 var cn = node.childNodes[0];
21375                 node.removeChild(cn);
21376                 node.parentNode.insertBefore(cn, node);
21377             }
21378             node.parentNode.removeChild(node);
21379             return;
21380         }
21381         
21382         if (!node.attributes || !node.attributes.length) {
21383             this.cleanUpChildren(node);
21384             return;
21385         }
21386         
21387         function cleanAttr(n,v)
21388         {
21389             
21390             if (v.match(/^\./) || v.match(/^\//)) {
21391                 return;
21392             }
21393             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21394                 return;
21395             }
21396             if (v.match(/^#/)) {
21397                 return;
21398             }
21399 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21400             node.removeAttribute(n);
21401             
21402         }
21403         
21404         var cwhite = this.cwhite;
21405         var cblack = this.cblack;
21406             
21407         function cleanStyle(n,v)
21408         {
21409             if (v.match(/expression/)) { //XSS?? should we even bother..
21410                 node.removeAttribute(n);
21411                 return;
21412             }
21413             
21414             var parts = v.split(/;/);
21415             var clean = [];
21416             
21417             Roo.each(parts, function(p) {
21418                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21419                 if (!p.length) {
21420                     return true;
21421                 }
21422                 var l = p.split(':').shift().replace(/\s+/g,'');
21423                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21424                 
21425                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21426 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21427                     //node.removeAttribute(n);
21428                     return true;
21429                 }
21430                 //Roo.log()
21431                 // only allow 'c whitelisted system attributes'
21432                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21433 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21434                     //node.removeAttribute(n);
21435                     return true;
21436                 }
21437                 
21438                 
21439                  
21440                 
21441                 clean.push(p);
21442                 return true;
21443             });
21444             if (clean.length) { 
21445                 node.setAttribute(n, clean.join(';'));
21446             } else {
21447                 node.removeAttribute(n);
21448             }
21449             
21450         }
21451         
21452         
21453         for (var i = node.attributes.length-1; i > -1 ; i--) {
21454             var a = node.attributes[i];
21455             //console.log(a);
21456             
21457             if (a.name.toLowerCase().substr(0,2)=='on')  {
21458                 node.removeAttribute(a.name);
21459                 continue;
21460             }
21461             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21462                 node.removeAttribute(a.name);
21463                 continue;
21464             }
21465             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21466                 cleanAttr(a.name,a.value); // fixme..
21467                 continue;
21468             }
21469             if (a.name == 'style') {
21470                 cleanStyle(a.name,a.value);
21471                 continue;
21472             }
21473             /// clean up MS crap..
21474             // tecnically this should be a list of valid class'es..
21475             
21476             
21477             if (a.name == 'class') {
21478                 if (a.value.match(/^Mso/)) {
21479                     node.className = '';
21480                 }
21481                 
21482                 if (a.value.match(/^body$/)) {
21483                     node.className = '';
21484                 }
21485                 continue;
21486             }
21487             
21488             // style cleanup!?
21489             // class cleanup?
21490             
21491         }
21492         
21493         
21494         this.cleanUpChildren(node);
21495         
21496         
21497     },
21498     
21499     /**
21500      * Clean up MS wordisms...
21501      */
21502     cleanWord : function(node)
21503     {
21504         
21505         
21506         if (!node) {
21507             this.cleanWord(this.doc.body);
21508             return;
21509         }
21510         if (node.nodeName == "#text") {
21511             // clean up silly Windows -- stuff?
21512             return; 
21513         }
21514         if (node.nodeName == "#comment") {
21515             node.parentNode.removeChild(node);
21516             // clean up silly Windows -- stuff?
21517             return; 
21518         }
21519         
21520         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21521             node.parentNode.removeChild(node);
21522             return;
21523         }
21524         
21525         // remove - but keep children..
21526         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21527             while (node.childNodes.length) {
21528                 var cn = node.childNodes[0];
21529                 node.removeChild(cn);
21530                 node.parentNode.insertBefore(cn, node);
21531             }
21532             node.parentNode.removeChild(node);
21533             this.iterateChildren(node, this.cleanWord);
21534             return;
21535         }
21536         // clean styles
21537         if (node.className.length) {
21538             
21539             var cn = node.className.split(/\W+/);
21540             var cna = [];
21541             Roo.each(cn, function(cls) {
21542                 if (cls.match(/Mso[a-zA-Z]+/)) {
21543                     return;
21544                 }
21545                 cna.push(cls);
21546             });
21547             node.className = cna.length ? cna.join(' ') : '';
21548             if (!cna.length) {
21549                 node.removeAttribute("class");
21550             }
21551         }
21552         
21553         if (node.hasAttribute("lang")) {
21554             node.removeAttribute("lang");
21555         }
21556         
21557         if (node.hasAttribute("style")) {
21558             
21559             var styles = node.getAttribute("style").split(";");
21560             var nstyle = [];
21561             Roo.each(styles, function(s) {
21562                 if (!s.match(/:/)) {
21563                     return;
21564                 }
21565                 var kv = s.split(":");
21566                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21567                     return;
21568                 }
21569                 // what ever is left... we allow.
21570                 nstyle.push(s);
21571             });
21572             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21573             if (!nstyle.length) {
21574                 node.removeAttribute('style');
21575             }
21576         }
21577         this.iterateChildren(node, this.cleanWord);
21578         
21579         
21580         
21581     },
21582     /**
21583      * iterateChildren of a Node, calling fn each time, using this as the scole..
21584      * @param {DomNode} node node to iterate children of.
21585      * @param {Function} fn method of this class to call on each item.
21586      */
21587     iterateChildren : function(node, fn)
21588     {
21589         if (!node.childNodes.length) {
21590                 return;
21591         }
21592         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21593            fn.call(this, node.childNodes[i])
21594         }
21595     },
21596     
21597     
21598     /**
21599      * cleanTableWidths.
21600      *
21601      * Quite often pasting from word etc.. results in tables with column and widths.
21602      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21603      *
21604      */
21605     cleanTableWidths : function(node)
21606     {
21607          
21608          
21609         if (!node) {
21610             this.cleanTableWidths(this.doc.body);
21611             return;
21612         }
21613         
21614         // ignore list...
21615         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21616             return; 
21617         }
21618         Roo.log(node.tagName);
21619         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21620             this.iterateChildren(node, this.cleanTableWidths);
21621             return;
21622         }
21623         if (node.hasAttribute('width')) {
21624             node.removeAttribute('width');
21625         }
21626         
21627          
21628         if (node.hasAttribute("style")) {
21629             // pretty basic...
21630             
21631             var styles = node.getAttribute("style").split(";");
21632             var nstyle = [];
21633             Roo.each(styles, function(s) {
21634                 if (!s.match(/:/)) {
21635                     return;
21636                 }
21637                 var kv = s.split(":");
21638                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21639                     return;
21640                 }
21641                 // what ever is left... we allow.
21642                 nstyle.push(s);
21643             });
21644             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21645             if (!nstyle.length) {
21646                 node.removeAttribute('style');
21647             }
21648         }
21649         
21650         this.iterateChildren(node, this.cleanTableWidths);
21651         
21652         
21653     },
21654     
21655     
21656     
21657     
21658     domToHTML : function(currentElement, depth, nopadtext) {
21659         
21660         depth = depth || 0;
21661         nopadtext = nopadtext || false;
21662     
21663         if (!currentElement) {
21664             return this.domToHTML(this.doc.body);
21665         }
21666         
21667         //Roo.log(currentElement);
21668         var j;
21669         var allText = false;
21670         var nodeName = currentElement.nodeName;
21671         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21672         
21673         if  (nodeName == '#text') {
21674             
21675             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21676         }
21677         
21678         
21679         var ret = '';
21680         if (nodeName != 'BODY') {
21681              
21682             var i = 0;
21683             // Prints the node tagName, such as <A>, <IMG>, etc
21684             if (tagName) {
21685                 var attr = [];
21686                 for(i = 0; i < currentElement.attributes.length;i++) {
21687                     // quoting?
21688                     var aname = currentElement.attributes.item(i).name;
21689                     if (!currentElement.attributes.item(i).value.length) {
21690                         continue;
21691                     }
21692                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21693                 }
21694                 
21695                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21696             } 
21697             else {
21698                 
21699                 // eack
21700             }
21701         } else {
21702             tagName = false;
21703         }
21704         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21705             return ret;
21706         }
21707         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21708             nopadtext = true;
21709         }
21710         
21711         
21712         // Traverse the tree
21713         i = 0;
21714         var currentElementChild = currentElement.childNodes.item(i);
21715         var allText = true;
21716         var innerHTML  = '';
21717         lastnode = '';
21718         while (currentElementChild) {
21719             // Formatting code (indent the tree so it looks nice on the screen)
21720             var nopad = nopadtext;
21721             if (lastnode == 'SPAN') {
21722                 nopad  = true;
21723             }
21724             // text
21725             if  (currentElementChild.nodeName == '#text') {
21726                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21727                 toadd = nopadtext ? toadd : toadd.trim();
21728                 if (!nopad && toadd.length > 80) {
21729                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21730                 }
21731                 innerHTML  += toadd;
21732                 
21733                 i++;
21734                 currentElementChild = currentElement.childNodes.item(i);
21735                 lastNode = '';
21736                 continue;
21737             }
21738             allText = false;
21739             
21740             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21741                 
21742             // Recursively traverse the tree structure of the child node
21743             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21744             lastnode = currentElementChild.nodeName;
21745             i++;
21746             currentElementChild=currentElement.childNodes.item(i);
21747         }
21748         
21749         ret += innerHTML;
21750         
21751         if (!allText) {
21752                 // The remaining code is mostly for formatting the tree
21753             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21754         }
21755         
21756         
21757         if (tagName) {
21758             ret+= "</"+tagName+">";
21759         }
21760         return ret;
21761         
21762     },
21763         
21764     applyBlacklists : function()
21765     {
21766         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21767         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21768         
21769         this.white = [];
21770         this.black = [];
21771         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21772             if (b.indexOf(tag) > -1) {
21773                 return;
21774             }
21775             this.white.push(tag);
21776             
21777         }, this);
21778         
21779         Roo.each(w, function(tag) {
21780             if (b.indexOf(tag) > -1) {
21781                 return;
21782             }
21783             if (this.white.indexOf(tag) > -1) {
21784                 return;
21785             }
21786             this.white.push(tag);
21787             
21788         }, this);
21789         
21790         
21791         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21792             if (w.indexOf(tag) > -1) {
21793                 return;
21794             }
21795             this.black.push(tag);
21796             
21797         }, this);
21798         
21799         Roo.each(b, function(tag) {
21800             if (w.indexOf(tag) > -1) {
21801                 return;
21802             }
21803             if (this.black.indexOf(tag) > -1) {
21804                 return;
21805             }
21806             this.black.push(tag);
21807             
21808         }, this);
21809         
21810         
21811         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21812         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21813         
21814         this.cwhite = [];
21815         this.cblack = [];
21816         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21817             if (b.indexOf(tag) > -1) {
21818                 return;
21819             }
21820             this.cwhite.push(tag);
21821             
21822         }, this);
21823         
21824         Roo.each(w, function(tag) {
21825             if (b.indexOf(tag) > -1) {
21826                 return;
21827             }
21828             if (this.cwhite.indexOf(tag) > -1) {
21829                 return;
21830             }
21831             this.cwhite.push(tag);
21832             
21833         }, this);
21834         
21835         
21836         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21837             if (w.indexOf(tag) > -1) {
21838                 return;
21839             }
21840             this.cblack.push(tag);
21841             
21842         }, this);
21843         
21844         Roo.each(b, function(tag) {
21845             if (w.indexOf(tag) > -1) {
21846                 return;
21847             }
21848             if (this.cblack.indexOf(tag) > -1) {
21849                 return;
21850             }
21851             this.cblack.push(tag);
21852             
21853         }, this);
21854     },
21855     
21856     setStylesheets : function(stylesheets)
21857     {
21858         if(typeof(stylesheets) == 'string'){
21859             Roo.get(this.iframe.contentDocument.head).createChild({
21860                 tag : 'link',
21861                 rel : 'stylesheet',
21862                 type : 'text/css',
21863                 href : stylesheets
21864             });
21865             
21866             return;
21867         }
21868         var _this = this;
21869      
21870         Roo.each(stylesheets, function(s) {
21871             if(!s.length){
21872                 return;
21873             }
21874             
21875             Roo.get(_this.iframe.contentDocument.head).createChild({
21876                 tag : 'link',
21877                 rel : 'stylesheet',
21878                 type : 'text/css',
21879                 href : s
21880             });
21881         });
21882
21883         
21884     },
21885     
21886     removeStylesheets : function()
21887     {
21888         var _this = this;
21889         
21890         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21891             s.remove();
21892         });
21893     }
21894     
21895     // hide stuff that is not compatible
21896     /**
21897      * @event blur
21898      * @hide
21899      */
21900     /**
21901      * @event change
21902      * @hide
21903      */
21904     /**
21905      * @event focus
21906      * @hide
21907      */
21908     /**
21909      * @event specialkey
21910      * @hide
21911      */
21912     /**
21913      * @cfg {String} fieldClass @hide
21914      */
21915     /**
21916      * @cfg {String} focusClass @hide
21917      */
21918     /**
21919      * @cfg {String} autoCreate @hide
21920      */
21921     /**
21922      * @cfg {String} inputType @hide
21923      */
21924     /**
21925      * @cfg {String} invalidClass @hide
21926      */
21927     /**
21928      * @cfg {String} invalidText @hide
21929      */
21930     /**
21931      * @cfg {String} msgFx @hide
21932      */
21933     /**
21934      * @cfg {String} validateOnBlur @hide
21935      */
21936 });
21937
21938 Roo.HtmlEditorCore.white = [
21939         'area', 'br', 'img', 'input', 'hr', 'wbr',
21940         
21941        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21942        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21943        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21944        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21945        'table',   'ul',         'xmp', 
21946        
21947        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21948       'thead',   'tr', 
21949      
21950       'dir', 'menu', 'ol', 'ul', 'dl',
21951        
21952       'embed',  'object'
21953 ];
21954
21955
21956 Roo.HtmlEditorCore.black = [
21957     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21958         'applet', // 
21959         'base',   'basefont', 'bgsound', 'blink',  'body', 
21960         'frame',  'frameset', 'head',    'html',   'ilayer', 
21961         'iframe', 'layer',  'link',     'meta',    'object',   
21962         'script', 'style' ,'title',  'xml' // clean later..
21963 ];
21964 Roo.HtmlEditorCore.clean = [
21965     'script', 'style', 'title', 'xml'
21966 ];
21967 Roo.HtmlEditorCore.remove = [
21968     'font'
21969 ];
21970 // attributes..
21971
21972 Roo.HtmlEditorCore.ablack = [
21973     'on'
21974 ];
21975     
21976 Roo.HtmlEditorCore.aclean = [ 
21977     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21978 ];
21979
21980 // protocols..
21981 Roo.HtmlEditorCore.pwhite= [
21982         'http',  'https',  'mailto'
21983 ];
21984
21985 // white listed style attributes.
21986 Roo.HtmlEditorCore.cwhite= [
21987       //  'text-align', /// default is to allow most things..
21988       
21989          
21990 //        'font-size'//??
21991 ];
21992
21993 // black listed style attributes.
21994 Roo.HtmlEditorCore.cblack= [
21995       //  'font-size' -- this can be set by the project 
21996 ];
21997
21998
21999 Roo.HtmlEditorCore.swapCodes   =[ 
22000     [    8211, "--" ], 
22001     [    8212, "--" ], 
22002     [    8216,  "'" ],  
22003     [    8217, "'" ],  
22004     [    8220, '"' ],  
22005     [    8221, '"' ],  
22006     [    8226, "*" ],  
22007     [    8230, "..." ]
22008 ]; 
22009
22010     //<script type="text/javascript">
22011
22012 /*
22013  * Ext JS Library 1.1.1
22014  * Copyright(c) 2006-2007, Ext JS, LLC.
22015  * Licence LGPL
22016  * 
22017  */
22018  
22019  
22020 Roo.form.HtmlEditor = function(config){
22021     
22022     
22023     
22024     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22025     
22026     if (!this.toolbars) {
22027         this.toolbars = [];
22028     }
22029     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22030     
22031     
22032 };
22033
22034 /**
22035  * @class Roo.form.HtmlEditor
22036  * @extends Roo.form.Field
22037  * Provides a lightweight HTML Editor component.
22038  *
22039  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22040  * 
22041  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22042  * supported by this editor.</b><br/><br/>
22043  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22044  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22045  */
22046 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22047     /**
22048      * @cfg {Boolean} clearUp
22049      */
22050     clearUp : true,
22051       /**
22052      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22053      */
22054     toolbars : false,
22055    
22056      /**
22057      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22058      *                        Roo.resizable.
22059      */
22060     resizable : false,
22061      /**
22062      * @cfg {Number} height (in pixels)
22063      */   
22064     height: 300,
22065    /**
22066      * @cfg {Number} width (in pixels)
22067      */   
22068     width: 500,
22069     
22070     /**
22071      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22072      * 
22073      */
22074     stylesheets: false,
22075     
22076     
22077      /**
22078      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22079      * 
22080      */
22081     cblack: false,
22082     /**
22083      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22084      * 
22085      */
22086     cwhite: false,
22087     
22088      /**
22089      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22090      * 
22091      */
22092     black: false,
22093     /**
22094      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22095      * 
22096      */
22097     white: false,
22098     
22099     // id of frame..
22100     frameId: false,
22101     
22102     // private properties
22103     validationEvent : false,
22104     deferHeight: true,
22105     initialized : false,
22106     activated : false,
22107     
22108     onFocus : Roo.emptyFn,
22109     iframePad:3,
22110     hideMode:'offsets',
22111     
22112     actionMode : 'container', // defaults to hiding it...
22113     
22114     defaultAutoCreate : { // modified by initCompnoent..
22115         tag: "textarea",
22116         style:"width:500px;height:300px;",
22117         autocomplete: "new-password"
22118     },
22119
22120     // private
22121     initComponent : function(){
22122         this.addEvents({
22123             /**
22124              * @event initialize
22125              * Fires when the editor is fully initialized (including the iframe)
22126              * @param {HtmlEditor} this
22127              */
22128             initialize: true,
22129             /**
22130              * @event activate
22131              * Fires when the editor is first receives the focus. Any insertion must wait
22132              * until after this event.
22133              * @param {HtmlEditor} this
22134              */
22135             activate: true,
22136              /**
22137              * @event beforesync
22138              * Fires before the textarea is updated with content from the editor iframe. Return false
22139              * to cancel the sync.
22140              * @param {HtmlEditor} this
22141              * @param {String} html
22142              */
22143             beforesync: true,
22144              /**
22145              * @event beforepush
22146              * Fires before the iframe editor is updated with content from the textarea. Return false
22147              * to cancel the push.
22148              * @param {HtmlEditor} this
22149              * @param {String} html
22150              */
22151             beforepush: true,
22152              /**
22153              * @event sync
22154              * Fires when the textarea is updated with content from the editor iframe.
22155              * @param {HtmlEditor} this
22156              * @param {String} html
22157              */
22158             sync: true,
22159              /**
22160              * @event push
22161              * Fires when the iframe editor is updated with content from the textarea.
22162              * @param {HtmlEditor} this
22163              * @param {String} html
22164              */
22165             push: true,
22166              /**
22167              * @event editmodechange
22168              * Fires when the editor switches edit modes
22169              * @param {HtmlEditor} this
22170              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22171              */
22172             editmodechange: true,
22173             /**
22174              * @event editorevent
22175              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22176              * @param {HtmlEditor} this
22177              */
22178             editorevent: true,
22179             /**
22180              * @event firstfocus
22181              * Fires when on first focus - needed by toolbars..
22182              * @param {HtmlEditor} this
22183              */
22184             firstfocus: true,
22185             /**
22186              * @event autosave
22187              * Auto save the htmlEditor value as a file into Events
22188              * @param {HtmlEditor} this
22189              */
22190             autosave: true,
22191             /**
22192              * @event savedpreview
22193              * preview the saved version of htmlEditor
22194              * @param {HtmlEditor} this
22195              */
22196             savedpreview: true,
22197             
22198             /**
22199             * @event stylesheetsclick
22200             * Fires when press the Sytlesheets button
22201             * @param {Roo.HtmlEditorCore} this
22202             */
22203             stylesheetsclick: true
22204         });
22205         this.defaultAutoCreate =  {
22206             tag: "textarea",
22207             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22208             autocomplete: "new-password"
22209         };
22210     },
22211
22212     /**
22213      * Protected method that will not generally be called directly. It
22214      * is called when the editor creates its toolbar. Override this method if you need to
22215      * add custom toolbar buttons.
22216      * @param {HtmlEditor} editor
22217      */
22218     createToolbar : function(editor){
22219         Roo.log("create toolbars");
22220         if (!editor.toolbars || !editor.toolbars.length) {
22221             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22222         }
22223         
22224         for (var i =0 ; i < editor.toolbars.length;i++) {
22225             editor.toolbars[i] = Roo.factory(
22226                     typeof(editor.toolbars[i]) == 'string' ?
22227                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22228                 Roo.form.HtmlEditor);
22229             editor.toolbars[i].init(editor);
22230         }
22231          
22232         
22233     },
22234
22235      
22236     // private
22237     onRender : function(ct, position)
22238     {
22239         var _t = this;
22240         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22241         
22242         this.wrap = this.el.wrap({
22243             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22244         });
22245         
22246         this.editorcore.onRender(ct, position);
22247          
22248         if (this.resizable) {
22249             this.resizeEl = new Roo.Resizable(this.wrap, {
22250                 pinned : true,
22251                 wrap: true,
22252                 dynamic : true,
22253                 minHeight : this.height,
22254                 height: this.height,
22255                 handles : this.resizable,
22256                 width: this.width,
22257                 listeners : {
22258                     resize : function(r, w, h) {
22259                         _t.onResize(w,h); // -something
22260                     }
22261                 }
22262             });
22263             
22264         }
22265         this.createToolbar(this);
22266        
22267         
22268         if(!this.width){
22269             this.setSize(this.wrap.getSize());
22270         }
22271         if (this.resizeEl) {
22272             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22273             // should trigger onReize..
22274         }
22275         
22276         this.keyNav = new Roo.KeyNav(this.el, {
22277             
22278             "tab" : function(e){
22279                 e.preventDefault();
22280                 
22281                 var value = this.getValue();
22282                 
22283                 var start = this.el.dom.selectionStart;
22284                 var end = this.el.dom.selectionEnd;
22285                 
22286                 if(!e.shiftKey){
22287                     
22288                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22289                     this.el.dom.setSelectionRange(end + 1, end + 1);
22290                     return;
22291                 }
22292                 
22293                 var f = value.substring(0, start).split("\t");
22294                 
22295                 if(f.pop().length != 0){
22296                     return;
22297                 }
22298                 
22299                 this.setValue(f.join("\t") + value.substring(end));
22300                 this.el.dom.setSelectionRange(start - 1, start - 1);
22301                 
22302             },
22303             
22304             "home" : function(e){
22305                 e.preventDefault();
22306                 
22307                 var curr = this.el.dom.selectionStart;
22308                 var lines = this.getValue().split("\n");
22309                 
22310                 if(!lines.length){
22311                     return;
22312                 }
22313                 
22314                 if(e.ctrlKey){
22315                     this.el.dom.setSelectionRange(0, 0);
22316                     return;
22317                 }
22318                 
22319                 var pos = 0;
22320                 
22321                 for (var i = 0; i < lines.length;i++) {
22322                     pos += lines[i].length;
22323                     
22324                     if(i != 0){
22325                         pos += 1;
22326                     }
22327                     
22328                     if(pos < curr){
22329                         continue;
22330                     }
22331                     
22332                     pos -= lines[i].length;
22333                     
22334                     break;
22335                 }
22336                 
22337                 if(!e.shiftKey){
22338                     this.el.dom.setSelectionRange(pos, pos);
22339                     return;
22340                 }
22341                 
22342                 this.el.dom.selectionStart = pos;
22343                 this.el.dom.selectionEnd = curr;
22344             },
22345             
22346             "end" : function(e){
22347                 e.preventDefault();
22348                 
22349                 var curr = this.el.dom.selectionStart;
22350                 var lines = this.getValue().split("\n");
22351                 
22352                 if(!lines.length){
22353                     return;
22354                 }
22355                 
22356                 if(e.ctrlKey){
22357                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22358                     return;
22359                 }
22360                 
22361                 var pos = 0;
22362                 
22363                 for (var i = 0; i < lines.length;i++) {
22364                     
22365                     pos += lines[i].length;
22366                     
22367                     if(i != 0){
22368                         pos += 1;
22369                     }
22370                     
22371                     if(pos < curr){
22372                         continue;
22373                     }
22374                     
22375                     break;
22376                 }
22377                 
22378                 if(!e.shiftKey){
22379                     this.el.dom.setSelectionRange(pos, pos);
22380                     return;
22381                 }
22382                 
22383                 this.el.dom.selectionStart = curr;
22384                 this.el.dom.selectionEnd = pos;
22385             },
22386
22387             scope : this,
22388
22389             doRelay : function(foo, bar, hname){
22390                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22391             },
22392
22393             forceKeyDown: true
22394         });
22395         
22396 //        if(this.autosave && this.w){
22397 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22398 //        }
22399     },
22400
22401     // private
22402     onResize : function(w, h)
22403     {
22404         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22405         var ew = false;
22406         var eh = false;
22407         
22408         if(this.el ){
22409             if(typeof w == 'number'){
22410                 var aw = w - this.wrap.getFrameWidth('lr');
22411                 this.el.setWidth(this.adjustWidth('textarea', aw));
22412                 ew = aw;
22413             }
22414             if(typeof h == 'number'){
22415                 var tbh = 0;
22416                 for (var i =0; i < this.toolbars.length;i++) {
22417                     // fixme - ask toolbars for heights?
22418                     tbh += this.toolbars[i].tb.el.getHeight();
22419                     if (this.toolbars[i].footer) {
22420                         tbh += this.toolbars[i].footer.el.getHeight();
22421                     }
22422                 }
22423                 
22424                 
22425                 
22426                 
22427                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22428                 ah -= 5; // knock a few pixes off for look..
22429 //                Roo.log(ah);
22430                 this.el.setHeight(this.adjustWidth('textarea', ah));
22431                 var eh = ah;
22432             }
22433         }
22434         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22435         this.editorcore.onResize(ew,eh);
22436         
22437     },
22438
22439     /**
22440      * Toggles the editor between standard and source edit mode.
22441      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22442      */
22443     toggleSourceEdit : function(sourceEditMode)
22444     {
22445         this.editorcore.toggleSourceEdit(sourceEditMode);
22446         
22447         if(this.editorcore.sourceEditMode){
22448             Roo.log('editor - showing textarea');
22449             
22450 //            Roo.log('in');
22451 //            Roo.log(this.syncValue());
22452             this.editorcore.syncValue();
22453             this.el.removeClass('x-hidden');
22454             this.el.dom.removeAttribute('tabIndex');
22455             this.el.focus();
22456             
22457             for (var i = 0; i < this.toolbars.length; i++) {
22458                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22459                     this.toolbars[i].tb.hide();
22460                     this.toolbars[i].footer.hide();
22461                 }
22462             }
22463             
22464         }else{
22465             Roo.log('editor - hiding textarea');
22466 //            Roo.log('out')
22467 //            Roo.log(this.pushValue()); 
22468             this.editorcore.pushValue();
22469             
22470             this.el.addClass('x-hidden');
22471             this.el.dom.setAttribute('tabIndex', -1);
22472             
22473             for (var i = 0; i < this.toolbars.length; i++) {
22474                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22475                     this.toolbars[i].tb.show();
22476                     this.toolbars[i].footer.show();
22477                 }
22478             }
22479             
22480             //this.deferFocus();
22481         }
22482         
22483         this.setSize(this.wrap.getSize());
22484         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22485         
22486         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22487     },
22488  
22489     // private (for BoxComponent)
22490     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22491
22492     // private (for BoxComponent)
22493     getResizeEl : function(){
22494         return this.wrap;
22495     },
22496
22497     // private (for BoxComponent)
22498     getPositionEl : function(){
22499         return this.wrap;
22500     },
22501
22502     // private
22503     initEvents : function(){
22504         this.originalValue = this.getValue();
22505     },
22506
22507     /**
22508      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22509      * @method
22510      */
22511     markInvalid : Roo.emptyFn,
22512     /**
22513      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22514      * @method
22515      */
22516     clearInvalid : Roo.emptyFn,
22517
22518     setValue : function(v){
22519         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22520         this.editorcore.pushValue();
22521     },
22522
22523      
22524     // private
22525     deferFocus : function(){
22526         this.focus.defer(10, this);
22527     },
22528
22529     // doc'ed in Field
22530     focus : function(){
22531         this.editorcore.focus();
22532         
22533     },
22534       
22535
22536     // private
22537     onDestroy : function(){
22538         
22539         
22540         
22541         if(this.rendered){
22542             
22543             for (var i =0; i < this.toolbars.length;i++) {
22544                 // fixme - ask toolbars for heights?
22545                 this.toolbars[i].onDestroy();
22546             }
22547             
22548             this.wrap.dom.innerHTML = '';
22549             this.wrap.remove();
22550         }
22551     },
22552
22553     // private
22554     onFirstFocus : function(){
22555         //Roo.log("onFirstFocus");
22556         this.editorcore.onFirstFocus();
22557          for (var i =0; i < this.toolbars.length;i++) {
22558             this.toolbars[i].onFirstFocus();
22559         }
22560         
22561     },
22562     
22563     // private
22564     syncValue : function()
22565     {
22566         this.editorcore.syncValue();
22567     },
22568     
22569     pushValue : function()
22570     {
22571         this.editorcore.pushValue();
22572     },
22573     
22574     setStylesheets : function(stylesheets)
22575     {
22576         this.editorcore.setStylesheets(stylesheets);
22577     },
22578     
22579     removeStylesheets : function()
22580     {
22581         this.editorcore.removeStylesheets();
22582     }
22583      
22584     
22585     // hide stuff that is not compatible
22586     /**
22587      * @event blur
22588      * @hide
22589      */
22590     /**
22591      * @event change
22592      * @hide
22593      */
22594     /**
22595      * @event focus
22596      * @hide
22597      */
22598     /**
22599      * @event specialkey
22600      * @hide
22601      */
22602     /**
22603      * @cfg {String} fieldClass @hide
22604      */
22605     /**
22606      * @cfg {String} focusClass @hide
22607      */
22608     /**
22609      * @cfg {String} autoCreate @hide
22610      */
22611     /**
22612      * @cfg {String} inputType @hide
22613      */
22614     /**
22615      * @cfg {String} invalidClass @hide
22616      */
22617     /**
22618      * @cfg {String} invalidText @hide
22619      */
22620     /**
22621      * @cfg {String} msgFx @hide
22622      */
22623     /**
22624      * @cfg {String} validateOnBlur @hide
22625      */
22626 });
22627  
22628     // <script type="text/javascript">
22629 /*
22630  * Based on
22631  * Ext JS Library 1.1.1
22632  * Copyright(c) 2006-2007, Ext JS, LLC.
22633  *  
22634  
22635  */
22636
22637 /**
22638  * @class Roo.form.HtmlEditorToolbar1
22639  * Basic Toolbar
22640  * 
22641  * Usage:
22642  *
22643  new Roo.form.HtmlEditor({
22644     ....
22645     toolbars : [
22646         new Roo.form.HtmlEditorToolbar1({
22647             disable : { fonts: 1 , format: 1, ..., ... , ...],
22648             btns : [ .... ]
22649         })
22650     }
22651      
22652  * 
22653  * @cfg {Object} disable List of elements to disable..
22654  * @cfg {Array} btns List of additional buttons.
22655  * 
22656  * 
22657  * NEEDS Extra CSS? 
22658  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22659  */
22660  
22661 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22662 {
22663     
22664     Roo.apply(this, config);
22665     
22666     // default disabled, based on 'good practice'..
22667     this.disable = this.disable || {};
22668     Roo.applyIf(this.disable, {
22669         fontSize : true,
22670         colors : true,
22671         specialElements : true
22672     });
22673     
22674     
22675     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22676     // dont call parent... till later.
22677 }
22678
22679 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22680     
22681     tb: false,
22682     
22683     rendered: false,
22684     
22685     editor : false,
22686     editorcore : false,
22687     /**
22688      * @cfg {Object} disable  List of toolbar elements to disable
22689          
22690      */
22691     disable : false,
22692     
22693     
22694      /**
22695      * @cfg {String} createLinkText The default text for the create link prompt
22696      */
22697     createLinkText : 'Please enter the URL for the link:',
22698     /**
22699      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22700      */
22701     defaultLinkValue : 'http:/'+'/',
22702    
22703     
22704       /**
22705      * @cfg {Array} fontFamilies An array of available font families
22706      */
22707     fontFamilies : [
22708         'Arial',
22709         'Courier New',
22710         'Tahoma',
22711         'Times New Roman',
22712         'Verdana'
22713     ],
22714     
22715     specialChars : [
22716            "&#169;",
22717           "&#174;",     
22718           "&#8482;",    
22719           "&#163;" ,    
22720          // "&#8212;",    
22721           "&#8230;",    
22722           "&#247;" ,    
22723         //  "&#225;" ,     ?? a acute?
22724            "&#8364;"    , //Euro
22725        //   "&#8220;"    ,
22726         //  "&#8221;"    ,
22727         //  "&#8226;"    ,
22728           "&#176;"  //   , // degrees
22729
22730          // "&#233;"     , // e ecute
22731          // "&#250;"     , // u ecute?
22732     ],
22733     
22734     specialElements : [
22735         {
22736             text: "Insert Table",
22737             xtype: 'MenuItem',
22738             xns : Roo.Menu,
22739             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22740                 
22741         },
22742         {    
22743             text: "Insert Image",
22744             xtype: 'MenuItem',
22745             xns : Roo.Menu,
22746             ihtml : '<img src="about:blank"/>'
22747             
22748         }
22749         
22750          
22751     ],
22752     
22753     
22754     inputElements : [ 
22755             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22756             "input:submit", "input:button", "select", "textarea", "label" ],
22757     formats : [
22758         ["p"] ,  
22759         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22760         ["pre"],[ "code"], 
22761         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22762         ['div'],['span']
22763     ],
22764     
22765     cleanStyles : [
22766         "font-size"
22767     ],
22768      /**
22769      * @cfg {String} defaultFont default font to use.
22770      */
22771     defaultFont: 'tahoma',
22772    
22773     fontSelect : false,
22774     
22775     
22776     formatCombo : false,
22777     
22778     init : function(editor)
22779     {
22780         this.editor = editor;
22781         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22782         var editorcore = this.editorcore;
22783         
22784         var _t = this;
22785         
22786         var fid = editorcore.frameId;
22787         var etb = this;
22788         function btn(id, toggle, handler){
22789             var xid = fid + '-'+ id ;
22790             return {
22791                 id : xid,
22792                 cmd : id,
22793                 cls : 'x-btn-icon x-edit-'+id,
22794                 enableToggle:toggle !== false,
22795                 scope: _t, // was editor...
22796                 handler:handler||_t.relayBtnCmd,
22797                 clickEvent:'mousedown',
22798                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22799                 tabIndex:-1
22800             };
22801         }
22802         
22803         
22804         
22805         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22806         this.tb = tb;
22807          // stop form submits
22808         tb.el.on('click', function(e){
22809             e.preventDefault(); // what does this do?
22810         });
22811
22812         if(!this.disable.font) { // && !Roo.isSafari){
22813             /* why no safari for fonts 
22814             editor.fontSelect = tb.el.createChild({
22815                 tag:'select',
22816                 tabIndex: -1,
22817                 cls:'x-font-select',
22818                 html: this.createFontOptions()
22819             });
22820             
22821             editor.fontSelect.on('change', function(){
22822                 var font = editor.fontSelect.dom.value;
22823                 editor.relayCmd('fontname', font);
22824                 editor.deferFocus();
22825             }, editor);
22826             
22827             tb.add(
22828                 editor.fontSelect.dom,
22829                 '-'
22830             );
22831             */
22832             
22833         };
22834         if(!this.disable.formats){
22835             this.formatCombo = new Roo.form.ComboBox({
22836                 store: new Roo.data.SimpleStore({
22837                     id : 'tag',
22838                     fields: ['tag'],
22839                     data : this.formats // from states.js
22840                 }),
22841                 blockFocus : true,
22842                 name : '',
22843                 //autoCreate : {tag: "div",  size: "20"},
22844                 displayField:'tag',
22845                 typeAhead: false,
22846                 mode: 'local',
22847                 editable : false,
22848                 triggerAction: 'all',
22849                 emptyText:'Add tag',
22850                 selectOnFocus:true,
22851                 width:135,
22852                 listeners : {
22853                     'select': function(c, r, i) {
22854                         editorcore.insertTag(r.get('tag'));
22855                         editor.focus();
22856                     }
22857                 }
22858
22859             });
22860             tb.addField(this.formatCombo);
22861             
22862         }
22863         
22864         if(!this.disable.format){
22865             tb.add(
22866                 btn('bold'),
22867                 btn('italic'),
22868                 btn('underline'),
22869                 btn('strikethrough')
22870             );
22871         };
22872         if(!this.disable.fontSize){
22873             tb.add(
22874                 '-',
22875                 
22876                 
22877                 btn('increasefontsize', false, editorcore.adjustFont),
22878                 btn('decreasefontsize', false, editorcore.adjustFont)
22879             );
22880         };
22881         
22882         
22883         if(!this.disable.colors){
22884             tb.add(
22885                 '-', {
22886                     id:editorcore.frameId +'-forecolor',
22887                     cls:'x-btn-icon x-edit-forecolor',
22888                     clickEvent:'mousedown',
22889                     tooltip: this.buttonTips['forecolor'] || undefined,
22890                     tabIndex:-1,
22891                     menu : new Roo.menu.ColorMenu({
22892                         allowReselect: true,
22893                         focus: Roo.emptyFn,
22894                         value:'000000',
22895                         plain:true,
22896                         selectHandler: function(cp, color){
22897                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22898                             editor.deferFocus();
22899                         },
22900                         scope: editorcore,
22901                         clickEvent:'mousedown'
22902                     })
22903                 }, {
22904                     id:editorcore.frameId +'backcolor',
22905                     cls:'x-btn-icon x-edit-backcolor',
22906                     clickEvent:'mousedown',
22907                     tooltip: this.buttonTips['backcolor'] || undefined,
22908                     tabIndex:-1,
22909                     menu : new Roo.menu.ColorMenu({
22910                         focus: Roo.emptyFn,
22911                         value:'FFFFFF',
22912                         plain:true,
22913                         allowReselect: true,
22914                         selectHandler: function(cp, color){
22915                             if(Roo.isGecko){
22916                                 editorcore.execCmd('useCSS', false);
22917                                 editorcore.execCmd('hilitecolor', color);
22918                                 editorcore.execCmd('useCSS', true);
22919                                 editor.deferFocus();
22920                             }else{
22921                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22922                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22923                                 editor.deferFocus();
22924                             }
22925                         },
22926                         scope:editorcore,
22927                         clickEvent:'mousedown'
22928                     })
22929                 }
22930             );
22931         };
22932         // now add all the items...
22933         
22934
22935         if(!this.disable.alignments){
22936             tb.add(
22937                 '-',
22938                 btn('justifyleft'),
22939                 btn('justifycenter'),
22940                 btn('justifyright')
22941             );
22942         };
22943
22944         //if(!Roo.isSafari){
22945             if(!this.disable.links){
22946                 tb.add(
22947                     '-',
22948                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22949                 );
22950             };
22951
22952             if(!this.disable.lists){
22953                 tb.add(
22954                     '-',
22955                     btn('insertorderedlist'),
22956                     btn('insertunorderedlist')
22957                 );
22958             }
22959             if(!this.disable.sourceEdit){
22960                 tb.add(
22961                     '-',
22962                     btn('sourceedit', true, function(btn){
22963                         this.toggleSourceEdit(btn.pressed);
22964                     })
22965                 );
22966             }
22967         //}
22968         
22969         var smenu = { };
22970         // special menu.. - needs to be tidied up..
22971         if (!this.disable.special) {
22972             smenu = {
22973                 text: "&#169;",
22974                 cls: 'x-edit-none',
22975                 
22976                 menu : {
22977                     items : []
22978                 }
22979             };
22980             for (var i =0; i < this.specialChars.length; i++) {
22981                 smenu.menu.items.push({
22982                     
22983                     html: this.specialChars[i],
22984                     handler: function(a,b) {
22985                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
22986                         //editor.insertAtCursor(a.html);
22987                         
22988                     },
22989                     tabIndex:-1
22990                 });
22991             }
22992             
22993             
22994             tb.add(smenu);
22995             
22996             
22997         }
22998         
22999         var cmenu = { };
23000         if (!this.disable.cleanStyles) {
23001             cmenu = {
23002                 cls: 'x-btn-icon x-btn-clear',
23003                 
23004                 menu : {
23005                     items : []
23006                 }
23007             };
23008             for (var i =0; i < this.cleanStyles.length; i++) {
23009                 cmenu.menu.items.push({
23010                     actiontype : this.cleanStyles[i],
23011                     html: 'Remove ' + this.cleanStyles[i],
23012                     handler: function(a,b) {
23013 //                        Roo.log(a);
23014 //                        Roo.log(b);
23015                         var c = Roo.get(editorcore.doc.body);
23016                         c.select('[style]').each(function(s) {
23017                             s.dom.style.removeProperty(a.actiontype);
23018                         });
23019                         editorcore.syncValue();
23020                     },
23021                     tabIndex:-1
23022                 });
23023             }
23024              cmenu.menu.items.push({
23025                 actiontype : 'tablewidths',
23026                 html: 'Remove Table Widths',
23027                 handler: function(a,b) {
23028                     editorcore.cleanTableWidths();
23029                     editorcore.syncValue();
23030                 },
23031                 tabIndex:-1
23032             });
23033             cmenu.menu.items.push({
23034                 actiontype : 'word',
23035                 html: 'Remove MS Word Formating',
23036                 handler: function(a,b) {
23037                     editorcore.cleanWord();
23038                     editorcore.syncValue();
23039                 },
23040                 tabIndex:-1
23041             });
23042             
23043             cmenu.menu.items.push({
23044                 actiontype : 'all',
23045                 html: 'Remove All Styles',
23046                 handler: function(a,b) {
23047                     
23048                     var c = Roo.get(editorcore.doc.body);
23049                     c.select('[style]').each(function(s) {
23050                         s.dom.removeAttribute('style');
23051                     });
23052                     editorcore.syncValue();
23053                 },
23054                 tabIndex:-1
23055             });
23056             
23057             cmenu.menu.items.push({
23058                 actiontype : 'all',
23059                 html: 'Remove All CSS Classes',
23060                 handler: function(a,b) {
23061                     
23062                     var c = Roo.get(editorcore.doc.body);
23063                     c.select('[class]').each(function(s) {
23064                         s.dom.className = '';
23065                     });
23066                     editorcore.syncValue();
23067                 },
23068                 tabIndex:-1
23069             });
23070             
23071              cmenu.menu.items.push({
23072                 actiontype : 'tidy',
23073                 html: 'Tidy HTML Source',
23074                 handler: function(a,b) {
23075                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23076                     editorcore.syncValue();
23077                 },
23078                 tabIndex:-1
23079             });
23080             
23081             
23082             tb.add(cmenu);
23083         }
23084          
23085         if (!this.disable.specialElements) {
23086             var semenu = {
23087                 text: "Other;",
23088                 cls: 'x-edit-none',
23089                 menu : {
23090                     items : []
23091                 }
23092             };
23093             for (var i =0; i < this.specialElements.length; i++) {
23094                 semenu.menu.items.push(
23095                     Roo.apply({ 
23096                         handler: function(a,b) {
23097                             editor.insertAtCursor(this.ihtml);
23098                         }
23099                     }, this.specialElements[i])
23100                 );
23101                     
23102             }
23103             
23104             tb.add(semenu);
23105             
23106             
23107         }
23108          
23109         
23110         if (this.btns) {
23111             for(var i =0; i< this.btns.length;i++) {
23112                 var b = Roo.factory(this.btns[i],Roo.form);
23113                 b.cls =  'x-edit-none';
23114                 
23115                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23116                     b.cls += ' x-init-enable';
23117                 }
23118                 
23119                 b.scope = editorcore;
23120                 tb.add(b);
23121             }
23122         
23123         }
23124         
23125         
23126         
23127         // disable everything...
23128         
23129         this.tb.items.each(function(item){
23130             
23131            if(
23132                 item.id != editorcore.frameId+ '-sourceedit' && 
23133                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23134             ){
23135                 
23136                 item.disable();
23137             }
23138         });
23139         this.rendered = true;
23140         
23141         // the all the btns;
23142         editor.on('editorevent', this.updateToolbar, this);
23143         // other toolbars need to implement this..
23144         //editor.on('editmodechange', this.updateToolbar, this);
23145     },
23146     
23147     
23148     relayBtnCmd : function(btn) {
23149         this.editorcore.relayCmd(btn.cmd);
23150     },
23151     // private used internally
23152     createLink : function(){
23153         Roo.log("create link?");
23154         var url = prompt(this.createLinkText, this.defaultLinkValue);
23155         if(url && url != 'http:/'+'/'){
23156             this.editorcore.relayCmd('createlink', url);
23157         }
23158     },
23159
23160     
23161     /**
23162      * Protected method that will not generally be called directly. It triggers
23163      * a toolbar update by reading the markup state of the current selection in the editor.
23164      */
23165     updateToolbar: function(){
23166
23167         if(!this.editorcore.activated){
23168             this.editor.onFirstFocus();
23169             return;
23170         }
23171
23172         var btns = this.tb.items.map, 
23173             doc = this.editorcore.doc,
23174             frameId = this.editorcore.frameId;
23175
23176         if(!this.disable.font && !Roo.isSafari){
23177             /*
23178             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23179             if(name != this.fontSelect.dom.value){
23180                 this.fontSelect.dom.value = name;
23181             }
23182             */
23183         }
23184         if(!this.disable.format){
23185             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23186             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23187             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23188             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23189         }
23190         if(!this.disable.alignments){
23191             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23192             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23193             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23194         }
23195         if(!Roo.isSafari && !this.disable.lists){
23196             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23197             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23198         }
23199         
23200         var ans = this.editorcore.getAllAncestors();
23201         if (this.formatCombo) {
23202             
23203             
23204             var store = this.formatCombo.store;
23205             this.formatCombo.setValue("");
23206             for (var i =0; i < ans.length;i++) {
23207                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23208                     // select it..
23209                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23210                     break;
23211                 }
23212             }
23213         }
23214         
23215         
23216         
23217         // hides menus... - so this cant be on a menu...
23218         Roo.menu.MenuMgr.hideAll();
23219
23220         //this.editorsyncValue();
23221     },
23222    
23223     
23224     createFontOptions : function(){
23225         var buf = [], fs = this.fontFamilies, ff, lc;
23226         
23227         
23228         
23229         for(var i = 0, len = fs.length; i< len; i++){
23230             ff = fs[i];
23231             lc = ff.toLowerCase();
23232             buf.push(
23233                 '<option value="',lc,'" style="font-family:',ff,';"',
23234                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23235                     ff,
23236                 '</option>'
23237             );
23238         }
23239         return buf.join('');
23240     },
23241     
23242     toggleSourceEdit : function(sourceEditMode){
23243         
23244         Roo.log("toolbar toogle");
23245         if(sourceEditMode === undefined){
23246             sourceEditMode = !this.sourceEditMode;
23247         }
23248         this.sourceEditMode = sourceEditMode === true;
23249         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23250         // just toggle the button?
23251         if(btn.pressed !== this.sourceEditMode){
23252             btn.toggle(this.sourceEditMode);
23253             return;
23254         }
23255         
23256         if(sourceEditMode){
23257             Roo.log("disabling buttons");
23258             this.tb.items.each(function(item){
23259                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23260                     item.disable();
23261                 }
23262             });
23263           
23264         }else{
23265             Roo.log("enabling buttons");
23266             if(this.editorcore.initialized){
23267                 this.tb.items.each(function(item){
23268                     item.enable();
23269                 });
23270             }
23271             
23272         }
23273         Roo.log("calling toggole on editor");
23274         // tell the editor that it's been pressed..
23275         this.editor.toggleSourceEdit(sourceEditMode);
23276        
23277     },
23278      /**
23279      * Object collection of toolbar tooltips for the buttons in the editor. The key
23280      * is the command id associated with that button and the value is a valid QuickTips object.
23281      * For example:
23282 <pre><code>
23283 {
23284     bold : {
23285         title: 'Bold (Ctrl+B)',
23286         text: 'Make the selected text bold.',
23287         cls: 'x-html-editor-tip'
23288     },
23289     italic : {
23290         title: 'Italic (Ctrl+I)',
23291         text: 'Make the selected text italic.',
23292         cls: 'x-html-editor-tip'
23293     },
23294     ...
23295 </code></pre>
23296     * @type Object
23297      */
23298     buttonTips : {
23299         bold : {
23300             title: 'Bold (Ctrl+B)',
23301             text: 'Make the selected text bold.',
23302             cls: 'x-html-editor-tip'
23303         },
23304         italic : {
23305             title: 'Italic (Ctrl+I)',
23306             text: 'Make the selected text italic.',
23307             cls: 'x-html-editor-tip'
23308         },
23309         underline : {
23310             title: 'Underline (Ctrl+U)',
23311             text: 'Underline the selected text.',
23312             cls: 'x-html-editor-tip'
23313         },
23314         strikethrough : {
23315             title: 'Strikethrough',
23316             text: 'Strikethrough the selected text.',
23317             cls: 'x-html-editor-tip'
23318         },
23319         increasefontsize : {
23320             title: 'Grow Text',
23321             text: 'Increase the font size.',
23322             cls: 'x-html-editor-tip'
23323         },
23324         decreasefontsize : {
23325             title: 'Shrink Text',
23326             text: 'Decrease the font size.',
23327             cls: 'x-html-editor-tip'
23328         },
23329         backcolor : {
23330             title: 'Text Highlight Color',
23331             text: 'Change the background color of the selected text.',
23332             cls: 'x-html-editor-tip'
23333         },
23334         forecolor : {
23335             title: 'Font Color',
23336             text: 'Change the color of the selected text.',
23337             cls: 'x-html-editor-tip'
23338         },
23339         justifyleft : {
23340             title: 'Align Text Left',
23341             text: 'Align text to the left.',
23342             cls: 'x-html-editor-tip'
23343         },
23344         justifycenter : {
23345             title: 'Center Text',
23346             text: 'Center text in the editor.',
23347             cls: 'x-html-editor-tip'
23348         },
23349         justifyright : {
23350             title: 'Align Text Right',
23351             text: 'Align text to the right.',
23352             cls: 'x-html-editor-tip'
23353         },
23354         insertunorderedlist : {
23355             title: 'Bullet List',
23356             text: 'Start a bulleted list.',
23357             cls: 'x-html-editor-tip'
23358         },
23359         insertorderedlist : {
23360             title: 'Numbered List',
23361             text: 'Start a numbered list.',
23362             cls: 'x-html-editor-tip'
23363         },
23364         createlink : {
23365             title: 'Hyperlink',
23366             text: 'Make the selected text a hyperlink.',
23367             cls: 'x-html-editor-tip'
23368         },
23369         sourceedit : {
23370             title: 'Source Edit',
23371             text: 'Switch to source editing mode.',
23372             cls: 'x-html-editor-tip'
23373         }
23374     },
23375     // private
23376     onDestroy : function(){
23377         if(this.rendered){
23378             
23379             this.tb.items.each(function(item){
23380                 if(item.menu){
23381                     item.menu.removeAll();
23382                     if(item.menu.el){
23383                         item.menu.el.destroy();
23384                     }
23385                 }
23386                 item.destroy();
23387             });
23388              
23389         }
23390     },
23391     onFirstFocus: function() {
23392         this.tb.items.each(function(item){
23393            item.enable();
23394         });
23395     }
23396 });
23397
23398
23399
23400
23401 // <script type="text/javascript">
23402 /*
23403  * Based on
23404  * Ext JS Library 1.1.1
23405  * Copyright(c) 2006-2007, Ext JS, LLC.
23406  *  
23407  
23408  */
23409
23410  
23411 /**
23412  * @class Roo.form.HtmlEditor.ToolbarContext
23413  * Context Toolbar
23414  * 
23415  * Usage:
23416  *
23417  new Roo.form.HtmlEditor({
23418     ....
23419     toolbars : [
23420         { xtype: 'ToolbarStandard', styles : {} }
23421         { xtype: 'ToolbarContext', disable : {} }
23422     ]
23423 })
23424
23425      
23426  * 
23427  * @config : {Object} disable List of elements to disable.. (not done yet.)
23428  * @config : {Object} styles  Map of styles available.
23429  * 
23430  */
23431
23432 Roo.form.HtmlEditor.ToolbarContext = function(config)
23433 {
23434     
23435     Roo.apply(this, config);
23436     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23437     // dont call parent... till later.
23438     this.styles = this.styles || {};
23439 }
23440
23441  
23442
23443 Roo.form.HtmlEditor.ToolbarContext.types = {
23444     'IMG' : {
23445         width : {
23446             title: "Width",
23447             width: 40
23448         },
23449         height:  {
23450             title: "Height",
23451             width: 40
23452         },
23453         align: {
23454             title: "Align",
23455             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23456             width : 80
23457             
23458         },
23459         border: {
23460             title: "Border",
23461             width: 40
23462         },
23463         alt: {
23464             title: "Alt",
23465             width: 120
23466         },
23467         src : {
23468             title: "Src",
23469             width: 220
23470         }
23471         
23472     },
23473     'A' : {
23474         name : {
23475             title: "Name",
23476             width: 50
23477         },
23478         target:  {
23479             title: "Target",
23480             width: 120
23481         },
23482         href:  {
23483             title: "Href",
23484             width: 220
23485         } // border?
23486         
23487     },
23488     'TABLE' : {
23489         rows : {
23490             title: "Rows",
23491             width: 20
23492         },
23493         cols : {
23494             title: "Cols",
23495             width: 20
23496         },
23497         width : {
23498             title: "Width",
23499             width: 40
23500         },
23501         height : {
23502             title: "Height",
23503             width: 40
23504         },
23505         border : {
23506             title: "Border",
23507             width: 20
23508         }
23509     },
23510     'TD' : {
23511         width : {
23512             title: "Width",
23513             width: 40
23514         },
23515         height : {
23516             title: "Height",
23517             width: 40
23518         },   
23519         align: {
23520             title: "Align",
23521             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23522             width: 80
23523         },
23524         valign: {
23525             title: "Valign",
23526             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23527             width: 80
23528         },
23529         colspan: {
23530             title: "Colspan",
23531             width: 20
23532             
23533         },
23534          'font-family'  : {
23535             title : "Font",
23536             style : 'fontFamily',
23537             displayField: 'display',
23538             optname : 'font-family',
23539             width: 140
23540         }
23541     },
23542     'INPUT' : {
23543         name : {
23544             title: "name",
23545             width: 120
23546         },
23547         value : {
23548             title: "Value",
23549             width: 120
23550         },
23551         width : {
23552             title: "Width",
23553             width: 40
23554         }
23555     },
23556     'LABEL' : {
23557         'for' : {
23558             title: "For",
23559             width: 120
23560         }
23561     },
23562     'TEXTAREA' : {
23563           name : {
23564             title: "name",
23565             width: 120
23566         },
23567         rows : {
23568             title: "Rows",
23569             width: 20
23570         },
23571         cols : {
23572             title: "Cols",
23573             width: 20
23574         }
23575     },
23576     'SELECT' : {
23577         name : {
23578             title: "name",
23579             width: 120
23580         },
23581         selectoptions : {
23582             title: "Options",
23583             width: 200
23584         }
23585     },
23586     
23587     // should we really allow this??
23588     // should this just be 
23589     'BODY' : {
23590         title : {
23591             title: "Title",
23592             width: 200,
23593             disabled : true
23594         }
23595     },
23596     'SPAN' : {
23597         'font-family'  : {
23598             title : "Font",
23599             style : 'fontFamily',
23600             displayField: 'display',
23601             optname : 'font-family',
23602             width: 140
23603         }
23604     },
23605     'DIV' : {
23606         'font-family'  : {
23607             title : "Font",
23608             style : 'fontFamily',
23609             displayField: 'display',
23610             optname : 'font-family',
23611             width: 140
23612         }
23613     },
23614      'P' : {
23615         'font-family'  : {
23616             title : "Font",
23617             style : 'fontFamily',
23618             displayField: 'display',
23619             optname : 'font-family',
23620             width: 140
23621         }
23622     },
23623     
23624     '*' : {
23625         // empty..
23626     }
23627
23628 };
23629
23630 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23631 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23632
23633 Roo.form.HtmlEditor.ToolbarContext.options = {
23634         'font-family'  : [ 
23635                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23636                 [ 'Courier New', 'Courier New'],
23637                 [ 'Tahoma', 'Tahoma'],
23638                 [ 'Times New Roman,serif', 'Times'],
23639                 [ 'Verdana','Verdana' ]
23640         ]
23641 };
23642
23643 // fixme - these need to be configurable..
23644  
23645
23646 //Roo.form.HtmlEditor.ToolbarContext.types
23647
23648
23649 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23650     
23651     tb: false,
23652     
23653     rendered: false,
23654     
23655     editor : false,
23656     editorcore : false,
23657     /**
23658      * @cfg {Object} disable  List of toolbar elements to disable
23659          
23660      */
23661     disable : false,
23662     /**
23663      * @cfg {Object} styles List of styles 
23664      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23665      *
23666      * These must be defined in the page, so they get rendered correctly..
23667      * .headline { }
23668      * TD.underline { }
23669      * 
23670      */
23671     styles : false,
23672     
23673     options: false,
23674     
23675     toolbars : false,
23676     
23677     init : function(editor)
23678     {
23679         this.editor = editor;
23680         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23681         var editorcore = this.editorcore;
23682         
23683         var fid = editorcore.frameId;
23684         var etb = this;
23685         function btn(id, toggle, handler){
23686             var xid = fid + '-'+ id ;
23687             return {
23688                 id : xid,
23689                 cmd : id,
23690                 cls : 'x-btn-icon x-edit-'+id,
23691                 enableToggle:toggle !== false,
23692                 scope: editorcore, // was editor...
23693                 handler:handler||editorcore.relayBtnCmd,
23694                 clickEvent:'mousedown',
23695                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23696                 tabIndex:-1
23697             };
23698         }
23699         // create a new element.
23700         var wdiv = editor.wrap.createChild({
23701                 tag: 'div'
23702             }, editor.wrap.dom.firstChild.nextSibling, true);
23703         
23704         // can we do this more than once??
23705         
23706          // stop form submits
23707       
23708  
23709         // disable everything...
23710         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23711         this.toolbars = {};
23712            
23713         for (var i in  ty) {
23714           
23715             this.toolbars[i] = this.buildToolbar(ty[i],i);
23716         }
23717         this.tb = this.toolbars.BODY;
23718         this.tb.el.show();
23719         this.buildFooter();
23720         this.footer.show();
23721         editor.on('hide', function( ) { this.footer.hide() }, this);
23722         editor.on('show', function( ) { this.footer.show() }, this);
23723         
23724          
23725         this.rendered = true;
23726         
23727         // the all the btns;
23728         editor.on('editorevent', this.updateToolbar, this);
23729         // other toolbars need to implement this..
23730         //editor.on('editmodechange', this.updateToolbar, this);
23731     },
23732     
23733     
23734     
23735     /**
23736      * Protected method that will not generally be called directly. It triggers
23737      * a toolbar update by reading the markup state of the current selection in the editor.
23738      *
23739      * Note you can force an update by calling on('editorevent', scope, false)
23740      */
23741     updateToolbar: function(editor,ev,sel){
23742
23743         //Roo.log(ev);
23744         // capture mouse up - this is handy for selecting images..
23745         // perhaps should go somewhere else...
23746         if(!this.editorcore.activated){
23747              this.editor.onFirstFocus();
23748             return;
23749         }
23750         
23751         
23752         
23753         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23754         // selectNode - might want to handle IE?
23755         if (ev &&
23756             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23757             ev.target && ev.target.tagName == 'IMG') {
23758             // they have click on an image...
23759             // let's see if we can change the selection...
23760             sel = ev.target;
23761          
23762               var nodeRange = sel.ownerDocument.createRange();
23763             try {
23764                 nodeRange.selectNode(sel);
23765             } catch (e) {
23766                 nodeRange.selectNodeContents(sel);
23767             }
23768             //nodeRange.collapse(true);
23769             var s = this.editorcore.win.getSelection();
23770             s.removeAllRanges();
23771             s.addRange(nodeRange);
23772         }  
23773         
23774       
23775         var updateFooter = sel ? false : true;
23776         
23777         
23778         var ans = this.editorcore.getAllAncestors();
23779         
23780         // pick
23781         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23782         
23783         if (!sel) { 
23784             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23785             sel = sel ? sel : this.editorcore.doc.body;
23786             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23787             
23788         }
23789         // pick a menu that exists..
23790         var tn = sel.tagName.toUpperCase();
23791         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23792         
23793         tn = sel.tagName.toUpperCase();
23794         
23795         var lastSel = this.tb.selectedNode;
23796         
23797         this.tb.selectedNode = sel;
23798         
23799         // if current menu does not match..
23800         
23801         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23802                 
23803             this.tb.el.hide();
23804             ///console.log("show: " + tn);
23805             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23806             this.tb.el.show();
23807             // update name
23808             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23809             
23810             
23811             // update attributes
23812             if (this.tb.fields) {
23813                 this.tb.fields.each(function(e) {
23814                     if (e.stylename) {
23815                         e.setValue(sel.style[e.stylename]);
23816                         return;
23817                     } 
23818                    e.setValue(sel.getAttribute(e.attrname));
23819                 });
23820             }
23821             
23822             var hasStyles = false;
23823             for(var i in this.styles) {
23824                 hasStyles = true;
23825                 break;
23826             }
23827             
23828             // update styles
23829             if (hasStyles) { 
23830                 var st = this.tb.fields.item(0);
23831                 
23832                 st.store.removeAll();
23833                
23834                 
23835                 var cn = sel.className.split(/\s+/);
23836                 
23837                 var avs = [];
23838                 if (this.styles['*']) {
23839                     
23840                     Roo.each(this.styles['*'], function(v) {
23841                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23842                     });
23843                 }
23844                 if (this.styles[tn]) { 
23845                     Roo.each(this.styles[tn], function(v) {
23846                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23847                     });
23848                 }
23849                 
23850                 st.store.loadData(avs);
23851                 st.collapse();
23852                 st.setValue(cn);
23853             }
23854             // flag our selected Node.
23855             this.tb.selectedNode = sel;
23856            
23857            
23858             Roo.menu.MenuMgr.hideAll();
23859
23860         }
23861         
23862         if (!updateFooter) {
23863             //this.footDisp.dom.innerHTML = ''; 
23864             return;
23865         }
23866         // update the footer
23867         //
23868         var html = '';
23869         
23870         this.footerEls = ans.reverse();
23871         Roo.each(this.footerEls, function(a,i) {
23872             if (!a) { return; }
23873             html += html.length ? ' &gt; '  :  '';
23874             
23875             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23876             
23877         });
23878        
23879         // 
23880         var sz = this.footDisp.up('td').getSize();
23881         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23882         this.footDisp.dom.style.marginLeft = '5px';
23883         
23884         this.footDisp.dom.style.overflow = 'hidden';
23885         
23886         this.footDisp.dom.innerHTML = html;
23887             
23888         //this.editorsyncValue();
23889     },
23890      
23891     
23892    
23893        
23894     // private
23895     onDestroy : function(){
23896         if(this.rendered){
23897             
23898             this.tb.items.each(function(item){
23899                 if(item.menu){
23900                     item.menu.removeAll();
23901                     if(item.menu.el){
23902                         item.menu.el.destroy();
23903                     }
23904                 }
23905                 item.destroy();
23906             });
23907              
23908         }
23909     },
23910     onFirstFocus: function() {
23911         // need to do this for all the toolbars..
23912         this.tb.items.each(function(item){
23913            item.enable();
23914         });
23915     },
23916     buildToolbar: function(tlist, nm)
23917     {
23918         var editor = this.editor;
23919         var editorcore = this.editorcore;
23920          // create a new element.
23921         var wdiv = editor.wrap.createChild({
23922                 tag: 'div'
23923             }, editor.wrap.dom.firstChild.nextSibling, true);
23924         
23925        
23926         var tb = new Roo.Toolbar(wdiv);
23927         // add the name..
23928         
23929         tb.add(nm+ ":&nbsp;");
23930         
23931         var styles = [];
23932         for(var i in this.styles) {
23933             styles.push(i);
23934         }
23935         
23936         // styles...
23937         if (styles && styles.length) {
23938             
23939             // this needs a multi-select checkbox...
23940             tb.addField( new Roo.form.ComboBox({
23941                 store: new Roo.data.SimpleStore({
23942                     id : 'val',
23943                     fields: ['val', 'selected'],
23944                     data : [] 
23945                 }),
23946                 name : '-roo-edit-className',
23947                 attrname : 'className',
23948                 displayField: 'val',
23949                 typeAhead: false,
23950                 mode: 'local',
23951                 editable : false,
23952                 triggerAction: 'all',
23953                 emptyText:'Select Style',
23954                 selectOnFocus:true,
23955                 width: 130,
23956                 listeners : {
23957                     'select': function(c, r, i) {
23958                         // initial support only for on class per el..
23959                         tb.selectedNode.className =  r ? r.get('val') : '';
23960                         editorcore.syncValue();
23961                     }
23962                 }
23963     
23964             }));
23965         }
23966         
23967         var tbc = Roo.form.HtmlEditor.ToolbarContext;
23968         var tbops = tbc.options;
23969         
23970         for (var i in tlist) {
23971             
23972             var item = tlist[i];
23973             tb.add(item.title + ":&nbsp;");
23974             
23975             
23976             //optname == used so you can configure the options available..
23977             var opts = item.opts ? item.opts : false;
23978             if (item.optname) {
23979                 opts = tbops[item.optname];
23980            
23981             }
23982             
23983             if (opts) {
23984                 // opts == pulldown..
23985                 tb.addField( new Roo.form.ComboBox({
23986                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
23987                         id : 'val',
23988                         fields: ['val', 'display'],
23989                         data : opts  
23990                     }),
23991                     name : '-roo-edit-' + i,
23992                     attrname : i,
23993                     stylename : item.style ? item.style : false,
23994                     displayField: item.displayField ? item.displayField : 'val',
23995                     valueField :  'val',
23996                     typeAhead: false,
23997                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
23998                     editable : false,
23999                     triggerAction: 'all',
24000                     emptyText:'Select',
24001                     selectOnFocus:true,
24002                     width: item.width ? item.width  : 130,
24003                     listeners : {
24004                         'select': function(c, r, i) {
24005                             if (c.stylename) {
24006                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24007                                 return;
24008                             }
24009                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24010                         }
24011                     }
24012
24013                 }));
24014                 continue;
24015                     
24016                  
24017                 
24018                 tb.addField( new Roo.form.TextField({
24019                     name: i,
24020                     width: 100,
24021                     //allowBlank:false,
24022                     value: ''
24023                 }));
24024                 continue;
24025             }
24026             tb.addField( new Roo.form.TextField({
24027                 name: '-roo-edit-' + i,
24028                 attrname : i,
24029                 
24030                 width: item.width,
24031                 //allowBlank:true,
24032                 value: '',
24033                 listeners: {
24034                     'change' : function(f, nv, ov) {
24035                         tb.selectedNode.setAttribute(f.attrname, nv);
24036                         editorcore.syncValue();
24037                     }
24038                 }
24039             }));
24040              
24041         }
24042         
24043         var _this = this;
24044         
24045         if(nm == 'BODY'){
24046             tb.addSeparator();
24047         
24048             tb.addButton( {
24049                 text: 'Stylesheets',
24050
24051                 listeners : {
24052                     click : function ()
24053                     {
24054                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24055                     }
24056                 }
24057             });
24058         }
24059         
24060         tb.addFill();
24061         tb.addButton( {
24062             text: 'Remove Tag',
24063     
24064             listeners : {
24065                 click : function ()
24066                 {
24067                     // remove
24068                     // undo does not work.
24069                      
24070                     var sn = tb.selectedNode;
24071                     
24072                     var pn = sn.parentNode;
24073                     
24074                     var stn =  sn.childNodes[0];
24075                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24076                     while (sn.childNodes.length) {
24077                         var node = sn.childNodes[0];
24078                         sn.removeChild(node);
24079                         //Roo.log(node);
24080                         pn.insertBefore(node, sn);
24081                         
24082                     }
24083                     pn.removeChild(sn);
24084                     var range = editorcore.createRange();
24085         
24086                     range.setStart(stn,0);
24087                     range.setEnd(en,0); //????
24088                     //range.selectNode(sel);
24089                     
24090                     
24091                     var selection = editorcore.getSelection();
24092                     selection.removeAllRanges();
24093                     selection.addRange(range);
24094                     
24095                     
24096                     
24097                     //_this.updateToolbar(null, null, pn);
24098                     _this.updateToolbar(null, null, null);
24099                     _this.footDisp.dom.innerHTML = ''; 
24100                 }
24101             }
24102             
24103                     
24104                 
24105             
24106         });
24107         
24108         
24109         tb.el.on('click', function(e){
24110             e.preventDefault(); // what does this do?
24111         });
24112         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24113         tb.el.hide();
24114         tb.name = nm;
24115         // dont need to disable them... as they will get hidden
24116         return tb;
24117          
24118         
24119     },
24120     buildFooter : function()
24121     {
24122         
24123         var fel = this.editor.wrap.createChild();
24124         this.footer = new Roo.Toolbar(fel);
24125         // toolbar has scrolly on left / right?
24126         var footDisp= new Roo.Toolbar.Fill();
24127         var _t = this;
24128         this.footer.add(
24129             {
24130                 text : '&lt;',
24131                 xtype: 'Button',
24132                 handler : function() {
24133                     _t.footDisp.scrollTo('left',0,true)
24134                 }
24135             }
24136         );
24137         this.footer.add( footDisp );
24138         this.footer.add( 
24139             {
24140                 text : '&gt;',
24141                 xtype: 'Button',
24142                 handler : function() {
24143                     // no animation..
24144                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24145                 }
24146             }
24147         );
24148         var fel = Roo.get(footDisp.el);
24149         fel.addClass('x-editor-context');
24150         this.footDispWrap = fel; 
24151         this.footDispWrap.overflow  = 'hidden';
24152         
24153         this.footDisp = fel.createChild();
24154         this.footDispWrap.on('click', this.onContextClick, this)
24155         
24156         
24157     },
24158     onContextClick : function (ev,dom)
24159     {
24160         ev.preventDefault();
24161         var  cn = dom.className;
24162         //Roo.log(cn);
24163         if (!cn.match(/x-ed-loc-/)) {
24164             return;
24165         }
24166         var n = cn.split('-').pop();
24167         var ans = this.footerEls;
24168         var sel = ans[n];
24169         
24170          // pick
24171         var range = this.editorcore.createRange();
24172         
24173         range.selectNodeContents(sel);
24174         //range.selectNode(sel);
24175         
24176         
24177         var selection = this.editorcore.getSelection();
24178         selection.removeAllRanges();
24179         selection.addRange(range);
24180         
24181         
24182         
24183         this.updateToolbar(null, null, sel);
24184         
24185         
24186     }
24187     
24188     
24189     
24190     
24191     
24192 });
24193
24194
24195
24196
24197
24198 /*
24199  * Based on:
24200  * Ext JS Library 1.1.1
24201  * Copyright(c) 2006-2007, Ext JS, LLC.
24202  *
24203  * Originally Released Under LGPL - original licence link has changed is not relivant.
24204  *
24205  * Fork - LGPL
24206  * <script type="text/javascript">
24207  */
24208  
24209 /**
24210  * @class Roo.form.BasicForm
24211  * @extends Roo.util.Observable
24212  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24213  * @constructor
24214  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24215  * @param {Object} config Configuration options
24216  */
24217 Roo.form.BasicForm = function(el, config){
24218     this.allItems = [];
24219     this.childForms = [];
24220     Roo.apply(this, config);
24221     /*
24222      * The Roo.form.Field items in this form.
24223      * @type MixedCollection
24224      */
24225      
24226      
24227     this.items = new Roo.util.MixedCollection(false, function(o){
24228         return o.id || (o.id = Roo.id());
24229     });
24230     this.addEvents({
24231         /**
24232          * @event beforeaction
24233          * Fires before any action is performed. Return false to cancel the action.
24234          * @param {Form} this
24235          * @param {Action} action The action to be performed
24236          */
24237         beforeaction: true,
24238         /**
24239          * @event actionfailed
24240          * Fires when an action fails.
24241          * @param {Form} this
24242          * @param {Action} action The action that failed
24243          */
24244         actionfailed : true,
24245         /**
24246          * @event actioncomplete
24247          * Fires when an action is completed.
24248          * @param {Form} this
24249          * @param {Action} action The action that completed
24250          */
24251         actioncomplete : true
24252     });
24253     if(el){
24254         this.initEl(el);
24255     }
24256     Roo.form.BasicForm.superclass.constructor.call(this);
24257 };
24258
24259 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24260     /**
24261      * @cfg {String} method
24262      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24263      */
24264     /**
24265      * @cfg {DataReader} reader
24266      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24267      * This is optional as there is built-in support for processing JSON.
24268      */
24269     /**
24270      * @cfg {DataReader} errorReader
24271      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24272      * This is completely optional as there is built-in support for processing JSON.
24273      */
24274     /**
24275      * @cfg {String} url
24276      * The URL to use for form actions if one isn't supplied in the action options.
24277      */
24278     /**
24279      * @cfg {Boolean} fileUpload
24280      * Set to true if this form is a file upload.
24281      */
24282      
24283     /**
24284      * @cfg {Object} baseParams
24285      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24286      */
24287      /**
24288      
24289     /**
24290      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24291      */
24292     timeout: 30,
24293
24294     // private
24295     activeAction : null,
24296
24297     /**
24298      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24299      * or setValues() data instead of when the form was first created.
24300      */
24301     trackResetOnLoad : false,
24302     
24303     
24304     /**
24305      * childForms - used for multi-tab forms
24306      * @type {Array}
24307      */
24308     childForms : false,
24309     
24310     /**
24311      * allItems - full list of fields.
24312      * @type {Array}
24313      */
24314     allItems : false,
24315     
24316     /**
24317      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24318      * element by passing it or its id or mask the form itself by passing in true.
24319      * @type Mixed
24320      */
24321     waitMsgTarget : false,
24322
24323     // private
24324     initEl : function(el){
24325         this.el = Roo.get(el);
24326         this.id = this.el.id || Roo.id();
24327         this.el.on('submit', this.onSubmit, this);
24328         this.el.addClass('x-form');
24329     },
24330
24331     // private
24332     onSubmit : function(e){
24333         e.stopEvent();
24334     },
24335
24336     /**
24337      * Returns true if client-side validation on the form is successful.
24338      * @return Boolean
24339      */
24340     isValid : function(){
24341         var valid = true;
24342         this.items.each(function(f){
24343            if(!f.validate()){
24344                valid = false;
24345            }
24346         });
24347         return valid;
24348     },
24349
24350     /**
24351      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24352      * @return Boolean
24353      */
24354     isDirty : function(){
24355         var dirty = false;
24356         this.items.each(function(f){
24357            if(f.isDirty()){
24358                dirty = true;
24359                return false;
24360            }
24361         });
24362         return dirty;
24363     },
24364     
24365     /**
24366      * Returns true if any fields in this form have changed since their original load. (New version)
24367      * @return Boolean
24368      */
24369     
24370     hasChanged : function()
24371     {
24372         var dirty = false;
24373         this.items.each(function(f){
24374            if(f.hasChanged()){
24375                dirty = true;
24376                return false;
24377            }
24378         });
24379         return dirty;
24380         
24381     },
24382     /**
24383      * Resets all hasChanged to 'false' -
24384      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24385      * So hasChanged storage is only to be used for this purpose
24386      * @return Boolean
24387      */
24388     resetHasChanged : function()
24389     {
24390         this.items.each(function(f){
24391            f.resetHasChanged();
24392         });
24393         
24394     },
24395     
24396     
24397     /**
24398      * Performs a predefined action (submit or load) or custom actions you define on this form.
24399      * @param {String} actionName The name of the action type
24400      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24401      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24402      * accept other config options):
24403      * <pre>
24404 Property          Type             Description
24405 ----------------  ---------------  ----------------------------------------------------------------------------------
24406 url               String           The url for the action (defaults to the form's url)
24407 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24408 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24409 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24410                                    validate the form on the client (defaults to false)
24411      * </pre>
24412      * @return {BasicForm} this
24413      */
24414     doAction : function(action, options){
24415         if(typeof action == 'string'){
24416             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24417         }
24418         if(this.fireEvent('beforeaction', this, action) !== false){
24419             this.beforeAction(action);
24420             action.run.defer(100, action);
24421         }
24422         return this;
24423     },
24424
24425     /**
24426      * Shortcut to do a submit action.
24427      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24428      * @return {BasicForm} this
24429      */
24430     submit : function(options){
24431         this.doAction('submit', options);
24432         return this;
24433     },
24434
24435     /**
24436      * Shortcut to do a load action.
24437      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24438      * @return {BasicForm} this
24439      */
24440     load : function(options){
24441         this.doAction('load', options);
24442         return this;
24443     },
24444
24445     /**
24446      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24447      * @param {Record} record The record to edit
24448      * @return {BasicForm} this
24449      */
24450     updateRecord : function(record){
24451         record.beginEdit();
24452         var fs = record.fields;
24453         fs.each(function(f){
24454             var field = this.findField(f.name);
24455             if(field){
24456                 record.set(f.name, field.getValue());
24457             }
24458         }, this);
24459         record.endEdit();
24460         return this;
24461     },
24462
24463     /**
24464      * Loads an Roo.data.Record into this form.
24465      * @param {Record} record The record to load
24466      * @return {BasicForm} this
24467      */
24468     loadRecord : function(record){
24469         this.setValues(record.data);
24470         return this;
24471     },
24472
24473     // private
24474     beforeAction : function(action){
24475         var o = action.options;
24476         
24477        
24478         if(this.waitMsgTarget === true){
24479             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24480         }else if(this.waitMsgTarget){
24481             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24482             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24483         }else {
24484             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24485         }
24486          
24487     },
24488
24489     // private
24490     afterAction : function(action, success){
24491         this.activeAction = null;
24492         var o = action.options;
24493         
24494         if(this.waitMsgTarget === true){
24495             this.el.unmask();
24496         }else if(this.waitMsgTarget){
24497             this.waitMsgTarget.unmask();
24498         }else{
24499             Roo.MessageBox.updateProgress(1);
24500             Roo.MessageBox.hide();
24501         }
24502          
24503         if(success){
24504             if(o.reset){
24505                 this.reset();
24506             }
24507             Roo.callback(o.success, o.scope, [this, action]);
24508             this.fireEvent('actioncomplete', this, action);
24509             
24510         }else{
24511             
24512             // failure condition..
24513             // we have a scenario where updates need confirming.
24514             // eg. if a locking scenario exists..
24515             // we look for { errors : { needs_confirm : true }} in the response.
24516             if (
24517                 (typeof(action.result) != 'undefined')  &&
24518                 (typeof(action.result.errors) != 'undefined')  &&
24519                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24520            ){
24521                 var _t = this;
24522                 Roo.MessageBox.confirm(
24523                     "Change requires confirmation",
24524                     action.result.errorMsg,
24525                     function(r) {
24526                         if (r != 'yes') {
24527                             return;
24528                         }
24529                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24530                     }
24531                     
24532                 );
24533                 
24534                 
24535                 
24536                 return;
24537             }
24538             
24539             Roo.callback(o.failure, o.scope, [this, action]);
24540             // show an error message if no failed handler is set..
24541             if (!this.hasListener('actionfailed')) {
24542                 Roo.MessageBox.alert("Error",
24543                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24544                         action.result.errorMsg :
24545                         "Saving Failed, please check your entries or try again"
24546                 );
24547             }
24548             
24549             this.fireEvent('actionfailed', this, action);
24550         }
24551         
24552     },
24553
24554     /**
24555      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24556      * @param {String} id The value to search for
24557      * @return Field
24558      */
24559     findField : function(id){
24560         var field = this.items.get(id);
24561         if(!field){
24562             this.items.each(function(f){
24563                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24564                     field = f;
24565                     return false;
24566                 }
24567             });
24568         }
24569         return field || null;
24570     },
24571
24572     /**
24573      * Add a secondary form to this one, 
24574      * Used to provide tabbed forms. One form is primary, with hidden values 
24575      * which mirror the elements from the other forms.
24576      * 
24577      * @param {Roo.form.Form} form to add.
24578      * 
24579      */
24580     addForm : function(form)
24581     {
24582        
24583         if (this.childForms.indexOf(form) > -1) {
24584             // already added..
24585             return;
24586         }
24587         this.childForms.push(form);
24588         var n = '';
24589         Roo.each(form.allItems, function (fe) {
24590             
24591             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24592             if (this.findField(n)) { // already added..
24593                 return;
24594             }
24595             var add = new Roo.form.Hidden({
24596                 name : n
24597             });
24598             add.render(this.el);
24599             
24600             this.add( add );
24601         }, this);
24602         
24603     },
24604     /**
24605      * Mark fields in this form invalid in bulk.
24606      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24607      * @return {BasicForm} this
24608      */
24609     markInvalid : function(errors){
24610         if(errors instanceof Array){
24611             for(var i = 0, len = errors.length; i < len; i++){
24612                 var fieldError = errors[i];
24613                 var f = this.findField(fieldError.id);
24614                 if(f){
24615                     f.markInvalid(fieldError.msg);
24616                 }
24617             }
24618         }else{
24619             var field, id;
24620             for(id in errors){
24621                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24622                     field.markInvalid(errors[id]);
24623                 }
24624             }
24625         }
24626         Roo.each(this.childForms || [], function (f) {
24627             f.markInvalid(errors);
24628         });
24629         
24630         return this;
24631     },
24632
24633     /**
24634      * Set values for fields in this form in bulk.
24635      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24636      * @return {BasicForm} this
24637      */
24638     setValues : function(values){
24639         if(values instanceof Array){ // array of objects
24640             for(var i = 0, len = values.length; i < len; i++){
24641                 var v = values[i];
24642                 var f = this.findField(v.id);
24643                 if(f){
24644                     f.setValue(v.value);
24645                     if(this.trackResetOnLoad){
24646                         f.originalValue = f.getValue();
24647                     }
24648                 }
24649             }
24650         }else{ // object hash
24651             var field, id;
24652             for(id in values){
24653                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24654                     
24655                     if (field.setFromData && 
24656                         field.valueField && 
24657                         field.displayField &&
24658                         // combos' with local stores can 
24659                         // be queried via setValue()
24660                         // to set their value..
24661                         (field.store && !field.store.isLocal)
24662                         ) {
24663                         // it's a combo
24664                         var sd = { };
24665                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24666                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24667                         field.setFromData(sd);
24668                         
24669                     } else {
24670                         field.setValue(values[id]);
24671                     }
24672                     
24673                     
24674                     if(this.trackResetOnLoad){
24675                         field.originalValue = field.getValue();
24676                     }
24677                 }
24678             }
24679         }
24680         this.resetHasChanged();
24681         
24682         
24683         Roo.each(this.childForms || [], function (f) {
24684             f.setValues(values);
24685             f.resetHasChanged();
24686         });
24687                 
24688         return this;
24689     },
24690
24691     /**
24692      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24693      * they are returned as an array.
24694      * @param {Boolean} asString
24695      * @return {Object}
24696      */
24697     getValues : function(asString){
24698         if (this.childForms) {
24699             // copy values from the child forms
24700             Roo.each(this.childForms, function (f) {
24701                 this.setValues(f.getValues());
24702             }, this);
24703         }
24704         
24705         
24706         
24707         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24708         if(asString === true){
24709             return fs;
24710         }
24711         return Roo.urlDecode(fs);
24712     },
24713     
24714     /**
24715      * Returns the fields in this form as an object with key/value pairs. 
24716      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24717      * @return {Object}
24718      */
24719     getFieldValues : function(with_hidden)
24720     {
24721         if (this.childForms) {
24722             // copy values from the child forms
24723             // should this call getFieldValues - probably not as we do not currently copy
24724             // hidden fields when we generate..
24725             Roo.each(this.childForms, function (f) {
24726                 this.setValues(f.getValues());
24727             }, this);
24728         }
24729         
24730         var ret = {};
24731         this.items.each(function(f){
24732             if (!f.getName()) {
24733                 return;
24734             }
24735             var v = f.getValue();
24736             if (f.inputType =='radio') {
24737                 if (typeof(ret[f.getName()]) == 'undefined') {
24738                     ret[f.getName()] = ''; // empty..
24739                 }
24740                 
24741                 if (!f.el.dom.checked) {
24742                     return;
24743                     
24744                 }
24745                 v = f.el.dom.value;
24746                 
24747             }
24748             
24749             // not sure if this supported any more..
24750             if ((typeof(v) == 'object') && f.getRawValue) {
24751                 v = f.getRawValue() ; // dates..
24752             }
24753             // combo boxes where name != hiddenName...
24754             if (f.name != f.getName()) {
24755                 ret[f.name] = f.getRawValue();
24756             }
24757             ret[f.getName()] = v;
24758         });
24759         
24760         return ret;
24761     },
24762
24763     /**
24764      * Clears all invalid messages in this form.
24765      * @return {BasicForm} this
24766      */
24767     clearInvalid : function(){
24768         this.items.each(function(f){
24769            f.clearInvalid();
24770         });
24771         
24772         Roo.each(this.childForms || [], function (f) {
24773             f.clearInvalid();
24774         });
24775         
24776         
24777         return this;
24778     },
24779
24780     /**
24781      * Resets this form.
24782      * @return {BasicForm} this
24783      */
24784     reset : function(){
24785         this.items.each(function(f){
24786             f.reset();
24787         });
24788         
24789         Roo.each(this.childForms || [], function (f) {
24790             f.reset();
24791         });
24792         this.resetHasChanged();
24793         
24794         return this;
24795     },
24796
24797     /**
24798      * Add Roo.form components to this form.
24799      * @param {Field} field1
24800      * @param {Field} field2 (optional)
24801      * @param {Field} etc (optional)
24802      * @return {BasicForm} this
24803      */
24804     add : function(){
24805         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24806         return this;
24807     },
24808
24809
24810     /**
24811      * Removes a field from the items collection (does NOT remove its markup).
24812      * @param {Field} field
24813      * @return {BasicForm} this
24814      */
24815     remove : function(field){
24816         this.items.remove(field);
24817         return this;
24818     },
24819
24820     /**
24821      * Looks at the fields in this form, checks them for an id attribute,
24822      * and calls applyTo on the existing dom element with that id.
24823      * @return {BasicForm} this
24824      */
24825     render : function(){
24826         this.items.each(function(f){
24827             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24828                 f.applyTo(f.id);
24829             }
24830         });
24831         return this;
24832     },
24833
24834     /**
24835      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24836      * @param {Object} values
24837      * @return {BasicForm} this
24838      */
24839     applyToFields : function(o){
24840         this.items.each(function(f){
24841            Roo.apply(f, o);
24842         });
24843         return this;
24844     },
24845
24846     /**
24847      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24848      * @param {Object} values
24849      * @return {BasicForm} this
24850      */
24851     applyIfToFields : function(o){
24852         this.items.each(function(f){
24853            Roo.applyIf(f, o);
24854         });
24855         return this;
24856     }
24857 });
24858
24859 // back compat
24860 Roo.BasicForm = Roo.form.BasicForm;/*
24861  * Based on:
24862  * Ext JS Library 1.1.1
24863  * Copyright(c) 2006-2007, Ext JS, LLC.
24864  *
24865  * Originally Released Under LGPL - original licence link has changed is not relivant.
24866  *
24867  * Fork - LGPL
24868  * <script type="text/javascript">
24869  */
24870
24871 /**
24872  * @class Roo.form.Form
24873  * @extends Roo.form.BasicForm
24874  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24875  * @constructor
24876  * @param {Object} config Configuration options
24877  */
24878 Roo.form.Form = function(config){
24879     var xitems =  [];
24880     if (config.items) {
24881         xitems = config.items;
24882         delete config.items;
24883     }
24884    
24885     
24886     Roo.form.Form.superclass.constructor.call(this, null, config);
24887     this.url = this.url || this.action;
24888     if(!this.root){
24889         this.root = new Roo.form.Layout(Roo.applyIf({
24890             id: Roo.id()
24891         }, config));
24892     }
24893     this.active = this.root;
24894     /**
24895      * Array of all the buttons that have been added to this form via {@link addButton}
24896      * @type Array
24897      */
24898     this.buttons = [];
24899     this.allItems = [];
24900     this.addEvents({
24901         /**
24902          * @event clientvalidation
24903          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24904          * @param {Form} this
24905          * @param {Boolean} valid true if the form has passed client-side validation
24906          */
24907         clientvalidation: true,
24908         /**
24909          * @event rendered
24910          * Fires when the form is rendered
24911          * @param {Roo.form.Form} form
24912          */
24913         rendered : true
24914     });
24915     
24916     if (this.progressUrl) {
24917             // push a hidden field onto the list of fields..
24918             this.addxtype( {
24919                     xns: Roo.form, 
24920                     xtype : 'Hidden', 
24921                     name : 'UPLOAD_IDENTIFIER' 
24922             });
24923         }
24924         
24925     
24926     Roo.each(xitems, this.addxtype, this);
24927     
24928     
24929     
24930 };
24931
24932 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24933     /**
24934      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24935      */
24936     /**
24937      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24938      */
24939     /**
24940      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24941      */
24942     buttonAlign:'center',
24943
24944     /**
24945      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24946      */
24947     minButtonWidth:75,
24948
24949     /**
24950      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24951      * This property cascades to child containers if not set.
24952      */
24953     labelAlign:'left',
24954
24955     /**
24956      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24957      * fires a looping event with that state. This is required to bind buttons to the valid
24958      * state using the config value formBind:true on the button.
24959      */
24960     monitorValid : false,
24961
24962     /**
24963      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24964      */
24965     monitorPoll : 200,
24966     
24967     /**
24968      * @cfg {String} progressUrl - Url to return progress data 
24969      */
24970     
24971     progressUrl : false,
24972   
24973     /**
24974      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
24975      * fields are added and the column is closed. If no fields are passed the column remains open
24976      * until end() is called.
24977      * @param {Object} config The config to pass to the column
24978      * @param {Field} field1 (optional)
24979      * @param {Field} field2 (optional)
24980      * @param {Field} etc (optional)
24981      * @return Column The column container object
24982      */
24983     column : function(c){
24984         var col = new Roo.form.Column(c);
24985         this.start(col);
24986         if(arguments.length > 1){ // duplicate code required because of Opera
24987             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
24988             this.end();
24989         }
24990         return col;
24991     },
24992
24993     /**
24994      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
24995      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
24996      * until end() is called.
24997      * @param {Object} config The config to pass to the fieldset
24998      * @param {Field} field1 (optional)
24999      * @param {Field} field2 (optional)
25000      * @param {Field} etc (optional)
25001      * @return FieldSet The fieldset container object
25002      */
25003     fieldset : function(c){
25004         var fs = new Roo.form.FieldSet(c);
25005         this.start(fs);
25006         if(arguments.length > 1){ // duplicate code required because of Opera
25007             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25008             this.end();
25009         }
25010         return fs;
25011     },
25012
25013     /**
25014      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25015      * fields are added and the container is closed. If no fields are passed the container remains open
25016      * until end() is called.
25017      * @param {Object} config The config to pass to the Layout
25018      * @param {Field} field1 (optional)
25019      * @param {Field} field2 (optional)
25020      * @param {Field} etc (optional)
25021      * @return Layout The container object
25022      */
25023     container : function(c){
25024         var l = new Roo.form.Layout(c);
25025         this.start(l);
25026         if(arguments.length > 1){ // duplicate code required because of Opera
25027             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25028             this.end();
25029         }
25030         return l;
25031     },
25032
25033     /**
25034      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25035      * @param {Object} container A Roo.form.Layout or subclass of Layout
25036      * @return {Form} this
25037      */
25038     start : function(c){
25039         // cascade label info
25040         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25041         this.active.stack.push(c);
25042         c.ownerCt = this.active;
25043         this.active = c;
25044         return this;
25045     },
25046
25047     /**
25048      * Closes the current open container
25049      * @return {Form} this
25050      */
25051     end : function(){
25052         if(this.active == this.root){
25053             return this;
25054         }
25055         this.active = this.active.ownerCt;
25056         return this;
25057     },
25058
25059     /**
25060      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25061      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25062      * as the label of the field.
25063      * @param {Field} field1
25064      * @param {Field} field2 (optional)
25065      * @param {Field} etc. (optional)
25066      * @return {Form} this
25067      */
25068     add : function(){
25069         this.active.stack.push.apply(this.active.stack, arguments);
25070         this.allItems.push.apply(this.allItems,arguments);
25071         var r = [];
25072         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25073             if(a[i].isFormField){
25074                 r.push(a[i]);
25075             }
25076         }
25077         if(r.length > 0){
25078             Roo.form.Form.superclass.add.apply(this, r);
25079         }
25080         return this;
25081     },
25082     
25083
25084     
25085     
25086     
25087      /**
25088      * Find any element that has been added to a form, using it's ID or name
25089      * This can include framesets, columns etc. along with regular fields..
25090      * @param {String} id - id or name to find.
25091      
25092      * @return {Element} e - or false if nothing found.
25093      */
25094     findbyId : function(id)
25095     {
25096         var ret = false;
25097         if (!id) {
25098             return ret;
25099         }
25100         Roo.each(this.allItems, function(f){
25101             if (f.id == id || f.name == id ){
25102                 ret = f;
25103                 return false;
25104             }
25105         });
25106         return ret;
25107     },
25108
25109     
25110     
25111     /**
25112      * Render this form into the passed container. This should only be called once!
25113      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25114      * @return {Form} this
25115      */
25116     render : function(ct)
25117     {
25118         
25119         
25120         
25121         ct = Roo.get(ct);
25122         var o = this.autoCreate || {
25123             tag: 'form',
25124             method : this.method || 'POST',
25125             id : this.id || Roo.id()
25126         };
25127         this.initEl(ct.createChild(o));
25128
25129         this.root.render(this.el);
25130         
25131        
25132              
25133         this.items.each(function(f){
25134             f.render('x-form-el-'+f.id);
25135         });
25136
25137         if(this.buttons.length > 0){
25138             // tables are required to maintain order and for correct IE layout
25139             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25140                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25141                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25142             }}, null, true);
25143             var tr = tb.getElementsByTagName('tr')[0];
25144             for(var i = 0, len = this.buttons.length; i < len; i++) {
25145                 var b = this.buttons[i];
25146                 var td = document.createElement('td');
25147                 td.className = 'x-form-btn-td';
25148                 b.render(tr.appendChild(td));
25149             }
25150         }
25151         if(this.monitorValid){ // initialize after render
25152             this.startMonitoring();
25153         }
25154         this.fireEvent('rendered', this);
25155         return this;
25156     },
25157
25158     /**
25159      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25160      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25161      * object or a valid Roo.DomHelper element config
25162      * @param {Function} handler The function called when the button is clicked
25163      * @param {Object} scope (optional) The scope of the handler function
25164      * @return {Roo.Button}
25165      */
25166     addButton : function(config, handler, scope){
25167         var bc = {
25168             handler: handler,
25169             scope: scope,
25170             minWidth: this.minButtonWidth,
25171             hideParent:true
25172         };
25173         if(typeof config == "string"){
25174             bc.text = config;
25175         }else{
25176             Roo.apply(bc, config);
25177         }
25178         var btn = new Roo.Button(null, bc);
25179         this.buttons.push(btn);
25180         return btn;
25181     },
25182
25183      /**
25184      * Adds a series of form elements (using the xtype property as the factory method.
25185      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25186      * @param {Object} config 
25187      */
25188     
25189     addxtype : function()
25190     {
25191         var ar = Array.prototype.slice.call(arguments, 0);
25192         var ret = false;
25193         for(var i = 0; i < ar.length; i++) {
25194             if (!ar[i]) {
25195                 continue; // skip -- if this happends something invalid got sent, we 
25196                 // should ignore it, as basically that interface element will not show up
25197                 // and that should be pretty obvious!!
25198             }
25199             
25200             if (Roo.form[ar[i].xtype]) {
25201                 ar[i].form = this;
25202                 var fe = Roo.factory(ar[i], Roo.form);
25203                 if (!ret) {
25204                     ret = fe;
25205                 }
25206                 fe.form = this;
25207                 if (fe.store) {
25208                     fe.store.form = this;
25209                 }
25210                 if (fe.isLayout) {  
25211                          
25212                     this.start(fe);
25213                     this.allItems.push(fe);
25214                     if (fe.items && fe.addxtype) {
25215                         fe.addxtype.apply(fe, fe.items);
25216                         delete fe.items;
25217                     }
25218                      this.end();
25219                     continue;
25220                 }
25221                 
25222                 
25223                  
25224                 this.add(fe);
25225               //  console.log('adding ' + ar[i].xtype);
25226             }
25227             if (ar[i].xtype == 'Button') {  
25228                 //console.log('adding button');
25229                 //console.log(ar[i]);
25230                 this.addButton(ar[i]);
25231                 this.allItems.push(fe);
25232                 continue;
25233             }
25234             
25235             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25236                 alert('end is not supported on xtype any more, use items');
25237             //    this.end();
25238             //    //console.log('adding end');
25239             }
25240             
25241         }
25242         return ret;
25243     },
25244     
25245     /**
25246      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25247      * option "monitorValid"
25248      */
25249     startMonitoring : function(){
25250         if(!this.bound){
25251             this.bound = true;
25252             Roo.TaskMgr.start({
25253                 run : this.bindHandler,
25254                 interval : this.monitorPoll || 200,
25255                 scope: this
25256             });
25257         }
25258     },
25259
25260     /**
25261      * Stops monitoring of the valid state of this form
25262      */
25263     stopMonitoring : function(){
25264         this.bound = false;
25265     },
25266
25267     // private
25268     bindHandler : function(){
25269         if(!this.bound){
25270             return false; // stops binding
25271         }
25272         var valid = true;
25273         this.items.each(function(f){
25274             if(!f.isValid(true)){
25275                 valid = false;
25276                 return false;
25277             }
25278         });
25279         for(var i = 0, len = this.buttons.length; i < len; i++){
25280             var btn = this.buttons[i];
25281             if(btn.formBind === true && btn.disabled === valid){
25282                 btn.setDisabled(!valid);
25283             }
25284         }
25285         this.fireEvent('clientvalidation', this, valid);
25286     }
25287     
25288     
25289     
25290     
25291     
25292     
25293     
25294     
25295 });
25296
25297
25298 // back compat
25299 Roo.Form = Roo.form.Form;
25300 /*
25301  * Based on:
25302  * Ext JS Library 1.1.1
25303  * Copyright(c) 2006-2007, Ext JS, LLC.
25304  *
25305  * Originally Released Under LGPL - original licence link has changed is not relivant.
25306  *
25307  * Fork - LGPL
25308  * <script type="text/javascript">
25309  */
25310
25311 // as we use this in bootstrap.
25312 Roo.namespace('Roo.form');
25313  /**
25314  * @class Roo.form.Action
25315  * Internal Class used to handle form actions
25316  * @constructor
25317  * @param {Roo.form.BasicForm} el The form element or its id
25318  * @param {Object} config Configuration options
25319  */
25320
25321  
25322  
25323 // define the action interface
25324 Roo.form.Action = function(form, options){
25325     this.form = form;
25326     this.options = options || {};
25327 };
25328 /**
25329  * Client Validation Failed
25330  * @const 
25331  */
25332 Roo.form.Action.CLIENT_INVALID = 'client';
25333 /**
25334  * Server Validation Failed
25335  * @const 
25336  */
25337 Roo.form.Action.SERVER_INVALID = 'server';
25338  /**
25339  * Connect to Server Failed
25340  * @const 
25341  */
25342 Roo.form.Action.CONNECT_FAILURE = 'connect';
25343 /**
25344  * Reading Data from Server Failed
25345  * @const 
25346  */
25347 Roo.form.Action.LOAD_FAILURE = 'load';
25348
25349 Roo.form.Action.prototype = {
25350     type : 'default',
25351     failureType : undefined,
25352     response : undefined,
25353     result : undefined,
25354
25355     // interface method
25356     run : function(options){
25357
25358     },
25359
25360     // interface method
25361     success : function(response){
25362
25363     },
25364
25365     // interface method
25366     handleResponse : function(response){
25367
25368     },
25369
25370     // default connection failure
25371     failure : function(response){
25372         
25373         this.response = response;
25374         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25375         this.form.afterAction(this, false);
25376     },
25377
25378     processResponse : function(response){
25379         this.response = response;
25380         if(!response.responseText){
25381             return true;
25382         }
25383         this.result = this.handleResponse(response);
25384         return this.result;
25385     },
25386
25387     // utility functions used internally
25388     getUrl : function(appendParams){
25389         var url = this.options.url || this.form.url || this.form.el.dom.action;
25390         if(appendParams){
25391             var p = this.getParams();
25392             if(p){
25393                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25394             }
25395         }
25396         return url;
25397     },
25398
25399     getMethod : function(){
25400         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25401     },
25402
25403     getParams : function(){
25404         var bp = this.form.baseParams;
25405         var p = this.options.params;
25406         if(p){
25407             if(typeof p == "object"){
25408                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25409             }else if(typeof p == 'string' && bp){
25410                 p += '&' + Roo.urlEncode(bp);
25411             }
25412         }else if(bp){
25413             p = Roo.urlEncode(bp);
25414         }
25415         return p;
25416     },
25417
25418     createCallback : function(){
25419         return {
25420             success: this.success,
25421             failure: this.failure,
25422             scope: this,
25423             timeout: (this.form.timeout*1000),
25424             upload: this.form.fileUpload ? this.success : undefined
25425         };
25426     }
25427 };
25428
25429 Roo.form.Action.Submit = function(form, options){
25430     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25431 };
25432
25433 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25434     type : 'submit',
25435
25436     haveProgress : false,
25437     uploadComplete : false,
25438     
25439     // uploadProgress indicator.
25440     uploadProgress : function()
25441     {
25442         if (!this.form.progressUrl) {
25443             return;
25444         }
25445         
25446         if (!this.haveProgress) {
25447             Roo.MessageBox.progress("Uploading", "Uploading");
25448         }
25449         if (this.uploadComplete) {
25450            Roo.MessageBox.hide();
25451            return;
25452         }
25453         
25454         this.haveProgress = true;
25455    
25456         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25457         
25458         var c = new Roo.data.Connection();
25459         c.request({
25460             url : this.form.progressUrl,
25461             params: {
25462                 id : uid
25463             },
25464             method: 'GET',
25465             success : function(req){
25466                //console.log(data);
25467                 var rdata = false;
25468                 var edata;
25469                 try  {
25470                    rdata = Roo.decode(req.responseText)
25471                 } catch (e) {
25472                     Roo.log("Invalid data from server..");
25473                     Roo.log(edata);
25474                     return;
25475                 }
25476                 if (!rdata || !rdata.success) {
25477                     Roo.log(rdata);
25478                     Roo.MessageBox.alert(Roo.encode(rdata));
25479                     return;
25480                 }
25481                 var data = rdata.data;
25482                 
25483                 if (this.uploadComplete) {
25484                    Roo.MessageBox.hide();
25485                    return;
25486                 }
25487                    
25488                 if (data){
25489                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25490                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25491                     );
25492                 }
25493                 this.uploadProgress.defer(2000,this);
25494             },
25495        
25496             failure: function(data) {
25497                 Roo.log('progress url failed ');
25498                 Roo.log(data);
25499             },
25500             scope : this
25501         });
25502            
25503     },
25504     
25505     
25506     run : function()
25507     {
25508         // run get Values on the form, so it syncs any secondary forms.
25509         this.form.getValues();
25510         
25511         var o = this.options;
25512         var method = this.getMethod();
25513         var isPost = method == 'POST';
25514         if(o.clientValidation === false || this.form.isValid()){
25515             
25516             if (this.form.progressUrl) {
25517                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25518                     (new Date() * 1) + '' + Math.random());
25519                     
25520             } 
25521             
25522             
25523             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25524                 form:this.form.el.dom,
25525                 url:this.getUrl(!isPost),
25526                 method: method,
25527                 params:isPost ? this.getParams() : null,
25528                 isUpload: this.form.fileUpload
25529             }));
25530             
25531             this.uploadProgress();
25532
25533         }else if (o.clientValidation !== false){ // client validation failed
25534             this.failureType = Roo.form.Action.CLIENT_INVALID;
25535             this.form.afterAction(this, false);
25536         }
25537     },
25538
25539     success : function(response)
25540     {
25541         this.uploadComplete= true;
25542         if (this.haveProgress) {
25543             Roo.MessageBox.hide();
25544         }
25545         
25546         
25547         var result = this.processResponse(response);
25548         if(result === true || result.success){
25549             this.form.afterAction(this, true);
25550             return;
25551         }
25552         if(result.errors){
25553             this.form.markInvalid(result.errors);
25554             this.failureType = Roo.form.Action.SERVER_INVALID;
25555         }
25556         this.form.afterAction(this, false);
25557     },
25558     failure : function(response)
25559     {
25560         this.uploadComplete= true;
25561         if (this.haveProgress) {
25562             Roo.MessageBox.hide();
25563         }
25564         
25565         this.response = response;
25566         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25567         this.form.afterAction(this, false);
25568     },
25569     
25570     handleResponse : function(response){
25571         if(this.form.errorReader){
25572             var rs = this.form.errorReader.read(response);
25573             var errors = [];
25574             if(rs.records){
25575                 for(var i = 0, len = rs.records.length; i < len; i++) {
25576                     var r = rs.records[i];
25577                     errors[i] = r.data;
25578                 }
25579             }
25580             if(errors.length < 1){
25581                 errors = null;
25582             }
25583             return {
25584                 success : rs.success,
25585                 errors : errors
25586             };
25587         }
25588         var ret = false;
25589         try {
25590             ret = Roo.decode(response.responseText);
25591         } catch (e) {
25592             ret = {
25593                 success: false,
25594                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25595                 errors : []
25596             };
25597         }
25598         return ret;
25599         
25600     }
25601 });
25602
25603
25604 Roo.form.Action.Load = function(form, options){
25605     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25606     this.reader = this.form.reader;
25607 };
25608
25609 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25610     type : 'load',
25611
25612     run : function(){
25613         
25614         Roo.Ajax.request(Roo.apply(
25615                 this.createCallback(), {
25616                     method:this.getMethod(),
25617                     url:this.getUrl(false),
25618                     params:this.getParams()
25619         }));
25620     },
25621
25622     success : function(response){
25623         
25624         var result = this.processResponse(response);
25625         if(result === true || !result.success || !result.data){
25626             this.failureType = Roo.form.Action.LOAD_FAILURE;
25627             this.form.afterAction(this, false);
25628             return;
25629         }
25630         this.form.clearInvalid();
25631         this.form.setValues(result.data);
25632         this.form.afterAction(this, true);
25633     },
25634
25635     handleResponse : function(response){
25636         if(this.form.reader){
25637             var rs = this.form.reader.read(response);
25638             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25639             return {
25640                 success : rs.success,
25641                 data : data
25642             };
25643         }
25644         return Roo.decode(response.responseText);
25645     }
25646 });
25647
25648 Roo.form.Action.ACTION_TYPES = {
25649     'load' : Roo.form.Action.Load,
25650     'submit' : Roo.form.Action.Submit
25651 };/*
25652  * Based on:
25653  * Ext JS Library 1.1.1
25654  * Copyright(c) 2006-2007, Ext JS, LLC.
25655  *
25656  * Originally Released Under LGPL - original licence link has changed is not relivant.
25657  *
25658  * Fork - LGPL
25659  * <script type="text/javascript">
25660  */
25661  
25662 /**
25663  * @class Roo.form.Layout
25664  * @extends Roo.Component
25665  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25666  * @constructor
25667  * @param {Object} config Configuration options
25668  */
25669 Roo.form.Layout = function(config){
25670     var xitems = [];
25671     if (config.items) {
25672         xitems = config.items;
25673         delete config.items;
25674     }
25675     Roo.form.Layout.superclass.constructor.call(this, config);
25676     this.stack = [];
25677     Roo.each(xitems, this.addxtype, this);
25678      
25679 };
25680
25681 Roo.extend(Roo.form.Layout, Roo.Component, {
25682     /**
25683      * @cfg {String/Object} autoCreate
25684      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25685      */
25686     /**
25687      * @cfg {String/Object/Function} style
25688      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25689      * a function which returns such a specification.
25690      */
25691     /**
25692      * @cfg {String} labelAlign
25693      * Valid values are "left," "top" and "right" (defaults to "left")
25694      */
25695     /**
25696      * @cfg {Number} labelWidth
25697      * Fixed width in pixels of all field labels (defaults to undefined)
25698      */
25699     /**
25700      * @cfg {Boolean} clear
25701      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25702      */
25703     clear : true,
25704     /**
25705      * @cfg {String} labelSeparator
25706      * The separator to use after field labels (defaults to ':')
25707      */
25708     labelSeparator : ':',
25709     /**
25710      * @cfg {Boolean} hideLabels
25711      * True to suppress the display of field labels in this layout (defaults to false)
25712      */
25713     hideLabels : false,
25714
25715     // private
25716     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25717     
25718     isLayout : true,
25719     
25720     // private
25721     onRender : function(ct, position){
25722         if(this.el){ // from markup
25723             this.el = Roo.get(this.el);
25724         }else {  // generate
25725             var cfg = this.getAutoCreate();
25726             this.el = ct.createChild(cfg, position);
25727         }
25728         if(this.style){
25729             this.el.applyStyles(this.style);
25730         }
25731         if(this.labelAlign){
25732             this.el.addClass('x-form-label-'+this.labelAlign);
25733         }
25734         if(this.hideLabels){
25735             this.labelStyle = "display:none";
25736             this.elementStyle = "padding-left:0;";
25737         }else{
25738             if(typeof this.labelWidth == 'number'){
25739                 this.labelStyle = "width:"+this.labelWidth+"px;";
25740                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25741             }
25742             if(this.labelAlign == 'top'){
25743                 this.labelStyle = "width:auto;";
25744                 this.elementStyle = "padding-left:0;";
25745             }
25746         }
25747         var stack = this.stack;
25748         var slen = stack.length;
25749         if(slen > 0){
25750             if(!this.fieldTpl){
25751                 var t = new Roo.Template(
25752                     '<div class="x-form-item {5}">',
25753                         '<label for="{0}" style="{2}">{1}{4}</label>',
25754                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25755                         '</div>',
25756                     '</div><div class="x-form-clear-left"></div>'
25757                 );
25758                 t.disableFormats = true;
25759                 t.compile();
25760                 Roo.form.Layout.prototype.fieldTpl = t;
25761             }
25762             for(var i = 0; i < slen; i++) {
25763                 if(stack[i].isFormField){
25764                     this.renderField(stack[i]);
25765                 }else{
25766                     this.renderComponent(stack[i]);
25767                 }
25768             }
25769         }
25770         if(this.clear){
25771             this.el.createChild({cls:'x-form-clear'});
25772         }
25773     },
25774
25775     // private
25776     renderField : function(f){
25777         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25778                f.id, //0
25779                f.fieldLabel, //1
25780                f.labelStyle||this.labelStyle||'', //2
25781                this.elementStyle||'', //3
25782                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25783                f.itemCls||this.itemCls||''  //5
25784        ], true).getPrevSibling());
25785     },
25786
25787     // private
25788     renderComponent : function(c){
25789         c.render(c.isLayout ? this.el : this.el.createChild());    
25790     },
25791     /**
25792      * Adds a object form elements (using the xtype property as the factory method.)
25793      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25794      * @param {Object} config 
25795      */
25796     addxtype : function(o)
25797     {
25798         // create the lement.
25799         o.form = this.form;
25800         var fe = Roo.factory(o, Roo.form);
25801         this.form.allItems.push(fe);
25802         this.stack.push(fe);
25803         
25804         if (fe.isFormField) {
25805             this.form.items.add(fe);
25806         }
25807          
25808         return fe;
25809     }
25810 });
25811
25812 /**
25813  * @class Roo.form.Column
25814  * @extends Roo.form.Layout
25815  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25816  * @constructor
25817  * @param {Object} config Configuration options
25818  */
25819 Roo.form.Column = function(config){
25820     Roo.form.Column.superclass.constructor.call(this, config);
25821 };
25822
25823 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25824     /**
25825      * @cfg {Number/String} width
25826      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25827      */
25828     /**
25829      * @cfg {String/Object} autoCreate
25830      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25831      */
25832
25833     // private
25834     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25835
25836     // private
25837     onRender : function(ct, position){
25838         Roo.form.Column.superclass.onRender.call(this, ct, position);
25839         if(this.width){
25840             this.el.setWidth(this.width);
25841         }
25842     }
25843 });
25844
25845
25846 /**
25847  * @class Roo.form.Row
25848  * @extends Roo.form.Layout
25849  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25850  * @constructor
25851  * @param {Object} config Configuration options
25852  */
25853
25854  
25855 Roo.form.Row = function(config){
25856     Roo.form.Row.superclass.constructor.call(this, config);
25857 };
25858  
25859 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25860       /**
25861      * @cfg {Number/String} width
25862      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25863      */
25864     /**
25865      * @cfg {Number/String} height
25866      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25867      */
25868     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25869     
25870     padWidth : 20,
25871     // private
25872     onRender : function(ct, position){
25873         //console.log('row render');
25874         if(!this.rowTpl){
25875             var t = new Roo.Template(
25876                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25877                     '<label for="{0}" style="{2}">{1}{4}</label>',
25878                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25879                     '</div>',
25880                 '</div>'
25881             );
25882             t.disableFormats = true;
25883             t.compile();
25884             Roo.form.Layout.prototype.rowTpl = t;
25885         }
25886         this.fieldTpl = this.rowTpl;
25887         
25888         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25889         var labelWidth = 100;
25890         
25891         if ((this.labelAlign != 'top')) {
25892             if (typeof this.labelWidth == 'number') {
25893                 labelWidth = this.labelWidth
25894             }
25895             this.padWidth =  20 + labelWidth;
25896             
25897         }
25898         
25899         Roo.form.Column.superclass.onRender.call(this, ct, position);
25900         if(this.width){
25901             this.el.setWidth(this.width);
25902         }
25903         if(this.height){
25904             this.el.setHeight(this.height);
25905         }
25906     },
25907     
25908     // private
25909     renderField : function(f){
25910         f.fieldEl = this.fieldTpl.append(this.el, [
25911                f.id, f.fieldLabel,
25912                f.labelStyle||this.labelStyle||'',
25913                this.elementStyle||'',
25914                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25915                f.itemCls||this.itemCls||'',
25916                f.width ? f.width + this.padWidth : 160 + this.padWidth
25917        ],true);
25918     }
25919 });
25920  
25921
25922 /**
25923  * @class Roo.form.FieldSet
25924  * @extends Roo.form.Layout
25925  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25926  * @constructor
25927  * @param {Object} config Configuration options
25928  */
25929 Roo.form.FieldSet = function(config){
25930     Roo.form.FieldSet.superclass.constructor.call(this, config);
25931 };
25932
25933 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25934     /**
25935      * @cfg {String} legend
25936      * The text to display as the legend for the FieldSet (defaults to '')
25937      */
25938     /**
25939      * @cfg {String/Object} autoCreate
25940      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25941      */
25942
25943     // private
25944     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25945
25946     // private
25947     onRender : function(ct, position){
25948         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25949         if(this.legend){
25950             this.setLegend(this.legend);
25951         }
25952     },
25953
25954     // private
25955     setLegend : function(text){
25956         if(this.rendered){
25957             this.el.child('legend').update(text);
25958         }
25959     }
25960 });/*
25961  * Based on:
25962  * Ext JS Library 1.1.1
25963  * Copyright(c) 2006-2007, Ext JS, LLC.
25964  *
25965  * Originally Released Under LGPL - original licence link has changed is not relivant.
25966  *
25967  * Fork - LGPL
25968  * <script type="text/javascript">
25969  */
25970 /**
25971  * @class Roo.form.VTypes
25972  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
25973  * @singleton
25974  */
25975 Roo.form.VTypes = function(){
25976     // closure these in so they are only created once.
25977     var alpha = /^[a-zA-Z_]+$/;
25978     var alphanum = /^[a-zA-Z0-9_]+$/;
25979     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
25980     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
25981
25982     // All these messages and functions are configurable
25983     return {
25984         /**
25985          * The function used to validate email addresses
25986          * @param {String} value The email address
25987          */
25988         'email' : function(v){
25989             return email.test(v);
25990         },
25991         /**
25992          * The error text to display when the email validation function returns false
25993          * @type String
25994          */
25995         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
25996         /**
25997          * The keystroke filter mask to be applied on email input
25998          * @type RegExp
25999          */
26000         'emailMask' : /[a-z0-9_\.\-@]/i,
26001
26002         /**
26003          * The function used to validate URLs
26004          * @param {String} value The URL
26005          */
26006         'url' : function(v){
26007             return url.test(v);
26008         },
26009         /**
26010          * The error text to display when the url validation function returns false
26011          * @type String
26012          */
26013         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26014         
26015         /**
26016          * The function used to validate alpha values
26017          * @param {String} value The value
26018          */
26019         'alpha' : function(v){
26020             return alpha.test(v);
26021         },
26022         /**
26023          * The error text to display when the alpha validation function returns false
26024          * @type String
26025          */
26026         'alphaText' : 'This field should only contain letters and _',
26027         /**
26028          * The keystroke filter mask to be applied on alpha input
26029          * @type RegExp
26030          */
26031         'alphaMask' : /[a-z_]/i,
26032
26033         /**
26034          * The function used to validate alphanumeric values
26035          * @param {String} value The value
26036          */
26037         'alphanum' : function(v){
26038             return alphanum.test(v);
26039         },
26040         /**
26041          * The error text to display when the alphanumeric validation function returns false
26042          * @type String
26043          */
26044         'alphanumText' : 'This field should only contain letters, numbers and _',
26045         /**
26046          * The keystroke filter mask to be applied on alphanumeric input
26047          * @type RegExp
26048          */
26049         'alphanumMask' : /[a-z0-9_]/i
26050     };
26051 }();//<script type="text/javascript">
26052
26053 /**
26054  * @class Roo.form.FCKeditor
26055  * @extends Roo.form.TextArea
26056  * Wrapper around the FCKEditor http://www.fckeditor.net
26057  * @constructor
26058  * Creates a new FCKeditor
26059  * @param {Object} config Configuration options
26060  */
26061 Roo.form.FCKeditor = function(config){
26062     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26063     this.addEvents({
26064          /**
26065          * @event editorinit
26066          * Fired when the editor is initialized - you can add extra handlers here..
26067          * @param {FCKeditor} this
26068          * @param {Object} the FCK object.
26069          */
26070         editorinit : true
26071     });
26072     
26073     
26074 };
26075 Roo.form.FCKeditor.editors = { };
26076 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26077 {
26078     //defaultAutoCreate : {
26079     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26080     //},
26081     // private
26082     /**
26083      * @cfg {Object} fck options - see fck manual for details.
26084      */
26085     fckconfig : false,
26086     
26087     /**
26088      * @cfg {Object} fck toolbar set (Basic or Default)
26089      */
26090     toolbarSet : 'Basic',
26091     /**
26092      * @cfg {Object} fck BasePath
26093      */ 
26094     basePath : '/fckeditor/',
26095     
26096     
26097     frame : false,
26098     
26099     value : '',
26100     
26101    
26102     onRender : function(ct, position)
26103     {
26104         if(!this.el){
26105             this.defaultAutoCreate = {
26106                 tag: "textarea",
26107                 style:"width:300px;height:60px;",
26108                 autocomplete: "new-password"
26109             };
26110         }
26111         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26112         /*
26113         if(this.grow){
26114             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26115             if(this.preventScrollbars){
26116                 this.el.setStyle("overflow", "hidden");
26117             }
26118             this.el.setHeight(this.growMin);
26119         }
26120         */
26121         //console.log('onrender' + this.getId() );
26122         Roo.form.FCKeditor.editors[this.getId()] = this;
26123          
26124
26125         this.replaceTextarea() ;
26126         
26127     },
26128     
26129     getEditor : function() {
26130         return this.fckEditor;
26131     },
26132     /**
26133      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26134      * @param {Mixed} value The value to set
26135      */
26136     
26137     
26138     setValue : function(value)
26139     {
26140         //console.log('setValue: ' + value);
26141         
26142         if(typeof(value) == 'undefined') { // not sure why this is happending...
26143             return;
26144         }
26145         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26146         
26147         //if(!this.el || !this.getEditor()) {
26148         //    this.value = value;
26149             //this.setValue.defer(100,this,[value]);    
26150         //    return;
26151         //} 
26152         
26153         if(!this.getEditor()) {
26154             return;
26155         }
26156         
26157         this.getEditor().SetData(value);
26158         
26159         //
26160
26161     },
26162
26163     /**
26164      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26165      * @return {Mixed} value The field value
26166      */
26167     getValue : function()
26168     {
26169         
26170         if (this.frame && this.frame.dom.style.display == 'none') {
26171             return Roo.form.FCKeditor.superclass.getValue.call(this);
26172         }
26173         
26174         if(!this.el || !this.getEditor()) {
26175            
26176            // this.getValue.defer(100,this); 
26177             return this.value;
26178         }
26179        
26180         
26181         var value=this.getEditor().GetData();
26182         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26183         return Roo.form.FCKeditor.superclass.getValue.call(this);
26184         
26185
26186     },
26187
26188     /**
26189      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26190      * @return {Mixed} value The field value
26191      */
26192     getRawValue : function()
26193     {
26194         if (this.frame && this.frame.dom.style.display == 'none') {
26195             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26196         }
26197         
26198         if(!this.el || !this.getEditor()) {
26199             //this.getRawValue.defer(100,this); 
26200             return this.value;
26201             return;
26202         }
26203         
26204         
26205         
26206         var value=this.getEditor().GetData();
26207         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26208         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26209          
26210     },
26211     
26212     setSize : function(w,h) {
26213         
26214         
26215         
26216         //if (this.frame && this.frame.dom.style.display == 'none') {
26217         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26218         //    return;
26219         //}
26220         //if(!this.el || !this.getEditor()) {
26221         //    this.setSize.defer(100,this, [w,h]); 
26222         //    return;
26223         //}
26224         
26225         
26226         
26227         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26228         
26229         this.frame.dom.setAttribute('width', w);
26230         this.frame.dom.setAttribute('height', h);
26231         this.frame.setSize(w,h);
26232         
26233     },
26234     
26235     toggleSourceEdit : function(value) {
26236         
26237       
26238          
26239         this.el.dom.style.display = value ? '' : 'none';
26240         this.frame.dom.style.display = value ?  'none' : '';
26241         
26242     },
26243     
26244     
26245     focus: function(tag)
26246     {
26247         if (this.frame.dom.style.display == 'none') {
26248             return Roo.form.FCKeditor.superclass.focus.call(this);
26249         }
26250         if(!this.el || !this.getEditor()) {
26251             this.focus.defer(100,this, [tag]); 
26252             return;
26253         }
26254         
26255         
26256         
26257         
26258         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26259         this.getEditor().Focus();
26260         if (tgs.length) {
26261             if (!this.getEditor().Selection.GetSelection()) {
26262                 this.focus.defer(100,this, [tag]); 
26263                 return;
26264             }
26265             
26266             
26267             var r = this.getEditor().EditorDocument.createRange();
26268             r.setStart(tgs[0],0);
26269             r.setEnd(tgs[0],0);
26270             this.getEditor().Selection.GetSelection().removeAllRanges();
26271             this.getEditor().Selection.GetSelection().addRange(r);
26272             this.getEditor().Focus();
26273         }
26274         
26275     },
26276     
26277     
26278     
26279     replaceTextarea : function()
26280     {
26281         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26282             return ;
26283         }
26284         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26285         //{
26286             // We must check the elements firstly using the Id and then the name.
26287         var oTextarea = document.getElementById( this.getId() );
26288         
26289         var colElementsByName = document.getElementsByName( this.getId() ) ;
26290          
26291         oTextarea.style.display = 'none' ;
26292
26293         if ( oTextarea.tabIndex ) {            
26294             this.TabIndex = oTextarea.tabIndex ;
26295         }
26296         
26297         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26298         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26299         this.frame = Roo.get(this.getId() + '___Frame')
26300     },
26301     
26302     _getConfigHtml : function()
26303     {
26304         var sConfig = '' ;
26305
26306         for ( var o in this.fckconfig ) {
26307             sConfig += sConfig.length > 0  ? '&amp;' : '';
26308             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26309         }
26310
26311         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26312     },
26313     
26314     
26315     _getIFrameHtml : function()
26316     {
26317         var sFile = 'fckeditor.html' ;
26318         /* no idea what this is about..
26319         try
26320         {
26321             if ( (/fcksource=true/i).test( window.top.location.search ) )
26322                 sFile = 'fckeditor.original.html' ;
26323         }
26324         catch (e) { 
26325         */
26326
26327         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26328         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26329         
26330         
26331         var html = '<iframe id="' + this.getId() +
26332             '___Frame" src="' + sLink +
26333             '" width="' + this.width +
26334             '" height="' + this.height + '"' +
26335             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26336             ' frameborder="0" scrolling="no"></iframe>' ;
26337
26338         return html ;
26339     },
26340     
26341     _insertHtmlBefore : function( html, element )
26342     {
26343         if ( element.insertAdjacentHTML )       {
26344             // IE
26345             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26346         } else { // Gecko
26347             var oRange = document.createRange() ;
26348             oRange.setStartBefore( element ) ;
26349             var oFragment = oRange.createContextualFragment( html );
26350             element.parentNode.insertBefore( oFragment, element ) ;
26351         }
26352     }
26353     
26354     
26355   
26356     
26357     
26358     
26359     
26360
26361 });
26362
26363 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26364
26365 function FCKeditor_OnComplete(editorInstance){
26366     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26367     f.fckEditor = editorInstance;
26368     //console.log("loaded");
26369     f.fireEvent('editorinit', f, editorInstance);
26370
26371   
26372
26373  
26374
26375
26376
26377
26378
26379
26380
26381
26382
26383
26384
26385
26386
26387
26388
26389 //<script type="text/javascript">
26390 /**
26391  * @class Roo.form.GridField
26392  * @extends Roo.form.Field
26393  * Embed a grid (or editable grid into a form)
26394  * STATUS ALPHA
26395  * 
26396  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26397  * it needs 
26398  * xgrid.store = Roo.data.Store
26399  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26400  * xgrid.store.reader = Roo.data.JsonReader 
26401  * 
26402  * 
26403  * @constructor
26404  * Creates a new GridField
26405  * @param {Object} config Configuration options
26406  */
26407 Roo.form.GridField = function(config){
26408     Roo.form.GridField.superclass.constructor.call(this, config);
26409      
26410 };
26411
26412 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26413     /**
26414      * @cfg {Number} width  - used to restrict width of grid..
26415      */
26416     width : 100,
26417     /**
26418      * @cfg {Number} height - used to restrict height of grid..
26419      */
26420     height : 50,
26421      /**
26422      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26423          * 
26424          *}
26425      */
26426     xgrid : false, 
26427     /**
26428      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26429      * {tag: "input", type: "checkbox", autocomplete: "off"})
26430      */
26431    // defaultAutoCreate : { tag: 'div' },
26432     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26433     /**
26434      * @cfg {String} addTitle Text to include for adding a title.
26435      */
26436     addTitle : false,
26437     //
26438     onResize : function(){
26439         Roo.form.Field.superclass.onResize.apply(this, arguments);
26440     },
26441
26442     initEvents : function(){
26443         // Roo.form.Checkbox.superclass.initEvents.call(this);
26444         // has no events...
26445        
26446     },
26447
26448
26449     getResizeEl : function(){
26450         return this.wrap;
26451     },
26452
26453     getPositionEl : function(){
26454         return this.wrap;
26455     },
26456
26457     // private
26458     onRender : function(ct, position){
26459         
26460         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26461         var style = this.style;
26462         delete this.style;
26463         
26464         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26465         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26466         this.viewEl = this.wrap.createChild({ tag: 'div' });
26467         if (style) {
26468             this.viewEl.applyStyles(style);
26469         }
26470         if (this.width) {
26471             this.viewEl.setWidth(this.width);
26472         }
26473         if (this.height) {
26474             this.viewEl.setHeight(this.height);
26475         }
26476         //if(this.inputValue !== undefined){
26477         //this.setValue(this.value);
26478         
26479         
26480         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26481         
26482         
26483         this.grid.render();
26484         this.grid.getDataSource().on('remove', this.refreshValue, this);
26485         this.grid.getDataSource().on('update', this.refreshValue, this);
26486         this.grid.on('afteredit', this.refreshValue, this);
26487  
26488     },
26489      
26490     
26491     /**
26492      * Sets the value of the item. 
26493      * @param {String} either an object  or a string..
26494      */
26495     setValue : function(v){
26496         //this.value = v;
26497         v = v || []; // empty set..
26498         // this does not seem smart - it really only affects memoryproxy grids..
26499         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26500             var ds = this.grid.getDataSource();
26501             // assumes a json reader..
26502             var data = {}
26503             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26504             ds.loadData( data);
26505         }
26506         // clear selection so it does not get stale.
26507         if (this.grid.sm) { 
26508             this.grid.sm.clearSelections();
26509         }
26510         
26511         Roo.form.GridField.superclass.setValue.call(this, v);
26512         this.refreshValue();
26513         // should load data in the grid really....
26514     },
26515     
26516     // private
26517     refreshValue: function() {
26518          var val = [];
26519         this.grid.getDataSource().each(function(r) {
26520             val.push(r.data);
26521         });
26522         this.el.dom.value = Roo.encode(val);
26523     }
26524     
26525      
26526     
26527     
26528 });/*
26529  * Based on:
26530  * Ext JS Library 1.1.1
26531  * Copyright(c) 2006-2007, Ext JS, LLC.
26532  *
26533  * Originally Released Under LGPL - original licence link has changed is not relivant.
26534  *
26535  * Fork - LGPL
26536  * <script type="text/javascript">
26537  */
26538 /**
26539  * @class Roo.form.DisplayField
26540  * @extends Roo.form.Field
26541  * A generic Field to display non-editable data.
26542  * @cfg {Boolean} closable (true|false) default false
26543  * @constructor
26544  * Creates a new Display Field item.
26545  * @param {Object} config Configuration options
26546  */
26547 Roo.form.DisplayField = function(config){
26548     Roo.form.DisplayField.superclass.constructor.call(this, config);
26549     
26550     this.addEvents({
26551         /**
26552          * @event close
26553          * Fires after the click the close btn
26554              * @param {Roo.form.DisplayField} this
26555              */
26556         close : true
26557     });
26558 };
26559
26560 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26561     inputType:      'hidden',
26562     allowBlank:     true,
26563     readOnly:         true,
26564     
26565  
26566     /**
26567      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26568      */
26569     focusClass : undefined,
26570     /**
26571      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26572      */
26573     fieldClass: 'x-form-field',
26574     
26575      /**
26576      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26577      */
26578     valueRenderer: undefined,
26579     
26580     width: 100,
26581     /**
26582      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26583      * {tag: "input", type: "checkbox", autocomplete: "off"})
26584      */
26585      
26586  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26587  
26588     closable : false,
26589     
26590     onResize : function(){
26591         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26592         
26593     },
26594
26595     initEvents : function(){
26596         // Roo.form.Checkbox.superclass.initEvents.call(this);
26597         // has no events...
26598         
26599         if(this.closable){
26600             this.closeEl.on('click', this.onClose, this);
26601         }
26602        
26603     },
26604
26605
26606     getResizeEl : function(){
26607         return this.wrap;
26608     },
26609
26610     getPositionEl : function(){
26611         return this.wrap;
26612     },
26613
26614     // private
26615     onRender : function(ct, position){
26616         
26617         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26618         //if(this.inputValue !== undefined){
26619         this.wrap = this.el.wrap();
26620         
26621         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26622         
26623         if(this.closable){
26624             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26625         }
26626         
26627         if (this.bodyStyle) {
26628             this.viewEl.applyStyles(this.bodyStyle);
26629         }
26630         //this.viewEl.setStyle('padding', '2px');
26631         
26632         this.setValue(this.value);
26633         
26634     },
26635 /*
26636     // private
26637     initValue : Roo.emptyFn,
26638
26639   */
26640
26641         // private
26642     onClick : function(){
26643         
26644     },
26645
26646     /**
26647      * Sets the checked state of the checkbox.
26648      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26649      */
26650     setValue : function(v){
26651         this.value = v;
26652         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26653         // this might be called before we have a dom element..
26654         if (!this.viewEl) {
26655             return;
26656         }
26657         this.viewEl.dom.innerHTML = html;
26658         Roo.form.DisplayField.superclass.setValue.call(this, v);
26659
26660     },
26661     
26662     onClose : function(e)
26663     {
26664         e.preventDefault();
26665         
26666         this.fireEvent('close', this);
26667     }
26668 });/*
26669  * 
26670  * Licence- LGPL
26671  * 
26672  */
26673
26674 /**
26675  * @class Roo.form.DayPicker
26676  * @extends Roo.form.Field
26677  * A Day picker show [M] [T] [W] ....
26678  * @constructor
26679  * Creates a new Day Picker
26680  * @param {Object} config Configuration options
26681  */
26682 Roo.form.DayPicker= function(config){
26683     Roo.form.DayPicker.superclass.constructor.call(this, config);
26684      
26685 };
26686
26687 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26688     /**
26689      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26690      */
26691     focusClass : undefined,
26692     /**
26693      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26694      */
26695     fieldClass: "x-form-field",
26696    
26697     /**
26698      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26699      * {tag: "input", type: "checkbox", autocomplete: "off"})
26700      */
26701     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26702     
26703    
26704     actionMode : 'viewEl', 
26705     //
26706     // private
26707  
26708     inputType : 'hidden',
26709     
26710      
26711     inputElement: false, // real input element?
26712     basedOn: false, // ????
26713     
26714     isFormField: true, // not sure where this is needed!!!!
26715
26716     onResize : function(){
26717         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26718         if(!this.boxLabel){
26719             this.el.alignTo(this.wrap, 'c-c');
26720         }
26721     },
26722
26723     initEvents : function(){
26724         Roo.form.Checkbox.superclass.initEvents.call(this);
26725         this.el.on("click", this.onClick,  this);
26726         this.el.on("change", this.onClick,  this);
26727     },
26728
26729
26730     getResizeEl : function(){
26731         return this.wrap;
26732     },
26733
26734     getPositionEl : function(){
26735         return this.wrap;
26736     },
26737
26738     
26739     // private
26740     onRender : function(ct, position){
26741         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26742        
26743         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26744         
26745         var r1 = '<table><tr>';
26746         var r2 = '<tr class="x-form-daypick-icons">';
26747         for (var i=0; i < 7; i++) {
26748             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26749             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26750         }
26751         
26752         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26753         viewEl.select('img').on('click', this.onClick, this);
26754         this.viewEl = viewEl;   
26755         
26756         
26757         // this will not work on Chrome!!!
26758         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26759         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26760         
26761         
26762           
26763
26764     },
26765
26766     // private
26767     initValue : Roo.emptyFn,
26768
26769     /**
26770      * Returns the checked state of the checkbox.
26771      * @return {Boolean} True if checked, else false
26772      */
26773     getValue : function(){
26774         return this.el.dom.value;
26775         
26776     },
26777
26778         // private
26779     onClick : function(e){ 
26780         //this.setChecked(!this.checked);
26781         Roo.get(e.target).toggleClass('x-menu-item-checked');
26782         this.refreshValue();
26783         //if(this.el.dom.checked != this.checked){
26784         //    this.setValue(this.el.dom.checked);
26785        // }
26786     },
26787     
26788     // private
26789     refreshValue : function()
26790     {
26791         var val = '';
26792         this.viewEl.select('img',true).each(function(e,i,n)  {
26793             val += e.is(".x-menu-item-checked") ? String(n) : '';
26794         });
26795         this.setValue(val, true);
26796     },
26797
26798     /**
26799      * Sets the checked state of the checkbox.
26800      * On is always based on a string comparison between inputValue and the param.
26801      * @param {Boolean/String} value - the value to set 
26802      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26803      */
26804     setValue : function(v,suppressEvent){
26805         if (!this.el.dom) {
26806             return;
26807         }
26808         var old = this.el.dom.value ;
26809         this.el.dom.value = v;
26810         if (suppressEvent) {
26811             return ;
26812         }
26813          
26814         // update display..
26815         this.viewEl.select('img',true).each(function(e,i,n)  {
26816             
26817             var on = e.is(".x-menu-item-checked");
26818             var newv = v.indexOf(String(n)) > -1;
26819             if (on != newv) {
26820                 e.toggleClass('x-menu-item-checked');
26821             }
26822             
26823         });
26824         
26825         
26826         this.fireEvent('change', this, v, old);
26827         
26828         
26829     },
26830    
26831     // handle setting of hidden value by some other method!!?!?
26832     setFromHidden: function()
26833     {
26834         if(!this.el){
26835             return;
26836         }
26837         //console.log("SET FROM HIDDEN");
26838         //alert('setFrom hidden');
26839         this.setValue(this.el.dom.value);
26840     },
26841     
26842     onDestroy : function()
26843     {
26844         if(this.viewEl){
26845             Roo.get(this.viewEl).remove();
26846         }
26847          
26848         Roo.form.DayPicker.superclass.onDestroy.call(this);
26849     }
26850
26851 });/*
26852  * RooJS Library 1.1.1
26853  * Copyright(c) 2008-2011  Alan Knowles
26854  *
26855  * License - LGPL
26856  */
26857  
26858
26859 /**
26860  * @class Roo.form.ComboCheck
26861  * @extends Roo.form.ComboBox
26862  * A combobox for multiple select items.
26863  *
26864  * FIXME - could do with a reset button..
26865  * 
26866  * @constructor
26867  * Create a new ComboCheck
26868  * @param {Object} config Configuration options
26869  */
26870 Roo.form.ComboCheck = function(config){
26871     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26872     // should verify some data...
26873     // like
26874     // hiddenName = required..
26875     // displayField = required
26876     // valudField == required
26877     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26878     var _t = this;
26879     Roo.each(req, function(e) {
26880         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26881             throw "Roo.form.ComboCheck : missing value for: " + e;
26882         }
26883     });
26884     
26885     
26886 };
26887
26888 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26889      
26890      
26891     editable : false,
26892      
26893     selectedClass: 'x-menu-item-checked', 
26894     
26895     // private
26896     onRender : function(ct, position){
26897         var _t = this;
26898         
26899         
26900         
26901         if(!this.tpl){
26902             var cls = 'x-combo-list';
26903
26904             
26905             this.tpl =  new Roo.Template({
26906                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26907                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26908                    '<span>{' + this.displayField + '}</span>' +
26909                     '</div>' 
26910                 
26911             });
26912         }
26913  
26914         
26915         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26916         this.view.singleSelect = false;
26917         this.view.multiSelect = true;
26918         this.view.toggleSelect = true;
26919         this.pageTb.add(new Roo.Toolbar.Fill(), {
26920             
26921             text: 'Done',
26922             handler: function()
26923             {
26924                 _t.collapse();
26925             }
26926         });
26927     },
26928     
26929     onViewOver : function(e, t){
26930         // do nothing...
26931         return;
26932         
26933     },
26934     
26935     onViewClick : function(doFocus,index){
26936         return;
26937         
26938     },
26939     select: function () {
26940         //Roo.log("SELECT CALLED");
26941     },
26942      
26943     selectByValue : function(xv, scrollIntoView){
26944         var ar = this.getValueArray();
26945         var sels = [];
26946         
26947         Roo.each(ar, function(v) {
26948             if(v === undefined || v === null){
26949                 return;
26950             }
26951             var r = this.findRecord(this.valueField, v);
26952             if(r){
26953                 sels.push(this.store.indexOf(r))
26954                 
26955             }
26956         },this);
26957         this.view.select(sels);
26958         return false;
26959     },
26960     
26961     
26962     
26963     onSelect : function(record, index){
26964        // Roo.log("onselect Called");
26965        // this is only called by the clear button now..
26966         this.view.clearSelections();
26967         this.setValue('[]');
26968         if (this.value != this.valueBefore) {
26969             this.fireEvent('change', this, this.value, this.valueBefore);
26970             this.valueBefore = this.value;
26971         }
26972     },
26973     getValueArray : function()
26974     {
26975         var ar = [] ;
26976         
26977         try {
26978             //Roo.log(this.value);
26979             if (typeof(this.value) == 'undefined') {
26980                 return [];
26981             }
26982             var ar = Roo.decode(this.value);
26983             return  ar instanceof Array ? ar : []; //?? valid?
26984             
26985         } catch(e) {
26986             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
26987             return [];
26988         }
26989          
26990     },
26991     expand : function ()
26992     {
26993         
26994         Roo.form.ComboCheck.superclass.expand.call(this);
26995         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
26996         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
26997         
26998
26999     },
27000     
27001     collapse : function(){
27002         Roo.form.ComboCheck.superclass.collapse.call(this);
27003         var sl = this.view.getSelectedIndexes();
27004         var st = this.store;
27005         var nv = [];
27006         var tv = [];
27007         var r;
27008         Roo.each(sl, function(i) {
27009             r = st.getAt(i);
27010             nv.push(r.get(this.valueField));
27011         },this);
27012         this.setValue(Roo.encode(nv));
27013         if (this.value != this.valueBefore) {
27014
27015             this.fireEvent('change', this, this.value, this.valueBefore);
27016             this.valueBefore = this.value;
27017         }
27018         
27019     },
27020     
27021     setValue : function(v){
27022         // Roo.log(v);
27023         this.value = v;
27024         
27025         var vals = this.getValueArray();
27026         var tv = [];
27027         Roo.each(vals, function(k) {
27028             var r = this.findRecord(this.valueField, k);
27029             if(r){
27030                 tv.push(r.data[this.displayField]);
27031             }else if(this.valueNotFoundText !== undefined){
27032                 tv.push( this.valueNotFoundText );
27033             }
27034         },this);
27035        // Roo.log(tv);
27036         
27037         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27038         this.hiddenField.value = v;
27039         this.value = v;
27040     }
27041     
27042 });/*
27043  * Based on:
27044  * Ext JS Library 1.1.1
27045  * Copyright(c) 2006-2007, Ext JS, LLC.
27046  *
27047  * Originally Released Under LGPL - original licence link has changed is not relivant.
27048  *
27049  * Fork - LGPL
27050  * <script type="text/javascript">
27051  */
27052  
27053 /**
27054  * @class Roo.form.Signature
27055  * @extends Roo.form.Field
27056  * Signature field.  
27057  * @constructor
27058  * 
27059  * @param {Object} config Configuration options
27060  */
27061
27062 Roo.form.Signature = function(config){
27063     Roo.form.Signature.superclass.constructor.call(this, config);
27064     
27065     this.addEvents({// not in used??
27066          /**
27067          * @event confirm
27068          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27069              * @param {Roo.form.Signature} combo This combo box
27070              */
27071         'confirm' : true,
27072         /**
27073          * @event reset
27074          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27075              * @param {Roo.form.ComboBox} combo This combo box
27076              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27077              */
27078         'reset' : true
27079     });
27080 };
27081
27082 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27083     /**
27084      * @cfg {Object} labels Label to use when rendering a form.
27085      * defaults to 
27086      * labels : { 
27087      *      clear : "Clear",
27088      *      confirm : "Confirm"
27089      *  }
27090      */
27091     labels : { 
27092         clear : "Clear",
27093         confirm : "Confirm"
27094     },
27095     /**
27096      * @cfg {Number} width The signature panel width (defaults to 300)
27097      */
27098     width: 300,
27099     /**
27100      * @cfg {Number} height The signature panel height (defaults to 100)
27101      */
27102     height : 100,
27103     /**
27104      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27105      */
27106     allowBlank : false,
27107     
27108     //private
27109     // {Object} signPanel The signature SVG panel element (defaults to {})
27110     signPanel : {},
27111     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27112     isMouseDown : false,
27113     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27114     isConfirmed : false,
27115     // {String} signatureTmp SVG mapping string (defaults to empty string)
27116     signatureTmp : '',
27117     
27118     
27119     defaultAutoCreate : { // modified by initCompnoent..
27120         tag: "input",
27121         type:"hidden"
27122     },
27123
27124     // private
27125     onRender : function(ct, position){
27126         
27127         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27128         
27129         this.wrap = this.el.wrap({
27130             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27131         });
27132         
27133         this.createToolbar(this);
27134         this.signPanel = this.wrap.createChild({
27135                 tag: 'div',
27136                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27137             }, this.el
27138         );
27139             
27140         this.svgID = Roo.id();
27141         this.svgEl = this.signPanel.createChild({
27142               xmlns : 'http://www.w3.org/2000/svg',
27143               tag : 'svg',
27144               id : this.svgID + "-svg",
27145               width: this.width,
27146               height: this.height,
27147               viewBox: '0 0 '+this.width+' '+this.height,
27148               cn : [
27149                 {
27150                     tag: "rect",
27151                     id: this.svgID + "-svg-r",
27152                     width: this.width,
27153                     height: this.height,
27154                     fill: "#ffa"
27155                 },
27156                 {
27157                     tag: "line",
27158                     id: this.svgID + "-svg-l",
27159                     x1: "0", // start
27160                     y1: (this.height*0.8), // start set the line in 80% of height
27161                     x2: this.width, // end
27162                     y2: (this.height*0.8), // end set the line in 80% of height
27163                     'stroke': "#666",
27164                     'stroke-width': "1",
27165                     'stroke-dasharray': "3",
27166                     'shape-rendering': "crispEdges",
27167                     'pointer-events': "none"
27168                 },
27169                 {
27170                     tag: "path",
27171                     id: this.svgID + "-svg-p",
27172                     'stroke': "navy",
27173                     'stroke-width': "3",
27174                     'fill': "none",
27175                     'pointer-events': 'none'
27176                 }
27177               ]
27178         });
27179         this.createSVG();
27180         this.svgBox = this.svgEl.dom.getScreenCTM();
27181     },
27182     createSVG : function(){ 
27183         var svg = this.signPanel;
27184         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27185         var t = this;
27186
27187         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27188         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27189         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27190         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27191         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27192         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27193         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27194         
27195     },
27196     isTouchEvent : function(e){
27197         return e.type.match(/^touch/);
27198     },
27199     getCoords : function (e) {
27200         var pt    = this.svgEl.dom.createSVGPoint();
27201         pt.x = e.clientX; 
27202         pt.y = e.clientY;
27203         if (this.isTouchEvent(e)) {
27204             pt.x =  e.targetTouches[0].clientX;
27205             pt.y = e.targetTouches[0].clientY;
27206         }
27207         var a = this.svgEl.dom.getScreenCTM();
27208         var b = a.inverse();
27209         var mx = pt.matrixTransform(b);
27210         return mx.x + ',' + mx.y;
27211     },
27212     //mouse event headler 
27213     down : function (e) {
27214         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27215         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27216         
27217         this.isMouseDown = true;
27218         
27219         e.preventDefault();
27220     },
27221     move : function (e) {
27222         if (this.isMouseDown) {
27223             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27224             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27225         }
27226         
27227         e.preventDefault();
27228     },
27229     up : function (e) {
27230         this.isMouseDown = false;
27231         var sp = this.signatureTmp.split(' ');
27232         
27233         if(sp.length > 1){
27234             if(!sp[sp.length-2].match(/^L/)){
27235                 sp.pop();
27236                 sp.pop();
27237                 sp.push("");
27238                 this.signatureTmp = sp.join(" ");
27239             }
27240         }
27241         if(this.getValue() != this.signatureTmp){
27242             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27243             this.isConfirmed = false;
27244         }
27245         e.preventDefault();
27246     },
27247     
27248     /**
27249      * Protected method that will not generally be called directly. It
27250      * is called when the editor creates its toolbar. Override this method if you need to
27251      * add custom toolbar buttons.
27252      * @param {HtmlEditor} editor
27253      */
27254     createToolbar : function(editor){
27255          function btn(id, toggle, handler){
27256             var xid = fid + '-'+ id ;
27257             return {
27258                 id : xid,
27259                 cmd : id,
27260                 cls : 'x-btn-icon x-edit-'+id,
27261                 enableToggle:toggle !== false,
27262                 scope: editor, // was editor...
27263                 handler:handler||editor.relayBtnCmd,
27264                 clickEvent:'mousedown',
27265                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27266                 tabIndex:-1
27267             };
27268         }
27269         
27270         
27271         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27272         this.tb = tb;
27273         this.tb.add(
27274            {
27275                 cls : ' x-signature-btn x-signature-'+id,
27276                 scope: editor, // was editor...
27277                 handler: this.reset,
27278                 clickEvent:'mousedown',
27279                 text: this.labels.clear
27280             },
27281             {
27282                  xtype : 'Fill',
27283                  xns: Roo.Toolbar
27284             }, 
27285             {
27286                 cls : '  x-signature-btn x-signature-'+id,
27287                 scope: editor, // was editor...
27288                 handler: this.confirmHandler,
27289                 clickEvent:'mousedown',
27290                 text: this.labels.confirm
27291             }
27292         );
27293     
27294     },
27295     //public
27296     /**
27297      * when user is clicked confirm then show this image.....
27298      * 
27299      * @return {String} Image Data URI
27300      */
27301     getImageDataURI : function(){
27302         var svg = this.svgEl.dom.parentNode.innerHTML;
27303         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27304         return src; 
27305     },
27306     /**
27307      * 
27308      * @return {Boolean} this.isConfirmed
27309      */
27310     getConfirmed : function(){
27311         return this.isConfirmed;
27312     },
27313     /**
27314      * 
27315      * @return {Number} this.width
27316      */
27317     getWidth : function(){
27318         return this.width;
27319     },
27320     /**
27321      * 
27322      * @return {Number} this.height
27323      */
27324     getHeight : function(){
27325         return this.height;
27326     },
27327     // private
27328     getSignature : function(){
27329         return this.signatureTmp;
27330     },
27331     // private
27332     reset : function(){
27333         this.signatureTmp = '';
27334         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27335         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27336         this.isConfirmed = false;
27337         Roo.form.Signature.superclass.reset.call(this);
27338     },
27339     setSignature : function(s){
27340         this.signatureTmp = s;
27341         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27342         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27343         this.setValue(s);
27344         this.isConfirmed = false;
27345         Roo.form.Signature.superclass.reset.call(this);
27346     }, 
27347     test : function(){
27348 //        Roo.log(this.signPanel.dom.contentWindow.up())
27349     },
27350     //private
27351     setConfirmed : function(){
27352         
27353         
27354         
27355 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27356     },
27357     // private
27358     confirmHandler : function(){
27359         if(!this.getSignature()){
27360             return;
27361         }
27362         
27363         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27364         this.setValue(this.getSignature());
27365         this.isConfirmed = true;
27366         
27367         this.fireEvent('confirm', this);
27368     },
27369     // private
27370     // Subclasses should provide the validation implementation by overriding this
27371     validateValue : function(value){
27372         if(this.allowBlank){
27373             return true;
27374         }
27375         
27376         if(this.isConfirmed){
27377             return true;
27378         }
27379         return false;
27380     }
27381 });/*
27382  * Based on:
27383  * Ext JS Library 1.1.1
27384  * Copyright(c) 2006-2007, Ext JS, LLC.
27385  *
27386  * Originally Released Under LGPL - original licence link has changed is not relivant.
27387  *
27388  * Fork - LGPL
27389  * <script type="text/javascript">
27390  */
27391  
27392
27393 /**
27394  * @class Roo.form.ComboBox
27395  * @extends Roo.form.TriggerField
27396  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27397  * @constructor
27398  * Create a new ComboBox.
27399  * @param {Object} config Configuration options
27400  */
27401 Roo.form.Select = function(config){
27402     Roo.form.Select.superclass.constructor.call(this, config);
27403      
27404 };
27405
27406 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27407     /**
27408      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27409      */
27410     /**
27411      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27412      * rendering into an Roo.Editor, defaults to false)
27413      */
27414     /**
27415      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27416      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27417      */
27418     /**
27419      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27420      */
27421     /**
27422      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27423      * the dropdown list (defaults to undefined, with no header element)
27424      */
27425
27426      /**
27427      * @cfg {String/Roo.Template} tpl The template to use to render the output
27428      */
27429      
27430     // private
27431     defaultAutoCreate : {tag: "select"  },
27432     /**
27433      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27434      */
27435     listWidth: undefined,
27436     /**
27437      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27438      * mode = 'remote' or 'text' if mode = 'local')
27439      */
27440     displayField: undefined,
27441     /**
27442      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27443      * mode = 'remote' or 'value' if mode = 'local'). 
27444      * Note: use of a valueField requires the user make a selection
27445      * in order for a value to be mapped.
27446      */
27447     valueField: undefined,
27448     
27449     
27450     /**
27451      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27452      * field's data value (defaults to the underlying DOM element's name)
27453      */
27454     hiddenName: undefined,
27455     /**
27456      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27457      */
27458     listClass: '',
27459     /**
27460      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27461      */
27462     selectedClass: 'x-combo-selected',
27463     /**
27464      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27465      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27466      * which displays a downward arrow icon).
27467      */
27468     triggerClass : 'x-form-arrow-trigger',
27469     /**
27470      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27471      */
27472     shadow:'sides',
27473     /**
27474      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27475      * anchor positions (defaults to 'tl-bl')
27476      */
27477     listAlign: 'tl-bl?',
27478     /**
27479      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27480      */
27481     maxHeight: 300,
27482     /**
27483      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27484      * query specified by the allQuery config option (defaults to 'query')
27485      */
27486     triggerAction: 'query',
27487     /**
27488      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27489      * (defaults to 4, does not apply if editable = false)
27490      */
27491     minChars : 4,
27492     /**
27493      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27494      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27495      */
27496     typeAhead: false,
27497     /**
27498      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27499      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27500      */
27501     queryDelay: 500,
27502     /**
27503      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27504      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27505      */
27506     pageSize: 0,
27507     /**
27508      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27509      * when editable = true (defaults to false)
27510      */
27511     selectOnFocus:false,
27512     /**
27513      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27514      */
27515     queryParam: 'query',
27516     /**
27517      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27518      * when mode = 'remote' (defaults to 'Loading...')
27519      */
27520     loadingText: 'Loading...',
27521     /**
27522      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27523      */
27524     resizable: false,
27525     /**
27526      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27527      */
27528     handleHeight : 8,
27529     /**
27530      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27531      * traditional select (defaults to true)
27532      */
27533     editable: true,
27534     /**
27535      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27536      */
27537     allQuery: '',
27538     /**
27539      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27540      */
27541     mode: 'remote',
27542     /**
27543      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27544      * listWidth has a higher value)
27545      */
27546     minListWidth : 70,
27547     /**
27548      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27549      * allow the user to set arbitrary text into the field (defaults to false)
27550      */
27551     forceSelection:false,
27552     /**
27553      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27554      * if typeAhead = true (defaults to 250)
27555      */
27556     typeAheadDelay : 250,
27557     /**
27558      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27559      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27560      */
27561     valueNotFoundText : undefined,
27562     
27563     /**
27564      * @cfg {String} defaultValue The value displayed after loading the store.
27565      */
27566     defaultValue: '',
27567     
27568     /**
27569      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27570      */
27571     blockFocus : false,
27572     
27573     /**
27574      * @cfg {Boolean} disableClear Disable showing of clear button.
27575      */
27576     disableClear : false,
27577     /**
27578      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27579      */
27580     alwaysQuery : false,
27581     
27582     //private
27583     addicon : false,
27584     editicon: false,
27585     
27586     // element that contains real text value.. (when hidden is used..)
27587      
27588     // private
27589     onRender : function(ct, position){
27590         Roo.form.Field.prototype.onRender.call(this, ct, position);
27591         
27592         if(this.store){
27593             this.store.on('beforeload', this.onBeforeLoad, this);
27594             this.store.on('load', this.onLoad, this);
27595             this.store.on('loadexception', this.onLoadException, this);
27596             this.store.load({});
27597         }
27598         
27599         
27600         
27601     },
27602
27603     // private
27604     initEvents : function(){
27605         //Roo.form.ComboBox.superclass.initEvents.call(this);
27606  
27607     },
27608
27609     onDestroy : function(){
27610        
27611         if(this.store){
27612             this.store.un('beforeload', this.onBeforeLoad, this);
27613             this.store.un('load', this.onLoad, this);
27614             this.store.un('loadexception', this.onLoadException, this);
27615         }
27616         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27617     },
27618
27619     // private
27620     fireKey : function(e){
27621         if(e.isNavKeyPress() && !this.list.isVisible()){
27622             this.fireEvent("specialkey", this, e);
27623         }
27624     },
27625
27626     // private
27627     onResize: function(w, h){
27628         
27629         return; 
27630     
27631         
27632     },
27633
27634     /**
27635      * Allow or prevent the user from directly editing the field text.  If false is passed,
27636      * the user will only be able to select from the items defined in the dropdown list.  This method
27637      * is the runtime equivalent of setting the 'editable' config option at config time.
27638      * @param {Boolean} value True to allow the user to directly edit the field text
27639      */
27640     setEditable : function(value){
27641          
27642     },
27643
27644     // private
27645     onBeforeLoad : function(){
27646         
27647         Roo.log("Select before load");
27648         return;
27649     
27650         this.innerList.update(this.loadingText ?
27651                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27652         //this.restrictHeight();
27653         this.selectedIndex = -1;
27654     },
27655
27656     // private
27657     onLoad : function(){
27658
27659     
27660         var dom = this.el.dom;
27661         dom.innerHTML = '';
27662          var od = dom.ownerDocument;
27663          
27664         if (this.emptyText) {
27665             var op = od.createElement('option');
27666             op.setAttribute('value', '');
27667             op.innerHTML = String.format('{0}', this.emptyText);
27668             dom.appendChild(op);
27669         }
27670         if(this.store.getCount() > 0){
27671            
27672             var vf = this.valueField;
27673             var df = this.displayField;
27674             this.store.data.each(function(r) {
27675                 // which colmsn to use... testing - cdoe / title..
27676                 var op = od.createElement('option');
27677                 op.setAttribute('value', r.data[vf]);
27678                 op.innerHTML = String.format('{0}', r.data[df]);
27679                 dom.appendChild(op);
27680             });
27681             if (typeof(this.defaultValue != 'undefined')) {
27682                 this.setValue(this.defaultValue);
27683             }
27684             
27685              
27686         }else{
27687             //this.onEmptyResults();
27688         }
27689         //this.el.focus();
27690     },
27691     // private
27692     onLoadException : function()
27693     {
27694         dom.innerHTML = '';
27695             
27696         Roo.log("Select on load exception");
27697         return;
27698     
27699         this.collapse();
27700         Roo.log(this.store.reader.jsonData);
27701         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27702             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27703         }
27704         
27705         
27706     },
27707     // private
27708     onTypeAhead : function(){
27709          
27710     },
27711
27712     // private
27713     onSelect : function(record, index){
27714         Roo.log('on select?');
27715         return;
27716         if(this.fireEvent('beforeselect', this, record, index) !== false){
27717             this.setFromData(index > -1 ? record.data : false);
27718             this.collapse();
27719             this.fireEvent('select', this, record, index);
27720         }
27721     },
27722
27723     /**
27724      * Returns the currently selected field value or empty string if no value is set.
27725      * @return {String} value The selected value
27726      */
27727     getValue : function(){
27728         var dom = this.el.dom;
27729         this.value = dom.options[dom.selectedIndex].value;
27730         return this.value;
27731         
27732     },
27733
27734     /**
27735      * Clears any text/value currently set in the field
27736      */
27737     clearValue : function(){
27738         this.value = '';
27739         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27740         
27741     },
27742
27743     /**
27744      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27745      * will be displayed in the field.  If the value does not match the data value of an existing item,
27746      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27747      * Otherwise the field will be blank (although the value will still be set).
27748      * @param {String} value The value to match
27749      */
27750     setValue : function(v){
27751         var d = this.el.dom;
27752         for (var i =0; i < d.options.length;i++) {
27753             if (v == d.options[i].value) {
27754                 d.selectedIndex = i;
27755                 this.value = v;
27756                 return;
27757             }
27758         }
27759         this.clearValue();
27760     },
27761     /**
27762      * @property {Object} the last set data for the element
27763      */
27764     
27765     lastData : false,
27766     /**
27767      * Sets the value of the field based on a object which is related to the record format for the store.
27768      * @param {Object} value the value to set as. or false on reset?
27769      */
27770     setFromData : function(o){
27771         Roo.log('setfrom data?');
27772          
27773         
27774         
27775     },
27776     // private
27777     reset : function(){
27778         this.clearValue();
27779     },
27780     // private
27781     findRecord : function(prop, value){
27782         
27783         return false;
27784     
27785         var record;
27786         if(this.store.getCount() > 0){
27787             this.store.each(function(r){
27788                 if(r.data[prop] == value){
27789                     record = r;
27790                     return false;
27791                 }
27792                 return true;
27793             });
27794         }
27795         return record;
27796     },
27797     
27798     getName: function()
27799     {
27800         // returns hidden if it's set..
27801         if (!this.rendered) {return ''};
27802         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27803         
27804     },
27805      
27806
27807     
27808
27809     // private
27810     onEmptyResults : function(){
27811         Roo.log('empty results');
27812         //this.collapse();
27813     },
27814
27815     /**
27816      * Returns true if the dropdown list is expanded, else false.
27817      */
27818     isExpanded : function(){
27819         return false;
27820     },
27821
27822     /**
27823      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27824      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27825      * @param {String} value The data value of the item to select
27826      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27827      * selected item if it is not currently in view (defaults to true)
27828      * @return {Boolean} True if the value matched an item in the list, else false
27829      */
27830     selectByValue : function(v, scrollIntoView){
27831         Roo.log('select By Value');
27832         return false;
27833     
27834         if(v !== undefined && v !== null){
27835             var r = this.findRecord(this.valueField || this.displayField, v);
27836             if(r){
27837                 this.select(this.store.indexOf(r), scrollIntoView);
27838                 return true;
27839             }
27840         }
27841         return false;
27842     },
27843
27844     /**
27845      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27846      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27847      * @param {Number} index The zero-based index of the list item to select
27848      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27849      * selected item if it is not currently in view (defaults to true)
27850      */
27851     select : function(index, scrollIntoView){
27852         Roo.log('select ');
27853         return  ;
27854         
27855         this.selectedIndex = index;
27856         this.view.select(index);
27857         if(scrollIntoView !== false){
27858             var el = this.view.getNode(index);
27859             if(el){
27860                 this.innerList.scrollChildIntoView(el, false);
27861             }
27862         }
27863     },
27864
27865       
27866
27867     // private
27868     validateBlur : function(){
27869         
27870         return;
27871         
27872     },
27873
27874     // private
27875     initQuery : function(){
27876         this.doQuery(this.getRawValue());
27877     },
27878
27879     // private
27880     doForce : function(){
27881         if(this.el.dom.value.length > 0){
27882             this.el.dom.value =
27883                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27884              
27885         }
27886     },
27887
27888     /**
27889      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27890      * query allowing the query action to be canceled if needed.
27891      * @param {String} query The SQL query to execute
27892      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27893      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27894      * saved in the current store (defaults to false)
27895      */
27896     doQuery : function(q, forceAll){
27897         
27898         Roo.log('doQuery?');
27899         if(q === undefined || q === null){
27900             q = '';
27901         }
27902         var qe = {
27903             query: q,
27904             forceAll: forceAll,
27905             combo: this,
27906             cancel:false
27907         };
27908         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27909             return false;
27910         }
27911         q = qe.query;
27912         forceAll = qe.forceAll;
27913         if(forceAll === true || (q.length >= this.minChars)){
27914             if(this.lastQuery != q || this.alwaysQuery){
27915                 this.lastQuery = q;
27916                 if(this.mode == 'local'){
27917                     this.selectedIndex = -1;
27918                     if(forceAll){
27919                         this.store.clearFilter();
27920                     }else{
27921                         this.store.filter(this.displayField, q);
27922                     }
27923                     this.onLoad();
27924                 }else{
27925                     this.store.baseParams[this.queryParam] = q;
27926                     this.store.load({
27927                         params: this.getParams(q)
27928                     });
27929                     this.expand();
27930                 }
27931             }else{
27932                 this.selectedIndex = -1;
27933                 this.onLoad();   
27934             }
27935         }
27936     },
27937
27938     // private
27939     getParams : function(q){
27940         var p = {};
27941         //p[this.queryParam] = q;
27942         if(this.pageSize){
27943             p.start = 0;
27944             p.limit = this.pageSize;
27945         }
27946         return p;
27947     },
27948
27949     /**
27950      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27951      */
27952     collapse : function(){
27953         
27954     },
27955
27956     // private
27957     collapseIf : function(e){
27958         
27959     },
27960
27961     /**
27962      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27963      */
27964     expand : function(){
27965         
27966     } ,
27967
27968     // private
27969      
27970
27971     /** 
27972     * @cfg {Boolean} grow 
27973     * @hide 
27974     */
27975     /** 
27976     * @cfg {Number} growMin 
27977     * @hide 
27978     */
27979     /** 
27980     * @cfg {Number} growMax 
27981     * @hide 
27982     */
27983     /**
27984      * @hide
27985      * @method autoSize
27986      */
27987     
27988     setWidth : function()
27989     {
27990         
27991     },
27992     getResizeEl : function(){
27993         return this.el;
27994     }
27995 });//<script type="text/javasscript">
27996  
27997
27998 /**
27999  * @class Roo.DDView
28000  * A DnD enabled version of Roo.View.
28001  * @param {Element/String} container The Element in which to create the View.
28002  * @param {String} tpl The template string used to create the markup for each element of the View
28003  * @param {Object} config The configuration properties. These include all the config options of
28004  * {@link Roo.View} plus some specific to this class.<br>
28005  * <p>
28006  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28007  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28008  * <p>
28009  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28010 .x-view-drag-insert-above {
28011         border-top:1px dotted #3366cc;
28012 }
28013 .x-view-drag-insert-below {
28014         border-bottom:1px dotted #3366cc;
28015 }
28016 </code></pre>
28017  * 
28018  */
28019  
28020 Roo.DDView = function(container, tpl, config) {
28021     Roo.DDView.superclass.constructor.apply(this, arguments);
28022     this.getEl().setStyle("outline", "0px none");
28023     this.getEl().unselectable();
28024     if (this.dragGroup) {
28025                 this.setDraggable(this.dragGroup.split(","));
28026     }
28027     if (this.dropGroup) {
28028                 this.setDroppable(this.dropGroup.split(","));
28029     }
28030     if (this.deletable) {
28031         this.setDeletable();
28032     }
28033     this.isDirtyFlag = false;
28034         this.addEvents({
28035                 "drop" : true
28036         });
28037 };
28038
28039 Roo.extend(Roo.DDView, Roo.View, {
28040 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28041 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28042 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28043 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28044
28045         isFormField: true,
28046
28047         reset: Roo.emptyFn,
28048         
28049         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28050
28051         validate: function() {
28052                 return true;
28053         },
28054         
28055         destroy: function() {
28056                 this.purgeListeners();
28057                 this.getEl.removeAllListeners();
28058                 this.getEl().remove();
28059                 if (this.dragZone) {
28060                         if (this.dragZone.destroy) {
28061                                 this.dragZone.destroy();
28062                         }
28063                 }
28064                 if (this.dropZone) {
28065                         if (this.dropZone.destroy) {
28066                                 this.dropZone.destroy();
28067                         }
28068                 }
28069         },
28070
28071 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28072         getName: function() {
28073                 return this.name;
28074         },
28075
28076 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28077         setValue: function(v) {
28078                 if (!this.store) {
28079                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28080                 }
28081                 var data = {};
28082                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28083                 this.store.proxy = new Roo.data.MemoryProxy(data);
28084                 this.store.load();
28085         },
28086
28087 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28088         getValue: function() {
28089                 var result = '(';
28090                 this.store.each(function(rec) {
28091                         result += rec.id + ',';
28092                 });
28093                 return result.substr(0, result.length - 1) + ')';
28094         },
28095         
28096         getIds: function() {
28097                 var i = 0, result = new Array(this.store.getCount());
28098                 this.store.each(function(rec) {
28099                         result[i++] = rec.id;
28100                 });
28101                 return result;
28102         },
28103         
28104         isDirty: function() {
28105                 return this.isDirtyFlag;
28106         },
28107
28108 /**
28109  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28110  *      whole Element becomes the target, and this causes the drop gesture to append.
28111  */
28112     getTargetFromEvent : function(e) {
28113                 var target = e.getTarget();
28114                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28115                 target = target.parentNode;
28116                 }
28117                 if (!target) {
28118                         target = this.el.dom.lastChild || this.el.dom;
28119                 }
28120                 return target;
28121     },
28122
28123 /**
28124  *      Create the drag data which consists of an object which has the property "ddel" as
28125  *      the drag proxy element. 
28126  */
28127     getDragData : function(e) {
28128         var target = this.findItemFromChild(e.getTarget());
28129                 if(target) {
28130                         this.handleSelection(e);
28131                         var selNodes = this.getSelectedNodes();
28132             var dragData = {
28133                 source: this,
28134                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28135                 nodes: selNodes,
28136                 records: []
28137                         };
28138                         var selectedIndices = this.getSelectedIndexes();
28139                         for (var i = 0; i < selectedIndices.length; i++) {
28140                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28141                         }
28142                         if (selNodes.length == 1) {
28143                                 dragData.ddel = target.cloneNode(true); // the div element
28144                         } else {
28145                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28146                                 div.className = 'multi-proxy';
28147                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28148                                         div.appendChild(selNodes[i].cloneNode(true));
28149                                 }
28150                                 dragData.ddel = div;
28151                         }
28152             //console.log(dragData)
28153             //console.log(dragData.ddel.innerHTML)
28154                         return dragData;
28155                 }
28156         //console.log('nodragData')
28157                 return false;
28158     },
28159     
28160 /**     Specify to which ddGroup items in this DDView may be dragged. */
28161     setDraggable: function(ddGroup) {
28162         if (ddGroup instanceof Array) {
28163                 Roo.each(ddGroup, this.setDraggable, this);
28164                 return;
28165         }
28166         if (this.dragZone) {
28167                 this.dragZone.addToGroup(ddGroup);
28168         } else {
28169                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28170                                 containerScroll: true,
28171                                 ddGroup: ddGroup 
28172
28173                         });
28174 //                      Draggability implies selection. DragZone's mousedown selects the element.
28175                         if (!this.multiSelect) { this.singleSelect = true; }
28176
28177 //                      Wire the DragZone's handlers up to methods in *this*
28178                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28179                 }
28180     },
28181
28182 /**     Specify from which ddGroup this DDView accepts drops. */
28183     setDroppable: function(ddGroup) {
28184         if (ddGroup instanceof Array) {
28185                 Roo.each(ddGroup, this.setDroppable, this);
28186                 return;
28187         }
28188         if (this.dropZone) {
28189                 this.dropZone.addToGroup(ddGroup);
28190         } else {
28191                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28192                                 containerScroll: true,
28193                                 ddGroup: ddGroup
28194                         });
28195
28196 //                      Wire the DropZone's handlers up to methods in *this*
28197                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28198                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28199                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28200                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28201                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28202                 }
28203     },
28204
28205 /**     Decide whether to drop above or below a View node. */
28206     getDropPoint : function(e, n, dd){
28207         if (n == this.el.dom) { return "above"; }
28208                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28209                 var c = t + (b - t) / 2;
28210                 var y = Roo.lib.Event.getPageY(e);
28211                 if(y <= c) {
28212                         return "above";
28213                 }else{
28214                         return "below";
28215                 }
28216     },
28217
28218     onNodeEnter : function(n, dd, e, data){
28219                 return false;
28220     },
28221     
28222     onNodeOver : function(n, dd, e, data){
28223                 var pt = this.getDropPoint(e, n, dd);
28224                 // set the insert point style on the target node
28225                 var dragElClass = this.dropNotAllowed;
28226                 if (pt) {
28227                         var targetElClass;
28228                         if (pt == "above"){
28229                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28230                                 targetElClass = "x-view-drag-insert-above";
28231                         } else {
28232                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28233                                 targetElClass = "x-view-drag-insert-below";
28234                         }
28235                         if (this.lastInsertClass != targetElClass){
28236                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28237                                 this.lastInsertClass = targetElClass;
28238                         }
28239                 }
28240                 return dragElClass;
28241         },
28242
28243     onNodeOut : function(n, dd, e, data){
28244                 this.removeDropIndicators(n);
28245     },
28246
28247     onNodeDrop : function(n, dd, e, data){
28248         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28249                 return false;
28250         }
28251         var pt = this.getDropPoint(e, n, dd);
28252                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28253                 if (pt == "below") { insertAt++; }
28254                 for (var i = 0; i < data.records.length; i++) {
28255                         var r = data.records[i];
28256                         var dup = this.store.getById(r.id);
28257                         if (dup && (dd != this.dragZone)) {
28258                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28259                         } else {
28260                                 if (data.copy) {
28261                                         this.store.insert(insertAt++, r.copy());
28262                                 } else {
28263                                         data.source.isDirtyFlag = true;
28264                                         r.store.remove(r);
28265                                         this.store.insert(insertAt++, r);
28266                                 }
28267                                 this.isDirtyFlag = true;
28268                         }
28269                 }
28270                 this.dragZone.cachedTarget = null;
28271                 return true;
28272     },
28273
28274     removeDropIndicators : function(n){
28275                 if(n){
28276                         Roo.fly(n).removeClass([
28277                                 "x-view-drag-insert-above",
28278                                 "x-view-drag-insert-below"]);
28279                         this.lastInsertClass = "_noclass";
28280                 }
28281     },
28282
28283 /**
28284  *      Utility method. Add a delete option to the DDView's context menu.
28285  *      @param {String} imageUrl The URL of the "delete" icon image.
28286  */
28287         setDeletable: function(imageUrl) {
28288                 if (!this.singleSelect && !this.multiSelect) {
28289                         this.singleSelect = true;
28290                 }
28291                 var c = this.getContextMenu();
28292                 this.contextMenu.on("itemclick", function(item) {
28293                         switch (item.id) {
28294                                 case "delete":
28295                                         this.remove(this.getSelectedIndexes());
28296                                         break;
28297                         }
28298                 }, this);
28299                 this.contextMenu.add({
28300                         icon: imageUrl,
28301                         id: "delete",
28302                         text: 'Delete'
28303                 });
28304         },
28305         
28306 /**     Return the context menu for this DDView. */
28307         getContextMenu: function() {
28308                 if (!this.contextMenu) {
28309 //                      Create the View's context menu
28310                         this.contextMenu = new Roo.menu.Menu({
28311                                 id: this.id + "-contextmenu"
28312                         });
28313                         this.el.on("contextmenu", this.showContextMenu, this);
28314                 }
28315                 return this.contextMenu;
28316         },
28317         
28318         disableContextMenu: function() {
28319                 if (this.contextMenu) {
28320                         this.el.un("contextmenu", this.showContextMenu, this);
28321                 }
28322         },
28323
28324         showContextMenu: function(e, item) {
28325         item = this.findItemFromChild(e.getTarget());
28326                 if (item) {
28327                         e.stopEvent();
28328                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28329                         this.contextMenu.showAt(e.getXY());
28330             }
28331     },
28332
28333 /**
28334  *      Remove {@link Roo.data.Record}s at the specified indices.
28335  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28336  */
28337     remove: function(selectedIndices) {
28338                 selectedIndices = [].concat(selectedIndices);
28339                 for (var i = 0; i < selectedIndices.length; i++) {
28340                         var rec = this.store.getAt(selectedIndices[i]);
28341                         this.store.remove(rec);
28342                 }
28343     },
28344
28345 /**
28346  *      Double click fires the event, but also, if this is draggable, and there is only one other
28347  *      related DropZone, it transfers the selected node.
28348  */
28349     onDblClick : function(e){
28350         var item = this.findItemFromChild(e.getTarget());
28351         if(item){
28352             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28353                 return false;
28354             }
28355             if (this.dragGroup) {
28356                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28357                     while (targets.indexOf(this.dropZone) > -1) {
28358                             targets.remove(this.dropZone);
28359                                 }
28360                     if (targets.length == 1) {
28361                                         this.dragZone.cachedTarget = null;
28362                         var el = Roo.get(targets[0].getEl());
28363                         var box = el.getBox(true);
28364                         targets[0].onNodeDrop(el.dom, {
28365                                 target: el.dom,
28366                                 xy: [box.x, box.y + box.height - 1]
28367                         }, null, this.getDragData(e));
28368                     }
28369                 }
28370         }
28371     },
28372     
28373     handleSelection: function(e) {
28374                 this.dragZone.cachedTarget = null;
28375         var item = this.findItemFromChild(e.getTarget());
28376         if (!item) {
28377                 this.clearSelections(true);
28378                 return;
28379         }
28380                 if (item && (this.multiSelect || this.singleSelect)){
28381                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28382                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28383                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28384                                 this.unselect(item);
28385                         } else {
28386                                 this.select(item, this.multiSelect && e.ctrlKey);
28387                                 this.lastSelection = item;
28388                         }
28389                 }
28390     },
28391
28392     onItemClick : function(item, index, e){
28393                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28394                         return false;
28395                 }
28396                 return true;
28397     },
28398
28399     unselect : function(nodeInfo, suppressEvent){
28400                 var node = this.getNode(nodeInfo);
28401                 if(node && this.isSelected(node)){
28402                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28403                                 Roo.fly(node).removeClass(this.selectedClass);
28404                                 this.selections.remove(node);
28405                                 if(!suppressEvent){
28406                                         this.fireEvent("selectionchange", this, this.selections);
28407                                 }
28408                         }
28409                 }
28410     }
28411 });
28412 /*
28413  * Based on:
28414  * Ext JS Library 1.1.1
28415  * Copyright(c) 2006-2007, Ext JS, LLC.
28416  *
28417  * Originally Released Under LGPL - original licence link has changed is not relivant.
28418  *
28419  * Fork - LGPL
28420  * <script type="text/javascript">
28421  */
28422  
28423 /**
28424  * @class Roo.LayoutManager
28425  * @extends Roo.util.Observable
28426  * Base class for layout managers.
28427  */
28428 Roo.LayoutManager = function(container, config){
28429     Roo.LayoutManager.superclass.constructor.call(this);
28430     this.el = Roo.get(container);
28431     // ie scrollbar fix
28432     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28433         document.body.scroll = "no";
28434     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28435         this.el.position('relative');
28436     }
28437     this.id = this.el.id;
28438     this.el.addClass("x-layout-container");
28439     /** false to disable window resize monitoring @type Boolean */
28440     this.monitorWindowResize = true;
28441     this.regions = {};
28442     this.addEvents({
28443         /**
28444          * @event layout
28445          * Fires when a layout is performed. 
28446          * @param {Roo.LayoutManager} this
28447          */
28448         "layout" : true,
28449         /**
28450          * @event regionresized
28451          * Fires when the user resizes a region. 
28452          * @param {Roo.LayoutRegion} region The resized region
28453          * @param {Number} newSize The new size (width for east/west, height for north/south)
28454          */
28455         "regionresized" : true,
28456         /**
28457          * @event regioncollapsed
28458          * Fires when a region is collapsed. 
28459          * @param {Roo.LayoutRegion} region The collapsed region
28460          */
28461         "regioncollapsed" : true,
28462         /**
28463          * @event regionexpanded
28464          * Fires when a region is expanded.  
28465          * @param {Roo.LayoutRegion} region The expanded region
28466          */
28467         "regionexpanded" : true
28468     });
28469     this.updating = false;
28470     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28471 };
28472
28473 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28474     /**
28475      * Returns true if this layout is currently being updated
28476      * @return {Boolean}
28477      */
28478     isUpdating : function(){
28479         return this.updating; 
28480     },
28481     
28482     /**
28483      * Suspend the LayoutManager from doing auto-layouts while
28484      * making multiple add or remove calls
28485      */
28486     beginUpdate : function(){
28487         this.updating = true;    
28488     },
28489     
28490     /**
28491      * Restore auto-layouts and optionally disable the manager from performing a layout
28492      * @param {Boolean} noLayout true to disable a layout update 
28493      */
28494     endUpdate : function(noLayout){
28495         this.updating = false;
28496         if(!noLayout){
28497             this.layout();
28498         }    
28499     },
28500     
28501     layout: function(){
28502         
28503     },
28504     
28505     onRegionResized : function(region, newSize){
28506         this.fireEvent("regionresized", region, newSize);
28507         this.layout();
28508     },
28509     
28510     onRegionCollapsed : function(region){
28511         this.fireEvent("regioncollapsed", region);
28512     },
28513     
28514     onRegionExpanded : function(region){
28515         this.fireEvent("regionexpanded", region);
28516     },
28517         
28518     /**
28519      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28520      * performs box-model adjustments.
28521      * @return {Object} The size as an object {width: (the width), height: (the height)}
28522      */
28523     getViewSize : function(){
28524         var size;
28525         if(this.el.dom != document.body){
28526             size = this.el.getSize();
28527         }else{
28528             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28529         }
28530         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28531         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28532         return size;
28533     },
28534     
28535     /**
28536      * Returns the Element this layout is bound to.
28537      * @return {Roo.Element}
28538      */
28539     getEl : function(){
28540         return this.el;
28541     },
28542     
28543     /**
28544      * Returns the specified region.
28545      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28546      * @return {Roo.LayoutRegion}
28547      */
28548     getRegion : function(target){
28549         return this.regions[target.toLowerCase()];
28550     },
28551     
28552     onWindowResize : function(){
28553         if(this.monitorWindowResize){
28554             this.layout();
28555         }
28556     }
28557 });/*
28558  * Based on:
28559  * Ext JS Library 1.1.1
28560  * Copyright(c) 2006-2007, Ext JS, LLC.
28561  *
28562  * Originally Released Under LGPL - original licence link has changed is not relivant.
28563  *
28564  * Fork - LGPL
28565  * <script type="text/javascript">
28566  */
28567 /**
28568  * @class Roo.BorderLayout
28569  * @extends Roo.LayoutManager
28570  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28571  * please see: <br><br>
28572  * <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>
28573  * <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>
28574  * Example:
28575  <pre><code>
28576  var layout = new Roo.BorderLayout(document.body, {
28577     north: {
28578         initialSize: 25,
28579         titlebar: false
28580     },
28581     west: {
28582         split:true,
28583         initialSize: 200,
28584         minSize: 175,
28585         maxSize: 400,
28586         titlebar: true,
28587         collapsible: true
28588     },
28589     east: {
28590         split:true,
28591         initialSize: 202,
28592         minSize: 175,
28593         maxSize: 400,
28594         titlebar: true,
28595         collapsible: true
28596     },
28597     south: {
28598         split:true,
28599         initialSize: 100,
28600         minSize: 100,
28601         maxSize: 200,
28602         titlebar: true,
28603         collapsible: true
28604     },
28605     center: {
28606         titlebar: true,
28607         autoScroll:true,
28608         resizeTabs: true,
28609         minTabWidth: 50,
28610         preferredTabWidth: 150
28611     }
28612 });
28613
28614 // shorthand
28615 var CP = Roo.ContentPanel;
28616
28617 layout.beginUpdate();
28618 layout.add("north", new CP("north", "North"));
28619 layout.add("south", new CP("south", {title: "South", closable: true}));
28620 layout.add("west", new CP("west", {title: "West"}));
28621 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28622 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28623 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28624 layout.getRegion("center").showPanel("center1");
28625 layout.endUpdate();
28626 </code></pre>
28627
28628 <b>The container the layout is rendered into can be either the body element or any other element.
28629 If it is not the body element, the container needs to either be an absolute positioned element,
28630 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28631 the container size if it is not the body element.</b>
28632
28633 * @constructor
28634 * Create a new BorderLayout
28635 * @param {String/HTMLElement/Element} container The container this layout is bound to
28636 * @param {Object} config Configuration options
28637  */
28638 Roo.BorderLayout = function(container, config){
28639     config = config || {};
28640     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28641     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28642     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28643         var target = this.factory.validRegions[i];
28644         if(config[target]){
28645             this.addRegion(target, config[target]);
28646         }
28647     }
28648 };
28649
28650 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28651     /**
28652      * Creates and adds a new region if it doesn't already exist.
28653      * @param {String} target The target region key (north, south, east, west or center).
28654      * @param {Object} config The regions config object
28655      * @return {BorderLayoutRegion} The new region
28656      */
28657     addRegion : function(target, config){
28658         if(!this.regions[target]){
28659             var r = this.factory.create(target, this, config);
28660             this.bindRegion(target, r);
28661         }
28662         return this.regions[target];
28663     },
28664
28665     // private (kinda)
28666     bindRegion : function(name, r){
28667         this.regions[name] = r;
28668         r.on("visibilitychange", this.layout, this);
28669         r.on("paneladded", this.layout, this);
28670         r.on("panelremoved", this.layout, this);
28671         r.on("invalidated", this.layout, this);
28672         r.on("resized", this.onRegionResized, this);
28673         r.on("collapsed", this.onRegionCollapsed, this);
28674         r.on("expanded", this.onRegionExpanded, this);
28675     },
28676
28677     /**
28678      * Performs a layout update.
28679      */
28680     layout : function(){
28681         if(this.updating) {
28682             return;
28683         }
28684         var size = this.getViewSize();
28685         var w = size.width;
28686         var h = size.height;
28687         var centerW = w;
28688         var centerH = h;
28689         var centerY = 0;
28690         var centerX = 0;
28691         //var x = 0, y = 0;
28692
28693         var rs = this.regions;
28694         var north = rs["north"];
28695         var south = rs["south"]; 
28696         var west = rs["west"];
28697         var east = rs["east"];
28698         var center = rs["center"];
28699         //if(this.hideOnLayout){ // not supported anymore
28700             //c.el.setStyle("display", "none");
28701         //}
28702         if(north && north.isVisible()){
28703             var b = north.getBox();
28704             var m = north.getMargins();
28705             b.width = w - (m.left+m.right);
28706             b.x = m.left;
28707             b.y = m.top;
28708             centerY = b.height + b.y + m.bottom;
28709             centerH -= centerY;
28710             north.updateBox(this.safeBox(b));
28711         }
28712         if(south && south.isVisible()){
28713             var b = south.getBox();
28714             var m = south.getMargins();
28715             b.width = w - (m.left+m.right);
28716             b.x = m.left;
28717             var totalHeight = (b.height + m.top + m.bottom);
28718             b.y = h - totalHeight + m.top;
28719             centerH -= totalHeight;
28720             south.updateBox(this.safeBox(b));
28721         }
28722         if(west && west.isVisible()){
28723             var b = west.getBox();
28724             var m = west.getMargins();
28725             b.height = centerH - (m.top+m.bottom);
28726             b.x = m.left;
28727             b.y = centerY + m.top;
28728             var totalWidth = (b.width + m.left + m.right);
28729             centerX += totalWidth;
28730             centerW -= totalWidth;
28731             west.updateBox(this.safeBox(b));
28732         }
28733         if(east && east.isVisible()){
28734             var b = east.getBox();
28735             var m = east.getMargins();
28736             b.height = centerH - (m.top+m.bottom);
28737             var totalWidth = (b.width + m.left + m.right);
28738             b.x = w - totalWidth + m.left;
28739             b.y = centerY + m.top;
28740             centerW -= totalWidth;
28741             east.updateBox(this.safeBox(b));
28742         }
28743         if(center){
28744             var m = center.getMargins();
28745             var centerBox = {
28746                 x: centerX + m.left,
28747                 y: centerY + m.top,
28748                 width: centerW - (m.left+m.right),
28749                 height: centerH - (m.top+m.bottom)
28750             };
28751             //if(this.hideOnLayout){
28752                 //center.el.setStyle("display", "block");
28753             //}
28754             center.updateBox(this.safeBox(centerBox));
28755         }
28756         this.el.repaint();
28757         this.fireEvent("layout", this);
28758     },
28759
28760     // private
28761     safeBox : function(box){
28762         box.width = Math.max(0, box.width);
28763         box.height = Math.max(0, box.height);
28764         return box;
28765     },
28766
28767     /**
28768      * Adds a ContentPanel (or subclass) to this layout.
28769      * @param {String} target The target region key (north, south, east, west or center).
28770      * @param {Roo.ContentPanel} panel The panel to add
28771      * @return {Roo.ContentPanel} The added panel
28772      */
28773     add : function(target, panel){
28774          
28775         target = target.toLowerCase();
28776         return this.regions[target].add(panel);
28777     },
28778
28779     /**
28780      * Remove a ContentPanel (or subclass) to this layout.
28781      * @param {String} target The target region key (north, south, east, west or center).
28782      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28783      * @return {Roo.ContentPanel} The removed panel
28784      */
28785     remove : function(target, panel){
28786         target = target.toLowerCase();
28787         return this.regions[target].remove(panel);
28788     },
28789
28790     /**
28791      * Searches all regions for a panel with the specified id
28792      * @param {String} panelId
28793      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28794      */
28795     findPanel : function(panelId){
28796         var rs = this.regions;
28797         for(var target in rs){
28798             if(typeof rs[target] != "function"){
28799                 var p = rs[target].getPanel(panelId);
28800                 if(p){
28801                     return p;
28802                 }
28803             }
28804         }
28805         return null;
28806     },
28807
28808     /**
28809      * Searches all regions for a panel with the specified id and activates (shows) it.
28810      * @param {String/ContentPanel} panelId The panels id or the panel itself
28811      * @return {Roo.ContentPanel} The shown panel or null
28812      */
28813     showPanel : function(panelId) {
28814       var rs = this.regions;
28815       for(var target in rs){
28816          var r = rs[target];
28817          if(typeof r != "function"){
28818             if(r.hasPanel(panelId)){
28819                return r.showPanel(panelId);
28820             }
28821          }
28822       }
28823       return null;
28824    },
28825
28826    /**
28827      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28828      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28829      */
28830     restoreState : function(provider){
28831         if(!provider){
28832             provider = Roo.state.Manager;
28833         }
28834         var sm = new Roo.LayoutStateManager();
28835         sm.init(this, provider);
28836     },
28837
28838     /**
28839      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28840      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28841      * a valid ContentPanel config object.  Example:
28842      * <pre><code>
28843 // Create the main layout
28844 var layout = new Roo.BorderLayout('main-ct', {
28845     west: {
28846         split:true,
28847         minSize: 175,
28848         titlebar: true
28849     },
28850     center: {
28851         title:'Components'
28852     }
28853 }, 'main-ct');
28854
28855 // Create and add multiple ContentPanels at once via configs
28856 layout.batchAdd({
28857    west: {
28858        id: 'source-files',
28859        autoCreate:true,
28860        title:'Ext Source Files',
28861        autoScroll:true,
28862        fitToFrame:true
28863    },
28864    center : {
28865        el: cview,
28866        autoScroll:true,
28867        fitToFrame:true,
28868        toolbar: tb,
28869        resizeEl:'cbody'
28870    }
28871 });
28872 </code></pre>
28873      * @param {Object} regions An object containing ContentPanel configs by region name
28874      */
28875     batchAdd : function(regions){
28876         this.beginUpdate();
28877         for(var rname in regions){
28878             var lr = this.regions[rname];
28879             if(lr){
28880                 this.addTypedPanels(lr, regions[rname]);
28881             }
28882         }
28883         this.endUpdate();
28884     },
28885
28886     // private
28887     addTypedPanels : function(lr, ps){
28888         if(typeof ps == 'string'){
28889             lr.add(new Roo.ContentPanel(ps));
28890         }
28891         else if(ps instanceof Array){
28892             for(var i =0, len = ps.length; i < len; i++){
28893                 this.addTypedPanels(lr, ps[i]);
28894             }
28895         }
28896         else if(!ps.events){ // raw config?
28897             var el = ps.el;
28898             delete ps.el; // prevent conflict
28899             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28900         }
28901         else {  // panel object assumed!
28902             lr.add(ps);
28903         }
28904     },
28905     /**
28906      * Adds a xtype elements to the layout.
28907      * <pre><code>
28908
28909 layout.addxtype({
28910        xtype : 'ContentPanel',
28911        region: 'west',
28912        items: [ .... ]
28913    }
28914 );
28915
28916 layout.addxtype({
28917         xtype : 'NestedLayoutPanel',
28918         region: 'west',
28919         layout: {
28920            center: { },
28921            west: { }   
28922         },
28923         items : [ ... list of content panels or nested layout panels.. ]
28924    }
28925 );
28926 </code></pre>
28927      * @param {Object} cfg Xtype definition of item to add.
28928      */
28929     addxtype : function(cfg)
28930     {
28931         // basically accepts a pannel...
28932         // can accept a layout region..!?!?
28933         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28934         
28935         if (!cfg.xtype.match(/Panel$/)) {
28936             return false;
28937         }
28938         var ret = false;
28939         
28940         if (typeof(cfg.region) == 'undefined') {
28941             Roo.log("Failed to add Panel, region was not set");
28942             Roo.log(cfg);
28943             return false;
28944         }
28945         var region = cfg.region;
28946         delete cfg.region;
28947         
28948           
28949         var xitems = [];
28950         if (cfg.items) {
28951             xitems = cfg.items;
28952             delete cfg.items;
28953         }
28954         var nb = false;
28955         
28956         switch(cfg.xtype) 
28957         {
28958             case 'ContentPanel':  // ContentPanel (el, cfg)
28959             case 'ScrollPanel':  // ContentPanel (el, cfg)
28960             case 'ViewPanel': 
28961                 if(cfg.autoCreate) {
28962                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28963                 } else {
28964                     var el = this.el.createChild();
28965                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28966                 }
28967                 
28968                 this.add(region, ret);
28969                 break;
28970             
28971             
28972             case 'TreePanel': // our new panel!
28973                 cfg.el = this.el.createChild();
28974                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28975                 this.add(region, ret);
28976                 break;
28977             
28978             case 'NestedLayoutPanel': 
28979                 // create a new Layout (which is  a Border Layout...
28980                 var el = this.el.createChild();
28981                 var clayout = cfg.layout;
28982                 delete cfg.layout;
28983                 clayout.items   = clayout.items  || [];
28984                 // replace this exitems with the clayout ones..
28985                 xitems = clayout.items;
28986                  
28987                 
28988                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28989                     cfg.background = false;
28990                 }
28991                 var layout = new Roo.BorderLayout(el, clayout);
28992                 
28993                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28994                 //console.log('adding nested layout panel '  + cfg.toSource());
28995                 this.add(region, ret);
28996                 nb = {}; /// find first...
28997                 break;
28998                 
28999             case 'GridPanel': 
29000             
29001                 // needs grid and region
29002                 
29003                 //var el = this.getRegion(region).el.createChild();
29004                 var el = this.el.createChild();
29005                 // create the grid first...
29006                 
29007                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29008                 delete cfg.grid;
29009                 if (region == 'center' && this.active ) {
29010                     cfg.background = false;
29011                 }
29012                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29013                 
29014                 this.add(region, ret);
29015                 if (cfg.background) {
29016                     ret.on('activate', function(gp) {
29017                         if (!gp.grid.rendered) {
29018                             gp.grid.render();
29019                         }
29020                     });
29021                 } else {
29022                     grid.render();
29023                 }
29024                 break;
29025            
29026            
29027            
29028                 
29029                 
29030                 
29031             default:
29032                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29033                     
29034                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29035                     this.add(region, ret);
29036                 } else {
29037                 
29038                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29039                     return null;
29040                 }
29041                 
29042              // GridPanel (grid, cfg)
29043             
29044         }
29045         this.beginUpdate();
29046         // add children..
29047         var region = '';
29048         var abn = {};
29049         Roo.each(xitems, function(i)  {
29050             region = nb && i.region ? i.region : false;
29051             
29052             var add = ret.addxtype(i);
29053            
29054             if (region) {
29055                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29056                 if (!i.background) {
29057                     abn[region] = nb[region] ;
29058                 }
29059             }
29060             
29061         });
29062         this.endUpdate();
29063
29064         // make the last non-background panel active..
29065         //if (nb) { Roo.log(abn); }
29066         if (nb) {
29067             
29068             for(var r in abn) {
29069                 region = this.getRegion(r);
29070                 if (region) {
29071                     // tried using nb[r], but it does not work..
29072                      
29073                     region.showPanel(abn[r]);
29074                    
29075                 }
29076             }
29077         }
29078         return ret;
29079         
29080     }
29081 });
29082
29083 /**
29084  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29085  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29086  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29087  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29088  * <pre><code>
29089 // shorthand
29090 var CP = Roo.ContentPanel;
29091
29092 var layout = Roo.BorderLayout.create({
29093     north: {
29094         initialSize: 25,
29095         titlebar: false,
29096         panels: [new CP("north", "North")]
29097     },
29098     west: {
29099         split:true,
29100         initialSize: 200,
29101         minSize: 175,
29102         maxSize: 400,
29103         titlebar: true,
29104         collapsible: true,
29105         panels: [new CP("west", {title: "West"})]
29106     },
29107     east: {
29108         split:true,
29109         initialSize: 202,
29110         minSize: 175,
29111         maxSize: 400,
29112         titlebar: true,
29113         collapsible: true,
29114         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29115     },
29116     south: {
29117         split:true,
29118         initialSize: 100,
29119         minSize: 100,
29120         maxSize: 200,
29121         titlebar: true,
29122         collapsible: true,
29123         panels: [new CP("south", {title: "South", closable: true})]
29124     },
29125     center: {
29126         titlebar: true,
29127         autoScroll:true,
29128         resizeTabs: true,
29129         minTabWidth: 50,
29130         preferredTabWidth: 150,
29131         panels: [
29132             new CP("center1", {title: "Close Me", closable: true}),
29133             new CP("center2", {title: "Center Panel", closable: false})
29134         ]
29135     }
29136 }, document.body);
29137
29138 layout.getRegion("center").showPanel("center1");
29139 </code></pre>
29140  * @param config
29141  * @param targetEl
29142  */
29143 Roo.BorderLayout.create = function(config, targetEl){
29144     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29145     layout.beginUpdate();
29146     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29147     for(var j = 0, jlen = regions.length; j < jlen; j++){
29148         var lr = regions[j];
29149         if(layout.regions[lr] && config[lr].panels){
29150             var r = layout.regions[lr];
29151             var ps = config[lr].panels;
29152             layout.addTypedPanels(r, ps);
29153         }
29154     }
29155     layout.endUpdate();
29156     return layout;
29157 };
29158
29159 // private
29160 Roo.BorderLayout.RegionFactory = {
29161     // private
29162     validRegions : ["north","south","east","west","center"],
29163
29164     // private
29165     create : function(target, mgr, config){
29166         target = target.toLowerCase();
29167         if(config.lightweight || config.basic){
29168             return new Roo.BasicLayoutRegion(mgr, config, target);
29169         }
29170         switch(target){
29171             case "north":
29172                 return new Roo.NorthLayoutRegion(mgr, config);
29173             case "south":
29174                 return new Roo.SouthLayoutRegion(mgr, config);
29175             case "east":
29176                 return new Roo.EastLayoutRegion(mgr, config);
29177             case "west":
29178                 return new Roo.WestLayoutRegion(mgr, config);
29179             case "center":
29180                 return new Roo.CenterLayoutRegion(mgr, config);
29181         }
29182         throw 'Layout region "'+target+'" not supported.';
29183     }
29184 };/*
29185  * Based on:
29186  * Ext JS Library 1.1.1
29187  * Copyright(c) 2006-2007, Ext JS, LLC.
29188  *
29189  * Originally Released Under LGPL - original licence link has changed is not relivant.
29190  *
29191  * Fork - LGPL
29192  * <script type="text/javascript">
29193  */
29194  
29195 /**
29196  * @class Roo.BasicLayoutRegion
29197  * @extends Roo.util.Observable
29198  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29199  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29200  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29201  */
29202 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29203     this.mgr = mgr;
29204     this.position  = pos;
29205     this.events = {
29206         /**
29207          * @scope Roo.BasicLayoutRegion
29208          */
29209         
29210         /**
29211          * @event beforeremove
29212          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29213          * @param {Roo.LayoutRegion} this
29214          * @param {Roo.ContentPanel} panel The panel
29215          * @param {Object} e The cancel event object
29216          */
29217         "beforeremove" : true,
29218         /**
29219          * @event invalidated
29220          * Fires when the layout for this region is changed.
29221          * @param {Roo.LayoutRegion} this
29222          */
29223         "invalidated" : true,
29224         /**
29225          * @event visibilitychange
29226          * Fires when this region is shown or hidden 
29227          * @param {Roo.LayoutRegion} this
29228          * @param {Boolean} visibility true or false
29229          */
29230         "visibilitychange" : true,
29231         /**
29232          * @event paneladded
29233          * Fires when a panel is added. 
29234          * @param {Roo.LayoutRegion} this
29235          * @param {Roo.ContentPanel} panel The panel
29236          */
29237         "paneladded" : true,
29238         /**
29239          * @event panelremoved
29240          * Fires when a panel is removed. 
29241          * @param {Roo.LayoutRegion} this
29242          * @param {Roo.ContentPanel} panel The panel
29243          */
29244         "panelremoved" : true,
29245         /**
29246          * @event beforecollapse
29247          * Fires when this region before collapse.
29248          * @param {Roo.LayoutRegion} this
29249          */
29250         "beforecollapse" : true,
29251         /**
29252          * @event collapsed
29253          * Fires when this region is collapsed.
29254          * @param {Roo.LayoutRegion} this
29255          */
29256         "collapsed" : true,
29257         /**
29258          * @event expanded
29259          * Fires when this region is expanded.
29260          * @param {Roo.LayoutRegion} this
29261          */
29262         "expanded" : true,
29263         /**
29264          * @event slideshow
29265          * Fires when this region is slid into view.
29266          * @param {Roo.LayoutRegion} this
29267          */
29268         "slideshow" : true,
29269         /**
29270          * @event slidehide
29271          * Fires when this region slides out of view. 
29272          * @param {Roo.LayoutRegion} this
29273          */
29274         "slidehide" : true,
29275         /**
29276          * @event panelactivated
29277          * Fires when a panel is activated. 
29278          * @param {Roo.LayoutRegion} this
29279          * @param {Roo.ContentPanel} panel The activated panel
29280          */
29281         "panelactivated" : true,
29282         /**
29283          * @event resized
29284          * Fires when the user resizes this region. 
29285          * @param {Roo.LayoutRegion} this
29286          * @param {Number} newSize The new size (width for east/west, height for north/south)
29287          */
29288         "resized" : true
29289     };
29290     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29291     this.panels = new Roo.util.MixedCollection();
29292     this.panels.getKey = this.getPanelId.createDelegate(this);
29293     this.box = null;
29294     this.activePanel = null;
29295     // ensure listeners are added...
29296     
29297     if (config.listeners || config.events) {
29298         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29299             listeners : config.listeners || {},
29300             events : config.events || {}
29301         });
29302     }
29303     
29304     if(skipConfig !== true){
29305         this.applyConfig(config);
29306     }
29307 };
29308
29309 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29310     getPanelId : function(p){
29311         return p.getId();
29312     },
29313     
29314     applyConfig : function(config){
29315         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29316         this.config = config;
29317         
29318     },
29319     
29320     /**
29321      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29322      * the width, for horizontal (north, south) the height.
29323      * @param {Number} newSize The new width or height
29324      */
29325     resizeTo : function(newSize){
29326         var el = this.el ? this.el :
29327                  (this.activePanel ? this.activePanel.getEl() : null);
29328         if(el){
29329             switch(this.position){
29330                 case "east":
29331                 case "west":
29332                     el.setWidth(newSize);
29333                     this.fireEvent("resized", this, newSize);
29334                 break;
29335                 case "north":
29336                 case "south":
29337                     el.setHeight(newSize);
29338                     this.fireEvent("resized", this, newSize);
29339                 break;                
29340             }
29341         }
29342     },
29343     
29344     getBox : function(){
29345         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29346     },
29347     
29348     getMargins : function(){
29349         return this.margins;
29350     },
29351     
29352     updateBox : function(box){
29353         this.box = box;
29354         var el = this.activePanel.getEl();
29355         el.dom.style.left = box.x + "px";
29356         el.dom.style.top = box.y + "px";
29357         this.activePanel.setSize(box.width, box.height);
29358     },
29359     
29360     /**
29361      * Returns the container element for this region.
29362      * @return {Roo.Element}
29363      */
29364     getEl : function(){
29365         return this.activePanel;
29366     },
29367     
29368     /**
29369      * Returns true if this region is currently visible.
29370      * @return {Boolean}
29371      */
29372     isVisible : function(){
29373         return this.activePanel ? true : false;
29374     },
29375     
29376     setActivePanel : function(panel){
29377         panel = this.getPanel(panel);
29378         if(this.activePanel && this.activePanel != panel){
29379             this.activePanel.setActiveState(false);
29380             this.activePanel.getEl().setLeftTop(-10000,-10000);
29381         }
29382         this.activePanel = panel;
29383         panel.setActiveState(true);
29384         if(this.box){
29385             panel.setSize(this.box.width, this.box.height);
29386         }
29387         this.fireEvent("panelactivated", this, panel);
29388         this.fireEvent("invalidated");
29389     },
29390     
29391     /**
29392      * Show the specified panel.
29393      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29394      * @return {Roo.ContentPanel} The shown panel or null
29395      */
29396     showPanel : function(panel){
29397         if(panel = this.getPanel(panel)){
29398             this.setActivePanel(panel);
29399         }
29400         return panel;
29401     },
29402     
29403     /**
29404      * Get the active panel for this region.
29405      * @return {Roo.ContentPanel} The active panel or null
29406      */
29407     getActivePanel : function(){
29408         return this.activePanel;
29409     },
29410     
29411     /**
29412      * Add the passed ContentPanel(s)
29413      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29414      * @return {Roo.ContentPanel} The panel added (if only one was added)
29415      */
29416     add : function(panel){
29417         if(arguments.length > 1){
29418             for(var i = 0, len = arguments.length; i < len; i++) {
29419                 this.add(arguments[i]);
29420             }
29421             return null;
29422         }
29423         if(this.hasPanel(panel)){
29424             this.showPanel(panel);
29425             return panel;
29426         }
29427         var el = panel.getEl();
29428         if(el.dom.parentNode != this.mgr.el.dom){
29429             this.mgr.el.dom.appendChild(el.dom);
29430         }
29431         if(panel.setRegion){
29432             panel.setRegion(this);
29433         }
29434         this.panels.add(panel);
29435         el.setStyle("position", "absolute");
29436         if(!panel.background){
29437             this.setActivePanel(panel);
29438             if(this.config.initialSize && this.panels.getCount()==1){
29439                 this.resizeTo(this.config.initialSize);
29440             }
29441         }
29442         this.fireEvent("paneladded", this, panel);
29443         return panel;
29444     },
29445     
29446     /**
29447      * Returns true if the panel is in this region.
29448      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29449      * @return {Boolean}
29450      */
29451     hasPanel : function(panel){
29452         if(typeof panel == "object"){ // must be panel obj
29453             panel = panel.getId();
29454         }
29455         return this.getPanel(panel) ? true : false;
29456     },
29457     
29458     /**
29459      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29460      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29461      * @param {Boolean} preservePanel Overrides the config preservePanel option
29462      * @return {Roo.ContentPanel} The panel that was removed
29463      */
29464     remove : function(panel, preservePanel){
29465         panel = this.getPanel(panel);
29466         if(!panel){
29467             return null;
29468         }
29469         var e = {};
29470         this.fireEvent("beforeremove", this, panel, e);
29471         if(e.cancel === true){
29472             return null;
29473         }
29474         var panelId = panel.getId();
29475         this.panels.removeKey(panelId);
29476         return panel;
29477     },
29478     
29479     /**
29480      * Returns the panel specified or null if it's not in this region.
29481      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29482      * @return {Roo.ContentPanel}
29483      */
29484     getPanel : function(id){
29485         if(typeof id == "object"){ // must be panel obj
29486             return id;
29487         }
29488         return this.panels.get(id);
29489     },
29490     
29491     /**
29492      * Returns this regions position (north/south/east/west/center).
29493      * @return {String} 
29494      */
29495     getPosition: function(){
29496         return this.position;    
29497     }
29498 });/*
29499  * Based on:
29500  * Ext JS Library 1.1.1
29501  * Copyright(c) 2006-2007, Ext JS, LLC.
29502  *
29503  * Originally Released Under LGPL - original licence link has changed is not relivant.
29504  *
29505  * Fork - LGPL
29506  * <script type="text/javascript">
29507  */
29508  
29509 /**
29510  * @class Roo.LayoutRegion
29511  * @extends Roo.BasicLayoutRegion
29512  * This class represents a region in a layout manager.
29513  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29514  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29515  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29516  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29517  * @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})
29518  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29519  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29520  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29521  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29522  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29523  * @cfg {String}    title           The title for the region (overrides panel titles)
29524  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29525  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29526  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29527  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29528  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29529  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29530  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29531  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29532  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29533  * @cfg {Boolean}   showPin         True to show a pin button
29534  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29535  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29536  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29537  * @cfg {Number}    width           For East/West panels
29538  * @cfg {Number}    height          For North/South panels
29539  * @cfg {Boolean}   split           To show the splitter
29540  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29541  */
29542 Roo.LayoutRegion = function(mgr, config, pos){
29543     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29544     var dh = Roo.DomHelper;
29545     /** This region's container element 
29546     * @type Roo.Element */
29547     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29548     /** This region's title element 
29549     * @type Roo.Element */
29550
29551     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29552         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29553         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29554     ]}, true);
29555     this.titleEl.enableDisplayMode();
29556     /** This region's title text element 
29557     * @type HTMLElement */
29558     this.titleTextEl = this.titleEl.dom.firstChild;
29559     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29560     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29561     this.closeBtn.enableDisplayMode();
29562     this.closeBtn.on("click", this.closeClicked, this);
29563     this.closeBtn.hide();
29564
29565     this.createBody(config);
29566     this.visible = true;
29567     this.collapsed = false;
29568
29569     if(config.hideWhenEmpty){
29570         this.hide();
29571         this.on("paneladded", this.validateVisibility, this);
29572         this.on("panelremoved", this.validateVisibility, this);
29573     }
29574     this.applyConfig(config);
29575 };
29576
29577 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29578
29579     createBody : function(){
29580         /** This region's body element 
29581         * @type Roo.Element */
29582         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29583     },
29584
29585     applyConfig : function(c){
29586         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29587             var dh = Roo.DomHelper;
29588             if(c.titlebar !== false){
29589                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29590                 this.collapseBtn.on("click", this.collapse, this);
29591                 this.collapseBtn.enableDisplayMode();
29592
29593                 if(c.showPin === true || this.showPin){
29594                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29595                     this.stickBtn.enableDisplayMode();
29596                     this.stickBtn.on("click", this.expand, this);
29597                     this.stickBtn.hide();
29598                 }
29599             }
29600             /** This region's collapsed element
29601             * @type Roo.Element */
29602             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29603                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29604             ]}, true);
29605             if(c.floatable !== false){
29606                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29607                this.collapsedEl.on("click", this.collapseClick, this);
29608             }
29609
29610             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29611                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29612                    id: "message", unselectable: "on", style:{"float":"left"}});
29613                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29614              }
29615             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29616             this.expandBtn.on("click", this.expand, this);
29617         }
29618         if(this.collapseBtn){
29619             this.collapseBtn.setVisible(c.collapsible == true);
29620         }
29621         this.cmargins = c.cmargins || this.cmargins ||
29622                          (this.position == "west" || this.position == "east" ?
29623                              {top: 0, left: 2, right:2, bottom: 0} :
29624                              {top: 2, left: 0, right:0, bottom: 2});
29625         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29626         this.bottomTabs = c.tabPosition != "top";
29627         this.autoScroll = c.autoScroll || false;
29628         if(this.autoScroll){
29629             this.bodyEl.setStyle("overflow", "auto");
29630         }else{
29631             this.bodyEl.setStyle("overflow", "hidden");
29632         }
29633         //if(c.titlebar !== false){
29634             if((!c.titlebar && !c.title) || c.titlebar === false){
29635                 this.titleEl.hide();
29636             }else{
29637                 this.titleEl.show();
29638                 if(c.title){
29639                     this.titleTextEl.innerHTML = c.title;
29640                 }
29641             }
29642         //}
29643         this.duration = c.duration || .30;
29644         this.slideDuration = c.slideDuration || .45;
29645         this.config = c;
29646         if(c.collapsed){
29647             this.collapse(true);
29648         }
29649         if(c.hidden){
29650             this.hide();
29651         }
29652     },
29653     /**
29654      * Returns true if this region is currently visible.
29655      * @return {Boolean}
29656      */
29657     isVisible : function(){
29658         return this.visible;
29659     },
29660
29661     /**
29662      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29663      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29664      */
29665     setCollapsedTitle : function(title){
29666         title = title || "&#160;";
29667         if(this.collapsedTitleTextEl){
29668             this.collapsedTitleTextEl.innerHTML = title;
29669         }
29670     },
29671
29672     getBox : function(){
29673         var b;
29674         if(!this.collapsed){
29675             b = this.el.getBox(false, true);
29676         }else{
29677             b = this.collapsedEl.getBox(false, true);
29678         }
29679         return b;
29680     },
29681
29682     getMargins : function(){
29683         return this.collapsed ? this.cmargins : this.margins;
29684     },
29685
29686     highlight : function(){
29687         this.el.addClass("x-layout-panel-dragover");
29688     },
29689
29690     unhighlight : function(){
29691         this.el.removeClass("x-layout-panel-dragover");
29692     },
29693
29694     updateBox : function(box){
29695         this.box = box;
29696         if(!this.collapsed){
29697             this.el.dom.style.left = box.x + "px";
29698             this.el.dom.style.top = box.y + "px";
29699             this.updateBody(box.width, box.height);
29700         }else{
29701             this.collapsedEl.dom.style.left = box.x + "px";
29702             this.collapsedEl.dom.style.top = box.y + "px";
29703             this.collapsedEl.setSize(box.width, box.height);
29704         }
29705         if(this.tabs){
29706             this.tabs.autoSizeTabs();
29707         }
29708     },
29709
29710     updateBody : function(w, h){
29711         if(w !== null){
29712             this.el.setWidth(w);
29713             w -= this.el.getBorderWidth("rl");
29714             if(this.config.adjustments){
29715                 w += this.config.adjustments[0];
29716             }
29717         }
29718         if(h !== null){
29719             this.el.setHeight(h);
29720             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29721             h -= this.el.getBorderWidth("tb");
29722             if(this.config.adjustments){
29723                 h += this.config.adjustments[1];
29724             }
29725             this.bodyEl.setHeight(h);
29726             if(this.tabs){
29727                 h = this.tabs.syncHeight(h);
29728             }
29729         }
29730         if(this.panelSize){
29731             w = w !== null ? w : this.panelSize.width;
29732             h = h !== null ? h : this.panelSize.height;
29733         }
29734         if(this.activePanel){
29735             var el = this.activePanel.getEl();
29736             w = w !== null ? w : el.getWidth();
29737             h = h !== null ? h : el.getHeight();
29738             this.panelSize = {width: w, height: h};
29739             this.activePanel.setSize(w, h);
29740         }
29741         if(Roo.isIE && this.tabs){
29742             this.tabs.el.repaint();
29743         }
29744     },
29745
29746     /**
29747      * Returns the container element for this region.
29748      * @return {Roo.Element}
29749      */
29750     getEl : function(){
29751         return this.el;
29752     },
29753
29754     /**
29755      * Hides this region.
29756      */
29757     hide : function(){
29758         if(!this.collapsed){
29759             this.el.dom.style.left = "-2000px";
29760             this.el.hide();
29761         }else{
29762             this.collapsedEl.dom.style.left = "-2000px";
29763             this.collapsedEl.hide();
29764         }
29765         this.visible = false;
29766         this.fireEvent("visibilitychange", this, false);
29767     },
29768
29769     /**
29770      * Shows this region if it was previously hidden.
29771      */
29772     show : function(){
29773         if(!this.collapsed){
29774             this.el.show();
29775         }else{
29776             this.collapsedEl.show();
29777         }
29778         this.visible = true;
29779         this.fireEvent("visibilitychange", this, true);
29780     },
29781
29782     closeClicked : function(){
29783         if(this.activePanel){
29784             this.remove(this.activePanel);
29785         }
29786     },
29787
29788     collapseClick : function(e){
29789         if(this.isSlid){
29790            e.stopPropagation();
29791            this.slideIn();
29792         }else{
29793            e.stopPropagation();
29794            this.slideOut();
29795         }
29796     },
29797
29798     /**
29799      * Collapses this region.
29800      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29801      */
29802     collapse : function(skipAnim, skipCheck = false){
29803         if(this.collapsed) {
29804             return;
29805         }
29806         
29807         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29808             
29809             this.collapsed = true;
29810             if(this.split){
29811                 this.split.el.hide();
29812             }
29813             if(this.config.animate && skipAnim !== true){
29814                 this.fireEvent("invalidated", this);
29815                 this.animateCollapse();
29816             }else{
29817                 this.el.setLocation(-20000,-20000);
29818                 this.el.hide();
29819                 this.collapsedEl.show();
29820                 this.fireEvent("collapsed", this);
29821                 this.fireEvent("invalidated", this);
29822             }
29823         }
29824         
29825     },
29826
29827     animateCollapse : function(){
29828         // overridden
29829     },
29830
29831     /**
29832      * Expands this region if it was previously collapsed.
29833      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29834      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29835      */
29836     expand : function(e, skipAnim){
29837         if(e) {
29838             e.stopPropagation();
29839         }
29840         if(!this.collapsed || this.el.hasActiveFx()) {
29841             return;
29842         }
29843         if(this.isSlid){
29844             this.afterSlideIn();
29845             skipAnim = true;
29846         }
29847         this.collapsed = false;
29848         if(this.config.animate && skipAnim !== true){
29849             this.animateExpand();
29850         }else{
29851             this.el.show();
29852             if(this.split){
29853                 this.split.el.show();
29854             }
29855             this.collapsedEl.setLocation(-2000,-2000);
29856             this.collapsedEl.hide();
29857             this.fireEvent("invalidated", this);
29858             this.fireEvent("expanded", this);
29859         }
29860     },
29861
29862     animateExpand : function(){
29863         // overridden
29864     },
29865
29866     initTabs : function()
29867     {
29868         this.bodyEl.setStyle("overflow", "hidden");
29869         var ts = new Roo.TabPanel(
29870                 this.bodyEl.dom,
29871                 {
29872                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29873                     disableTooltips: this.config.disableTabTips,
29874                     toolbar : this.config.toolbar
29875                 }
29876         );
29877         if(this.config.hideTabs){
29878             ts.stripWrap.setDisplayed(false);
29879         }
29880         this.tabs = ts;
29881         ts.resizeTabs = this.config.resizeTabs === true;
29882         ts.minTabWidth = this.config.minTabWidth || 40;
29883         ts.maxTabWidth = this.config.maxTabWidth || 250;
29884         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29885         ts.monitorResize = false;
29886         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29887         ts.bodyEl.addClass('x-layout-tabs-body');
29888         this.panels.each(this.initPanelAsTab, this);
29889     },
29890
29891     initPanelAsTab : function(panel){
29892         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29893                     this.config.closeOnTab && panel.isClosable());
29894         if(panel.tabTip !== undefined){
29895             ti.setTooltip(panel.tabTip);
29896         }
29897         ti.on("activate", function(){
29898               this.setActivePanel(panel);
29899         }, this);
29900         if(this.config.closeOnTab){
29901             ti.on("beforeclose", function(t, e){
29902                 e.cancel = true;
29903                 this.remove(panel);
29904             }, this);
29905         }
29906         return ti;
29907     },
29908
29909     updatePanelTitle : function(panel, title){
29910         if(this.activePanel == panel){
29911             this.updateTitle(title);
29912         }
29913         if(this.tabs){
29914             var ti = this.tabs.getTab(panel.getEl().id);
29915             ti.setText(title);
29916             if(panel.tabTip !== undefined){
29917                 ti.setTooltip(panel.tabTip);
29918             }
29919         }
29920     },
29921
29922     updateTitle : function(title){
29923         if(this.titleTextEl && !this.config.title){
29924             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29925         }
29926     },
29927
29928     setActivePanel : function(panel){
29929         panel = this.getPanel(panel);
29930         if(this.activePanel && this.activePanel != panel){
29931             this.activePanel.setActiveState(false);
29932         }
29933         this.activePanel = panel;
29934         panel.setActiveState(true);
29935         if(this.panelSize){
29936             panel.setSize(this.panelSize.width, this.panelSize.height);
29937         }
29938         if(this.closeBtn){
29939             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29940         }
29941         this.updateTitle(panel.getTitle());
29942         if(this.tabs){
29943             this.fireEvent("invalidated", this);
29944         }
29945         this.fireEvent("panelactivated", this, panel);
29946     },
29947
29948     /**
29949      * Shows the specified panel.
29950      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29951      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29952      */
29953     showPanel : function(panel)
29954     {
29955         panel = this.getPanel(panel);
29956         if(panel){
29957             if(this.tabs){
29958                 var tab = this.tabs.getTab(panel.getEl().id);
29959                 if(tab.isHidden()){
29960                     this.tabs.unhideTab(tab.id);
29961                 }
29962                 tab.activate();
29963             }else{
29964                 this.setActivePanel(panel);
29965             }
29966         }
29967         return panel;
29968     },
29969
29970     /**
29971      * Get the active panel for this region.
29972      * @return {Roo.ContentPanel} The active panel or null
29973      */
29974     getActivePanel : function(){
29975         return this.activePanel;
29976     },
29977
29978     validateVisibility : function(){
29979         if(this.panels.getCount() < 1){
29980             this.updateTitle("&#160;");
29981             this.closeBtn.hide();
29982             this.hide();
29983         }else{
29984             if(!this.isVisible()){
29985                 this.show();
29986             }
29987         }
29988     },
29989
29990     /**
29991      * Adds the passed ContentPanel(s) to this region.
29992      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29993      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29994      */
29995     add : function(panel){
29996         if(arguments.length > 1){
29997             for(var i = 0, len = arguments.length; i < len; i++) {
29998                 this.add(arguments[i]);
29999             }
30000             return null;
30001         }
30002         if(this.hasPanel(panel)){
30003             this.showPanel(panel);
30004             return panel;
30005         }
30006         panel.setRegion(this);
30007         this.panels.add(panel);
30008         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30009             this.bodyEl.dom.appendChild(panel.getEl().dom);
30010             if(panel.background !== true){
30011                 this.setActivePanel(panel);
30012             }
30013             this.fireEvent("paneladded", this, panel);
30014             return panel;
30015         }
30016         if(!this.tabs){
30017             this.initTabs();
30018         }else{
30019             this.initPanelAsTab(panel);
30020         }
30021         if(panel.background !== true){
30022             this.tabs.activate(panel.getEl().id);
30023         }
30024         this.fireEvent("paneladded", this, panel);
30025         return panel;
30026     },
30027
30028     /**
30029      * Hides the tab for the specified panel.
30030      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30031      */
30032     hidePanel : function(panel){
30033         if(this.tabs && (panel = this.getPanel(panel))){
30034             this.tabs.hideTab(panel.getEl().id);
30035         }
30036     },
30037
30038     /**
30039      * Unhides the tab for a previously hidden panel.
30040      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30041      */
30042     unhidePanel : function(panel){
30043         if(this.tabs && (panel = this.getPanel(panel))){
30044             this.tabs.unhideTab(panel.getEl().id);
30045         }
30046     },
30047
30048     clearPanels : function(){
30049         while(this.panels.getCount() > 0){
30050              this.remove(this.panels.first());
30051         }
30052     },
30053
30054     /**
30055      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30056      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30057      * @param {Boolean} preservePanel Overrides the config preservePanel option
30058      * @return {Roo.ContentPanel} The panel that was removed
30059      */
30060     remove : function(panel, preservePanel){
30061         panel = this.getPanel(panel);
30062         if(!panel){
30063             return null;
30064         }
30065         var e = {};
30066         this.fireEvent("beforeremove", this, panel, e);
30067         if(e.cancel === true){
30068             return null;
30069         }
30070         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30071         var panelId = panel.getId();
30072         this.panels.removeKey(panelId);
30073         if(preservePanel){
30074             document.body.appendChild(panel.getEl().dom);
30075         }
30076         if(this.tabs){
30077             this.tabs.removeTab(panel.getEl().id);
30078         }else if (!preservePanel){
30079             this.bodyEl.dom.removeChild(panel.getEl().dom);
30080         }
30081         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30082             var p = this.panels.first();
30083             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30084             tempEl.appendChild(p.getEl().dom);
30085             this.bodyEl.update("");
30086             this.bodyEl.dom.appendChild(p.getEl().dom);
30087             tempEl = null;
30088             this.updateTitle(p.getTitle());
30089             this.tabs = null;
30090             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30091             this.setActivePanel(p);
30092         }
30093         panel.setRegion(null);
30094         if(this.activePanel == panel){
30095             this.activePanel = null;
30096         }
30097         if(this.config.autoDestroy !== false && preservePanel !== true){
30098             try{panel.destroy();}catch(e){}
30099         }
30100         this.fireEvent("panelremoved", this, panel);
30101         return panel;
30102     },
30103
30104     /**
30105      * Returns the TabPanel component used by this region
30106      * @return {Roo.TabPanel}
30107      */
30108     getTabs : function(){
30109         return this.tabs;
30110     },
30111
30112     createTool : function(parentEl, className){
30113         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30114             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30115         btn.addClassOnOver("x-layout-tools-button-over");
30116         return btn;
30117     }
30118 });/*
30119  * Based on:
30120  * Ext JS Library 1.1.1
30121  * Copyright(c) 2006-2007, Ext JS, LLC.
30122  *
30123  * Originally Released Under LGPL - original licence link has changed is not relivant.
30124  *
30125  * Fork - LGPL
30126  * <script type="text/javascript">
30127  */
30128  
30129
30130
30131 /**
30132  * @class Roo.SplitLayoutRegion
30133  * @extends Roo.LayoutRegion
30134  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30135  */
30136 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30137     this.cursor = cursor;
30138     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30139 };
30140
30141 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30142     splitTip : "Drag to resize.",
30143     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30144     useSplitTips : false,
30145
30146     applyConfig : function(config){
30147         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30148         if(config.split){
30149             if(!this.split){
30150                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30151                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30152                 /** The SplitBar for this region 
30153                 * @type Roo.SplitBar */
30154                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30155                 this.split.on("moved", this.onSplitMove, this);
30156                 this.split.useShim = config.useShim === true;
30157                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30158                 if(this.useSplitTips){
30159                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30160                 }
30161                 if(config.collapsible){
30162                     this.split.el.on("dblclick", this.collapse,  this);
30163                 }
30164             }
30165             if(typeof config.minSize != "undefined"){
30166                 this.split.minSize = config.minSize;
30167             }
30168             if(typeof config.maxSize != "undefined"){
30169                 this.split.maxSize = config.maxSize;
30170             }
30171             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30172                 this.hideSplitter();
30173             }
30174         }
30175     },
30176
30177     getHMaxSize : function(){
30178          var cmax = this.config.maxSize || 10000;
30179          var center = this.mgr.getRegion("center");
30180          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30181     },
30182
30183     getVMaxSize : function(){
30184          var cmax = this.config.maxSize || 10000;
30185          var center = this.mgr.getRegion("center");
30186          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30187     },
30188
30189     onSplitMove : function(split, newSize){
30190         this.fireEvent("resized", this, newSize);
30191     },
30192     
30193     /** 
30194      * Returns the {@link Roo.SplitBar} for this region.
30195      * @return {Roo.SplitBar}
30196      */
30197     getSplitBar : function(){
30198         return this.split;
30199     },
30200     
30201     hide : function(){
30202         this.hideSplitter();
30203         Roo.SplitLayoutRegion.superclass.hide.call(this);
30204     },
30205
30206     hideSplitter : function(){
30207         if(this.split){
30208             this.split.el.setLocation(-2000,-2000);
30209             this.split.el.hide();
30210         }
30211     },
30212
30213     show : function(){
30214         if(this.split){
30215             this.split.el.show();
30216         }
30217         Roo.SplitLayoutRegion.superclass.show.call(this);
30218     },
30219     
30220     beforeSlide: function(){
30221         if(Roo.isGecko){// firefox overflow auto bug workaround
30222             this.bodyEl.clip();
30223             if(this.tabs) {
30224                 this.tabs.bodyEl.clip();
30225             }
30226             if(this.activePanel){
30227                 this.activePanel.getEl().clip();
30228                 
30229                 if(this.activePanel.beforeSlide){
30230                     this.activePanel.beforeSlide();
30231                 }
30232             }
30233         }
30234     },
30235     
30236     afterSlide : function(){
30237         if(Roo.isGecko){// firefox overflow auto bug workaround
30238             this.bodyEl.unclip();
30239             if(this.tabs) {
30240                 this.tabs.bodyEl.unclip();
30241             }
30242             if(this.activePanel){
30243                 this.activePanel.getEl().unclip();
30244                 if(this.activePanel.afterSlide){
30245                     this.activePanel.afterSlide();
30246                 }
30247             }
30248         }
30249     },
30250
30251     initAutoHide : function(){
30252         if(this.autoHide !== false){
30253             if(!this.autoHideHd){
30254                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30255                 this.autoHideHd = {
30256                     "mouseout": function(e){
30257                         if(!e.within(this.el, true)){
30258                             st.delay(500);
30259                         }
30260                     },
30261                     "mouseover" : function(e){
30262                         st.cancel();
30263                     },
30264                     scope : this
30265                 };
30266             }
30267             this.el.on(this.autoHideHd);
30268         }
30269     },
30270
30271     clearAutoHide : function(){
30272         if(this.autoHide !== false){
30273             this.el.un("mouseout", this.autoHideHd.mouseout);
30274             this.el.un("mouseover", this.autoHideHd.mouseover);
30275         }
30276     },
30277
30278     clearMonitor : function(){
30279         Roo.get(document).un("click", this.slideInIf, this);
30280     },
30281
30282     // these names are backwards but not changed for compat
30283     slideOut : function(){
30284         if(this.isSlid || this.el.hasActiveFx()){
30285             return;
30286         }
30287         this.isSlid = true;
30288         if(this.collapseBtn){
30289             this.collapseBtn.hide();
30290         }
30291         this.closeBtnState = this.closeBtn.getStyle('display');
30292         this.closeBtn.hide();
30293         if(this.stickBtn){
30294             this.stickBtn.show();
30295         }
30296         this.el.show();
30297         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30298         this.beforeSlide();
30299         this.el.setStyle("z-index", 10001);
30300         this.el.slideIn(this.getSlideAnchor(), {
30301             callback: function(){
30302                 this.afterSlide();
30303                 this.initAutoHide();
30304                 Roo.get(document).on("click", this.slideInIf, this);
30305                 this.fireEvent("slideshow", this);
30306             },
30307             scope: this,
30308             block: true
30309         });
30310     },
30311
30312     afterSlideIn : function(){
30313         this.clearAutoHide();
30314         this.isSlid = false;
30315         this.clearMonitor();
30316         this.el.setStyle("z-index", "");
30317         if(this.collapseBtn){
30318             this.collapseBtn.show();
30319         }
30320         this.closeBtn.setStyle('display', this.closeBtnState);
30321         if(this.stickBtn){
30322             this.stickBtn.hide();
30323         }
30324         this.fireEvent("slidehide", this);
30325     },
30326
30327     slideIn : function(cb){
30328         if(!this.isSlid || this.el.hasActiveFx()){
30329             Roo.callback(cb);
30330             return;
30331         }
30332         this.isSlid = false;
30333         this.beforeSlide();
30334         this.el.slideOut(this.getSlideAnchor(), {
30335             callback: function(){
30336                 this.el.setLeftTop(-10000, -10000);
30337                 this.afterSlide();
30338                 this.afterSlideIn();
30339                 Roo.callback(cb);
30340             },
30341             scope: this,
30342             block: true
30343         });
30344     },
30345     
30346     slideInIf : function(e){
30347         if(!e.within(this.el)){
30348             this.slideIn();
30349         }
30350     },
30351
30352     animateCollapse : function(){
30353         this.beforeSlide();
30354         this.el.setStyle("z-index", 20000);
30355         var anchor = this.getSlideAnchor();
30356         this.el.slideOut(anchor, {
30357             callback : function(){
30358                 this.el.setStyle("z-index", "");
30359                 this.collapsedEl.slideIn(anchor, {duration:.3});
30360                 this.afterSlide();
30361                 this.el.setLocation(-10000,-10000);
30362                 this.el.hide();
30363                 this.fireEvent("collapsed", this);
30364             },
30365             scope: this,
30366             block: true
30367         });
30368     },
30369
30370     animateExpand : function(){
30371         this.beforeSlide();
30372         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30373         this.el.setStyle("z-index", 20000);
30374         this.collapsedEl.hide({
30375             duration:.1
30376         });
30377         this.el.slideIn(this.getSlideAnchor(), {
30378             callback : function(){
30379                 this.el.setStyle("z-index", "");
30380                 this.afterSlide();
30381                 if(this.split){
30382                     this.split.el.show();
30383                 }
30384                 this.fireEvent("invalidated", this);
30385                 this.fireEvent("expanded", this);
30386             },
30387             scope: this,
30388             block: true
30389         });
30390     },
30391
30392     anchors : {
30393         "west" : "left",
30394         "east" : "right",
30395         "north" : "top",
30396         "south" : "bottom"
30397     },
30398
30399     sanchors : {
30400         "west" : "l",
30401         "east" : "r",
30402         "north" : "t",
30403         "south" : "b"
30404     },
30405
30406     canchors : {
30407         "west" : "tl-tr",
30408         "east" : "tr-tl",
30409         "north" : "tl-bl",
30410         "south" : "bl-tl"
30411     },
30412
30413     getAnchor : function(){
30414         return this.anchors[this.position];
30415     },
30416
30417     getCollapseAnchor : function(){
30418         return this.canchors[this.position];
30419     },
30420
30421     getSlideAnchor : function(){
30422         return this.sanchors[this.position];
30423     },
30424
30425     getAlignAdj : function(){
30426         var cm = this.cmargins;
30427         switch(this.position){
30428             case "west":
30429                 return [0, 0];
30430             break;
30431             case "east":
30432                 return [0, 0];
30433             break;
30434             case "north":
30435                 return [0, 0];
30436             break;
30437             case "south":
30438                 return [0, 0];
30439             break;
30440         }
30441     },
30442
30443     getExpandAdj : function(){
30444         var c = this.collapsedEl, cm = this.cmargins;
30445         switch(this.position){
30446             case "west":
30447                 return [-(cm.right+c.getWidth()+cm.left), 0];
30448             break;
30449             case "east":
30450                 return [cm.right+c.getWidth()+cm.left, 0];
30451             break;
30452             case "north":
30453                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30454             break;
30455             case "south":
30456                 return [0, cm.top+cm.bottom+c.getHeight()];
30457             break;
30458         }
30459     }
30460 });/*
30461  * Based on:
30462  * Ext JS Library 1.1.1
30463  * Copyright(c) 2006-2007, Ext JS, LLC.
30464  *
30465  * Originally Released Under LGPL - original licence link has changed is not relivant.
30466  *
30467  * Fork - LGPL
30468  * <script type="text/javascript">
30469  */
30470 /*
30471  * These classes are private internal classes
30472  */
30473 Roo.CenterLayoutRegion = function(mgr, config){
30474     Roo.LayoutRegion.call(this, mgr, config, "center");
30475     this.visible = true;
30476     this.minWidth = config.minWidth || 20;
30477     this.minHeight = config.minHeight || 20;
30478 };
30479
30480 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30481     hide : function(){
30482         // center panel can't be hidden
30483     },
30484     
30485     show : function(){
30486         // center panel can't be hidden
30487     },
30488     
30489     getMinWidth: function(){
30490         return this.minWidth;
30491     },
30492     
30493     getMinHeight: function(){
30494         return this.minHeight;
30495     }
30496 });
30497
30498
30499 Roo.NorthLayoutRegion = function(mgr, config){
30500     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30501     if(this.split){
30502         this.split.placement = Roo.SplitBar.TOP;
30503         this.split.orientation = Roo.SplitBar.VERTICAL;
30504         this.split.el.addClass("x-layout-split-v");
30505     }
30506     var size = config.initialSize || config.height;
30507     if(typeof size != "undefined"){
30508         this.el.setHeight(size);
30509     }
30510 };
30511 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30512     orientation: Roo.SplitBar.VERTICAL,
30513     getBox : function(){
30514         if(this.collapsed){
30515             return this.collapsedEl.getBox();
30516         }
30517         var box = this.el.getBox();
30518         if(this.split){
30519             box.height += this.split.el.getHeight();
30520         }
30521         return box;
30522     },
30523     
30524     updateBox : function(box){
30525         if(this.split && !this.collapsed){
30526             box.height -= this.split.el.getHeight();
30527             this.split.el.setLeft(box.x);
30528             this.split.el.setTop(box.y+box.height);
30529             this.split.el.setWidth(box.width);
30530         }
30531         if(this.collapsed){
30532             this.updateBody(box.width, null);
30533         }
30534         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30535     }
30536 });
30537
30538 Roo.SouthLayoutRegion = function(mgr, config){
30539     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30540     if(this.split){
30541         this.split.placement = Roo.SplitBar.BOTTOM;
30542         this.split.orientation = Roo.SplitBar.VERTICAL;
30543         this.split.el.addClass("x-layout-split-v");
30544     }
30545     var size = config.initialSize || config.height;
30546     if(typeof size != "undefined"){
30547         this.el.setHeight(size);
30548     }
30549 };
30550 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30551     orientation: Roo.SplitBar.VERTICAL,
30552     getBox : function(){
30553         if(this.collapsed){
30554             return this.collapsedEl.getBox();
30555         }
30556         var box = this.el.getBox();
30557         if(this.split){
30558             var sh = this.split.el.getHeight();
30559             box.height += sh;
30560             box.y -= sh;
30561         }
30562         return box;
30563     },
30564     
30565     updateBox : function(box){
30566         if(this.split && !this.collapsed){
30567             var sh = this.split.el.getHeight();
30568             box.height -= sh;
30569             box.y += sh;
30570             this.split.el.setLeft(box.x);
30571             this.split.el.setTop(box.y-sh);
30572             this.split.el.setWidth(box.width);
30573         }
30574         if(this.collapsed){
30575             this.updateBody(box.width, null);
30576         }
30577         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30578     }
30579 });
30580
30581 Roo.EastLayoutRegion = function(mgr, config){
30582     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30583     if(this.split){
30584         this.split.placement = Roo.SplitBar.RIGHT;
30585         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30586         this.split.el.addClass("x-layout-split-h");
30587     }
30588     var size = config.initialSize || config.width;
30589     if(typeof size != "undefined"){
30590         this.el.setWidth(size);
30591     }
30592 };
30593 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30594     orientation: Roo.SplitBar.HORIZONTAL,
30595     getBox : function(){
30596         if(this.collapsed){
30597             return this.collapsedEl.getBox();
30598         }
30599         var box = this.el.getBox();
30600         if(this.split){
30601             var sw = this.split.el.getWidth();
30602             box.width += sw;
30603             box.x -= sw;
30604         }
30605         return box;
30606     },
30607
30608     updateBox : function(box){
30609         if(this.split && !this.collapsed){
30610             var sw = this.split.el.getWidth();
30611             box.width -= sw;
30612             this.split.el.setLeft(box.x);
30613             this.split.el.setTop(box.y);
30614             this.split.el.setHeight(box.height);
30615             box.x += sw;
30616         }
30617         if(this.collapsed){
30618             this.updateBody(null, box.height);
30619         }
30620         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30621     }
30622 });
30623
30624 Roo.WestLayoutRegion = function(mgr, config){
30625     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30626     if(this.split){
30627         this.split.placement = Roo.SplitBar.LEFT;
30628         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30629         this.split.el.addClass("x-layout-split-h");
30630     }
30631     var size = config.initialSize || config.width;
30632     if(typeof size != "undefined"){
30633         this.el.setWidth(size);
30634     }
30635 };
30636 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30637     orientation: Roo.SplitBar.HORIZONTAL,
30638     getBox : function(){
30639         if(this.collapsed){
30640             return this.collapsedEl.getBox();
30641         }
30642         var box = this.el.getBox();
30643         if(this.split){
30644             box.width += this.split.el.getWidth();
30645         }
30646         return box;
30647     },
30648     
30649     updateBox : function(box){
30650         if(this.split && !this.collapsed){
30651             var sw = this.split.el.getWidth();
30652             box.width -= sw;
30653             this.split.el.setLeft(box.x+box.width);
30654             this.split.el.setTop(box.y);
30655             this.split.el.setHeight(box.height);
30656         }
30657         if(this.collapsed){
30658             this.updateBody(null, box.height);
30659         }
30660         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30661     }
30662 });
30663 /*
30664  * Based on:
30665  * Ext JS Library 1.1.1
30666  * Copyright(c) 2006-2007, Ext JS, LLC.
30667  *
30668  * Originally Released Under LGPL - original licence link has changed is not relivant.
30669  *
30670  * Fork - LGPL
30671  * <script type="text/javascript">
30672  */
30673  
30674  
30675 /*
30676  * Private internal class for reading and applying state
30677  */
30678 Roo.LayoutStateManager = function(layout){
30679      // default empty state
30680      this.state = {
30681         north: {},
30682         south: {},
30683         east: {},
30684         west: {}       
30685     };
30686 };
30687
30688 Roo.LayoutStateManager.prototype = {
30689     init : function(layout, provider){
30690         this.provider = provider;
30691         var state = provider.get(layout.id+"-layout-state");
30692         if(state){
30693             var wasUpdating = layout.isUpdating();
30694             if(!wasUpdating){
30695                 layout.beginUpdate();
30696             }
30697             for(var key in state){
30698                 if(typeof state[key] != "function"){
30699                     var rstate = state[key];
30700                     var r = layout.getRegion(key);
30701                     if(r && rstate){
30702                         if(rstate.size){
30703                             r.resizeTo(rstate.size);
30704                         }
30705                         if(rstate.collapsed == true){
30706                             r.collapse(true);
30707                         }else{
30708                             r.expand(null, true);
30709                         }
30710                     }
30711                 }
30712             }
30713             if(!wasUpdating){
30714                 layout.endUpdate();
30715             }
30716             this.state = state; 
30717         }
30718         this.layout = layout;
30719         layout.on("regionresized", this.onRegionResized, this);
30720         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30721         layout.on("regionexpanded", this.onRegionExpanded, this);
30722     },
30723     
30724     storeState : function(){
30725         this.provider.set(this.layout.id+"-layout-state", this.state);
30726     },
30727     
30728     onRegionResized : function(region, newSize){
30729         this.state[region.getPosition()].size = newSize;
30730         this.storeState();
30731     },
30732     
30733     onRegionCollapsed : function(region){
30734         this.state[region.getPosition()].collapsed = true;
30735         this.storeState();
30736     },
30737     
30738     onRegionExpanded : function(region){
30739         this.state[region.getPosition()].collapsed = false;
30740         this.storeState();
30741     }
30742 };/*
30743  * Based on:
30744  * Ext JS Library 1.1.1
30745  * Copyright(c) 2006-2007, Ext JS, LLC.
30746  *
30747  * Originally Released Under LGPL - original licence link has changed is not relivant.
30748  *
30749  * Fork - LGPL
30750  * <script type="text/javascript">
30751  */
30752 /**
30753  * @class Roo.ContentPanel
30754  * @extends Roo.util.Observable
30755  * A basic ContentPanel element.
30756  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30757  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30758  * @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
30759  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30760  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30761  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30762  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30763  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30764  * @cfg {String} title          The title for this panel
30765  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30766  * @cfg {String} url            Calls {@link #setUrl} with this value
30767  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30768  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30769  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30770  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30771
30772  * @constructor
30773  * Create a new ContentPanel.
30774  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30775  * @param {String/Object} config A string to set only the title or a config object
30776  * @param {String} content (optional) Set the HTML content for this panel
30777  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30778  */
30779 Roo.ContentPanel = function(el, config, content){
30780     
30781      
30782     /*
30783     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30784         config = el;
30785         el = Roo.id();
30786     }
30787     if (config && config.parentLayout) { 
30788         el = config.parentLayout.el.createChild(); 
30789     }
30790     */
30791     if(el.autoCreate){ // xtype is available if this is called from factory
30792         config = el;
30793         el = Roo.id();
30794     }
30795     this.el = Roo.get(el);
30796     if(!this.el && config && config.autoCreate){
30797         if(typeof config.autoCreate == "object"){
30798             if(!config.autoCreate.id){
30799                 config.autoCreate.id = config.id||el;
30800             }
30801             this.el = Roo.DomHelper.append(document.body,
30802                         config.autoCreate, true);
30803         }else{
30804             this.el = Roo.DomHelper.append(document.body,
30805                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30806         }
30807     }
30808     this.closable = false;
30809     this.loaded = false;
30810     this.active = false;
30811     if(typeof config == "string"){
30812         this.title = config;
30813     }else{
30814         Roo.apply(this, config);
30815     }
30816     
30817     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30818         this.wrapEl = this.el.wrap();
30819         this.toolbar.container = this.el.insertSibling(false, 'before');
30820         this.toolbar = new Roo.Toolbar(this.toolbar);
30821     }
30822     
30823     // xtype created footer. - not sure if will work as we normally have to render first..
30824     if (this.footer && !this.footer.el && this.footer.xtype) {
30825         if (!this.wrapEl) {
30826             this.wrapEl = this.el.wrap();
30827         }
30828     
30829         this.footer.container = this.wrapEl.createChild();
30830          
30831         this.footer = Roo.factory(this.footer, Roo);
30832         
30833     }
30834     
30835     if(this.resizeEl){
30836         this.resizeEl = Roo.get(this.resizeEl, true);
30837     }else{
30838         this.resizeEl = this.el;
30839     }
30840     // handle view.xtype
30841     
30842  
30843     
30844     
30845     this.addEvents({
30846         /**
30847          * @event activate
30848          * Fires when this panel is activated. 
30849          * @param {Roo.ContentPanel} this
30850          */
30851         "activate" : true,
30852         /**
30853          * @event deactivate
30854          * Fires when this panel is activated. 
30855          * @param {Roo.ContentPanel} this
30856          */
30857         "deactivate" : true,
30858
30859         /**
30860          * @event resize
30861          * Fires when this panel is resized if fitToFrame is true.
30862          * @param {Roo.ContentPanel} this
30863          * @param {Number} width The width after any component adjustments
30864          * @param {Number} height The height after any component adjustments
30865          */
30866         "resize" : true,
30867         
30868          /**
30869          * @event render
30870          * Fires when this tab is created
30871          * @param {Roo.ContentPanel} this
30872          */
30873         "render" : true
30874         
30875         
30876         
30877     });
30878     
30879
30880     
30881     
30882     if(this.autoScroll){
30883         this.resizeEl.setStyle("overflow", "auto");
30884     } else {
30885         // fix randome scrolling
30886         this.el.on('scroll', function() {
30887             Roo.log('fix random scolling');
30888             this.scrollTo('top',0); 
30889         });
30890     }
30891     content = content || this.content;
30892     if(content){
30893         this.setContent(content);
30894     }
30895     if(config && config.url){
30896         this.setUrl(this.url, this.params, this.loadOnce);
30897     }
30898     
30899     
30900     
30901     Roo.ContentPanel.superclass.constructor.call(this);
30902     
30903     if (this.view && typeof(this.view.xtype) != 'undefined') {
30904         this.view.el = this.el.appendChild(document.createElement("div"));
30905         this.view = Roo.factory(this.view); 
30906         this.view.render  &&  this.view.render(false, '');  
30907     }
30908     
30909     
30910     this.fireEvent('render', this);
30911 };
30912
30913 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30914     tabTip:'',
30915     setRegion : function(region){
30916         this.region = region;
30917         if(region){
30918            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30919         }else{
30920            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30921         } 
30922     },
30923     
30924     /**
30925      * Returns the toolbar for this Panel if one was configured. 
30926      * @return {Roo.Toolbar} 
30927      */
30928     getToolbar : function(){
30929         return this.toolbar;
30930     },
30931     
30932     setActiveState : function(active){
30933         this.active = active;
30934         if(!active){
30935             this.fireEvent("deactivate", this);
30936         }else{
30937             this.fireEvent("activate", this);
30938         }
30939     },
30940     /**
30941      * Updates this panel's element
30942      * @param {String} content The new content
30943      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30944     */
30945     setContent : function(content, loadScripts){
30946         this.el.update(content, loadScripts);
30947     },
30948
30949     ignoreResize : function(w, h){
30950         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30951             return true;
30952         }else{
30953             this.lastSize = {width: w, height: h};
30954             return false;
30955         }
30956     },
30957     /**
30958      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30959      * @return {Roo.UpdateManager} The UpdateManager
30960      */
30961     getUpdateManager : function(){
30962         return this.el.getUpdateManager();
30963     },
30964      /**
30965      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30966      * @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:
30967 <pre><code>
30968 panel.load({
30969     url: "your-url.php",
30970     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30971     callback: yourFunction,
30972     scope: yourObject, //(optional scope)
30973     discardUrl: false,
30974     nocache: false,
30975     text: "Loading...",
30976     timeout: 30,
30977     scripts: false
30978 });
30979 </code></pre>
30980      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30981      * 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.
30982      * @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}
30983      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30984      * @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.
30985      * @return {Roo.ContentPanel} this
30986      */
30987     load : function(){
30988         var um = this.el.getUpdateManager();
30989         um.update.apply(um, arguments);
30990         return this;
30991     },
30992
30993
30994     /**
30995      * 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.
30996      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30997      * @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)
30998      * @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)
30999      * @return {Roo.UpdateManager} The UpdateManager
31000      */
31001     setUrl : function(url, params, loadOnce){
31002         if(this.refreshDelegate){
31003             this.removeListener("activate", this.refreshDelegate);
31004         }
31005         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31006         this.on("activate", this.refreshDelegate);
31007         return this.el.getUpdateManager();
31008     },
31009     
31010     _handleRefresh : function(url, params, loadOnce){
31011         if(!loadOnce || !this.loaded){
31012             var updater = this.el.getUpdateManager();
31013             updater.update(url, params, this._setLoaded.createDelegate(this));
31014         }
31015     },
31016     
31017     _setLoaded : function(){
31018         this.loaded = true;
31019     }, 
31020     
31021     /**
31022      * Returns this panel's id
31023      * @return {String} 
31024      */
31025     getId : function(){
31026         return this.el.id;
31027     },
31028     
31029     /** 
31030      * Returns this panel's element - used by regiosn to add.
31031      * @return {Roo.Element} 
31032      */
31033     getEl : function(){
31034         return this.wrapEl || this.el;
31035     },
31036     
31037     adjustForComponents : function(width, height)
31038     {
31039         //Roo.log('adjustForComponents ');
31040         if(this.resizeEl != this.el){
31041             width -= this.el.getFrameWidth('lr');
31042             height -= this.el.getFrameWidth('tb');
31043         }
31044         if(this.toolbar){
31045             var te = this.toolbar.getEl();
31046             height -= te.getHeight();
31047             te.setWidth(width);
31048         }
31049         if(this.footer){
31050             var te = this.footer.getEl();
31051             Roo.log("footer:" + te.getHeight());
31052             
31053             height -= te.getHeight();
31054             te.setWidth(width);
31055         }
31056         
31057         
31058         if(this.adjustments){
31059             width += this.adjustments[0];
31060             height += this.adjustments[1];
31061         }
31062         return {"width": width, "height": height};
31063     },
31064     
31065     setSize : function(width, height){
31066         if(this.fitToFrame && !this.ignoreResize(width, height)){
31067             if(this.fitContainer && this.resizeEl != this.el){
31068                 this.el.setSize(width, height);
31069             }
31070             var size = this.adjustForComponents(width, height);
31071             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31072             this.fireEvent('resize', this, size.width, size.height);
31073         }
31074     },
31075     
31076     /**
31077      * Returns this panel's title
31078      * @return {String} 
31079      */
31080     getTitle : function(){
31081         return this.title;
31082     },
31083     
31084     /**
31085      * Set this panel's title
31086      * @param {String} title
31087      */
31088     setTitle : function(title){
31089         this.title = title;
31090         if(this.region){
31091             this.region.updatePanelTitle(this, title);
31092         }
31093     },
31094     
31095     /**
31096      * Returns true is this panel was configured to be closable
31097      * @return {Boolean} 
31098      */
31099     isClosable : function(){
31100         return this.closable;
31101     },
31102     
31103     beforeSlide : function(){
31104         this.el.clip();
31105         this.resizeEl.clip();
31106     },
31107     
31108     afterSlide : function(){
31109         this.el.unclip();
31110         this.resizeEl.unclip();
31111     },
31112     
31113     /**
31114      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31115      *   Will fail silently if the {@link #setUrl} method has not been called.
31116      *   This does not activate the panel, just updates its content.
31117      */
31118     refresh : function(){
31119         if(this.refreshDelegate){
31120            this.loaded = false;
31121            this.refreshDelegate();
31122         }
31123     },
31124     
31125     /**
31126      * Destroys this panel
31127      */
31128     destroy : function(){
31129         this.el.removeAllListeners();
31130         var tempEl = document.createElement("span");
31131         tempEl.appendChild(this.el.dom);
31132         tempEl.innerHTML = "";
31133         this.el.remove();
31134         this.el = null;
31135     },
31136     
31137     /**
31138      * form - if the content panel contains a form - this is a reference to it.
31139      * @type {Roo.form.Form}
31140      */
31141     form : false,
31142     /**
31143      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31144      *    This contains a reference to it.
31145      * @type {Roo.View}
31146      */
31147     view : false,
31148     
31149       /**
31150      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31151      * <pre><code>
31152
31153 layout.addxtype({
31154        xtype : 'Form',
31155        items: [ .... ]
31156    }
31157 );
31158
31159 </code></pre>
31160      * @param {Object} cfg Xtype definition of item to add.
31161      */
31162     
31163     addxtype : function(cfg) {
31164         // add form..
31165         if (cfg.xtype.match(/^Form$/)) {
31166             
31167             var el;
31168             //if (this.footer) {
31169             //    el = this.footer.container.insertSibling(false, 'before');
31170             //} else {
31171                 el = this.el.createChild();
31172             //}
31173
31174             this.form = new  Roo.form.Form(cfg);
31175             
31176             
31177             if ( this.form.allItems.length) {
31178                 this.form.render(el.dom);
31179             }
31180             return this.form;
31181         }
31182         // should only have one of theses..
31183         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31184             // views.. should not be just added - used named prop 'view''
31185             
31186             cfg.el = this.el.appendChild(document.createElement("div"));
31187             // factory?
31188             
31189             var ret = new Roo.factory(cfg);
31190              
31191              ret.render && ret.render(false, ''); // render blank..
31192             this.view = ret;
31193             return ret;
31194         }
31195         return false;
31196     }
31197 });
31198
31199 /**
31200  * @class Roo.GridPanel
31201  * @extends Roo.ContentPanel
31202  * @constructor
31203  * Create a new GridPanel.
31204  * @param {Roo.grid.Grid} grid The grid for this panel
31205  * @param {String/Object} config A string to set only the panel's title, or a config object
31206  */
31207 Roo.GridPanel = function(grid, config){
31208     
31209   
31210     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31211         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31212         
31213     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31214     
31215     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31216     
31217     if(this.toolbar){
31218         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31219     }
31220     // xtype created footer. - not sure if will work as we normally have to render first..
31221     if (this.footer && !this.footer.el && this.footer.xtype) {
31222         
31223         this.footer.container = this.grid.getView().getFooterPanel(true);
31224         this.footer.dataSource = this.grid.dataSource;
31225         this.footer = Roo.factory(this.footer, Roo);
31226         
31227     }
31228     
31229     grid.monitorWindowResize = false; // turn off autosizing
31230     grid.autoHeight = false;
31231     grid.autoWidth = false;
31232     this.grid = grid;
31233     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31234 };
31235
31236 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31237     getId : function(){
31238         return this.grid.id;
31239     },
31240     
31241     /**
31242      * Returns the grid for this panel
31243      * @return {Roo.grid.Grid} 
31244      */
31245     getGrid : function(){
31246         return this.grid;    
31247     },
31248     
31249     setSize : function(width, height){
31250         if(!this.ignoreResize(width, height)){
31251             var grid = this.grid;
31252             var size = this.adjustForComponents(width, height);
31253             grid.getGridEl().setSize(size.width, size.height);
31254             grid.autoSize();
31255         }
31256     },
31257     
31258     beforeSlide : function(){
31259         this.grid.getView().scroller.clip();
31260     },
31261     
31262     afterSlide : function(){
31263         this.grid.getView().scroller.unclip();
31264     },
31265     
31266     destroy : function(){
31267         this.grid.destroy();
31268         delete this.grid;
31269         Roo.GridPanel.superclass.destroy.call(this); 
31270     }
31271 });
31272
31273
31274 /**
31275  * @class Roo.NestedLayoutPanel
31276  * @extends Roo.ContentPanel
31277  * @constructor
31278  * Create a new NestedLayoutPanel.
31279  * 
31280  * 
31281  * @param {Roo.BorderLayout} layout The layout for this panel
31282  * @param {String/Object} config A string to set only the title or a config object
31283  */
31284 Roo.NestedLayoutPanel = function(layout, config)
31285 {
31286     // construct with only one argument..
31287     /* FIXME - implement nicer consturctors
31288     if (layout.layout) {
31289         config = layout;
31290         layout = config.layout;
31291         delete config.layout;
31292     }
31293     if (layout.xtype && !layout.getEl) {
31294         // then layout needs constructing..
31295         layout = Roo.factory(layout, Roo);
31296     }
31297     */
31298     
31299     
31300     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31301     
31302     layout.monitorWindowResize = false; // turn off autosizing
31303     this.layout = layout;
31304     this.layout.getEl().addClass("x-layout-nested-layout");
31305     
31306     
31307     
31308     
31309 };
31310
31311 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31312
31313     setSize : function(width, height){
31314         if(!this.ignoreResize(width, height)){
31315             var size = this.adjustForComponents(width, height);
31316             var el = this.layout.getEl();
31317             el.setSize(size.width, size.height);
31318             var touch = el.dom.offsetWidth;
31319             this.layout.layout();
31320             // ie requires a double layout on the first pass
31321             if(Roo.isIE && !this.initialized){
31322                 this.initialized = true;
31323                 this.layout.layout();
31324             }
31325         }
31326     },
31327     
31328     // activate all subpanels if not currently active..
31329     
31330     setActiveState : function(active){
31331         this.active = active;
31332         if(!active){
31333             this.fireEvent("deactivate", this);
31334             return;
31335         }
31336         
31337         this.fireEvent("activate", this);
31338         // not sure if this should happen before or after..
31339         if (!this.layout) {
31340             return; // should not happen..
31341         }
31342         var reg = false;
31343         for (var r in this.layout.regions) {
31344             reg = this.layout.getRegion(r);
31345             if (reg.getActivePanel()) {
31346                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31347                 reg.setActivePanel(reg.getActivePanel());
31348                 continue;
31349             }
31350             if (!reg.panels.length) {
31351                 continue;
31352             }
31353             reg.showPanel(reg.getPanel(0));
31354         }
31355         
31356         
31357         
31358         
31359     },
31360     
31361     /**
31362      * Returns the nested BorderLayout for this panel
31363      * @return {Roo.BorderLayout} 
31364      */
31365     getLayout : function(){
31366         return this.layout;
31367     },
31368     
31369      /**
31370      * Adds a xtype elements to the layout of the nested panel
31371      * <pre><code>
31372
31373 panel.addxtype({
31374        xtype : 'ContentPanel',
31375        region: 'west',
31376        items: [ .... ]
31377    }
31378 );
31379
31380 panel.addxtype({
31381         xtype : 'NestedLayoutPanel',
31382         region: 'west',
31383         layout: {
31384            center: { },
31385            west: { }   
31386         },
31387         items : [ ... list of content panels or nested layout panels.. ]
31388    }
31389 );
31390 </code></pre>
31391      * @param {Object} cfg Xtype definition of item to add.
31392      */
31393     addxtype : function(cfg) {
31394         return this.layout.addxtype(cfg);
31395     
31396     }
31397 });
31398
31399 Roo.ScrollPanel = function(el, config, content){
31400     config = config || {};
31401     config.fitToFrame = true;
31402     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31403     
31404     this.el.dom.style.overflow = "hidden";
31405     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31406     this.el.removeClass("x-layout-inactive-content");
31407     this.el.on("mousewheel", this.onWheel, this);
31408
31409     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31410     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31411     up.unselectable(); down.unselectable();
31412     up.on("click", this.scrollUp, this);
31413     down.on("click", this.scrollDown, this);
31414     up.addClassOnOver("x-scroller-btn-over");
31415     down.addClassOnOver("x-scroller-btn-over");
31416     up.addClassOnClick("x-scroller-btn-click");
31417     down.addClassOnClick("x-scroller-btn-click");
31418     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31419
31420     this.resizeEl = this.el;
31421     this.el = wrap; this.up = up; this.down = down;
31422 };
31423
31424 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31425     increment : 100,
31426     wheelIncrement : 5,
31427     scrollUp : function(){
31428         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31429     },
31430
31431     scrollDown : function(){
31432         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31433     },
31434
31435     afterScroll : function(){
31436         var el = this.resizeEl;
31437         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31438         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31439         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31440     },
31441
31442     setSize : function(){
31443         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31444         this.afterScroll();
31445     },
31446
31447     onWheel : function(e){
31448         var d = e.getWheelDelta();
31449         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31450         this.afterScroll();
31451         e.stopEvent();
31452     },
31453
31454     setContent : function(content, loadScripts){
31455         this.resizeEl.update(content, loadScripts);
31456     }
31457
31458 });
31459
31460
31461
31462
31463
31464
31465
31466
31467
31468 /**
31469  * @class Roo.TreePanel
31470  * @extends Roo.ContentPanel
31471  * @constructor
31472  * Create a new TreePanel. - defaults to fit/scoll contents.
31473  * @param {String/Object} config A string to set only the panel's title, or a config object
31474  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31475  */
31476 Roo.TreePanel = function(config){
31477     var el = config.el;
31478     var tree = config.tree;
31479     delete config.tree; 
31480     delete config.el; // hopefull!
31481     
31482     // wrapper for IE7 strict & safari scroll issue
31483     
31484     var treeEl = el.createChild();
31485     config.resizeEl = treeEl;
31486     
31487     
31488     
31489     Roo.TreePanel.superclass.constructor.call(this, el, config);
31490  
31491  
31492     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31493     //console.log(tree);
31494     this.on('activate', function()
31495     {
31496         if (this.tree.rendered) {
31497             return;
31498         }
31499         //console.log('render tree');
31500         this.tree.render();
31501     });
31502     // this should not be needed.. - it's actually the 'el' that resizes?
31503     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31504     
31505     //this.on('resize',  function (cp, w, h) {
31506     //        this.tree.innerCt.setWidth(w);
31507     //        this.tree.innerCt.setHeight(h);
31508     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31509     //});
31510
31511         
31512     
31513 };
31514
31515 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31516     fitToFrame : true,
31517     autoScroll : true
31518 });
31519
31520
31521
31522
31523
31524
31525
31526
31527
31528
31529
31530 /*
31531  * Based on:
31532  * Ext JS Library 1.1.1
31533  * Copyright(c) 2006-2007, Ext JS, LLC.
31534  *
31535  * Originally Released Under LGPL - original licence link has changed is not relivant.
31536  *
31537  * Fork - LGPL
31538  * <script type="text/javascript">
31539  */
31540  
31541
31542 /**
31543  * @class Roo.ReaderLayout
31544  * @extends Roo.BorderLayout
31545  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31546  * center region containing two nested regions (a top one for a list view and one for item preview below),
31547  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31548  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31549  * expedites the setup of the overall layout and regions for this common application style.
31550  * Example:
31551  <pre><code>
31552 var reader = new Roo.ReaderLayout();
31553 var CP = Roo.ContentPanel;  // shortcut for adding
31554
31555 reader.beginUpdate();
31556 reader.add("north", new CP("north", "North"));
31557 reader.add("west", new CP("west", {title: "West"}));
31558 reader.add("east", new CP("east", {title: "East"}));
31559
31560 reader.regions.listView.add(new CP("listView", "List"));
31561 reader.regions.preview.add(new CP("preview", "Preview"));
31562 reader.endUpdate();
31563 </code></pre>
31564 * @constructor
31565 * Create a new ReaderLayout
31566 * @param {Object} config Configuration options
31567 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31568 * document.body if omitted)
31569 */
31570 Roo.ReaderLayout = function(config, renderTo){
31571     var c = config || {size:{}};
31572     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31573         north: c.north !== false ? Roo.apply({
31574             split:false,
31575             initialSize: 32,
31576             titlebar: false
31577         }, c.north) : false,
31578         west: c.west !== false ? Roo.apply({
31579             split:true,
31580             initialSize: 200,
31581             minSize: 175,
31582             maxSize: 400,
31583             titlebar: true,
31584             collapsible: true,
31585             animate: true,
31586             margins:{left:5,right:0,bottom:5,top:5},
31587             cmargins:{left:5,right:5,bottom:5,top:5}
31588         }, c.west) : false,
31589         east: c.east !== false ? Roo.apply({
31590             split:true,
31591             initialSize: 200,
31592             minSize: 175,
31593             maxSize: 400,
31594             titlebar: true,
31595             collapsible: true,
31596             animate: true,
31597             margins:{left:0,right:5,bottom:5,top:5},
31598             cmargins:{left:5,right:5,bottom:5,top:5}
31599         }, c.east) : false,
31600         center: Roo.apply({
31601             tabPosition: 'top',
31602             autoScroll:false,
31603             closeOnTab: true,
31604             titlebar:false,
31605             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31606         }, c.center)
31607     });
31608
31609     this.el.addClass('x-reader');
31610
31611     this.beginUpdate();
31612
31613     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31614         south: c.preview !== false ? Roo.apply({
31615             split:true,
31616             initialSize: 200,
31617             minSize: 100,
31618             autoScroll:true,
31619             collapsible:true,
31620             titlebar: true,
31621             cmargins:{top:5,left:0, right:0, bottom:0}
31622         }, c.preview) : false,
31623         center: Roo.apply({
31624             autoScroll:false,
31625             titlebar:false,
31626             minHeight:200
31627         }, c.listView)
31628     });
31629     this.add('center', new Roo.NestedLayoutPanel(inner,
31630             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31631
31632     this.endUpdate();
31633
31634     this.regions.preview = inner.getRegion('south');
31635     this.regions.listView = inner.getRegion('center');
31636 };
31637
31638 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31639  * Based on:
31640  * Ext JS Library 1.1.1
31641  * Copyright(c) 2006-2007, Ext JS, LLC.
31642  *
31643  * Originally Released Under LGPL - original licence link has changed is not relivant.
31644  *
31645  * Fork - LGPL
31646  * <script type="text/javascript">
31647  */
31648  
31649 /**
31650  * @class Roo.grid.Grid
31651  * @extends Roo.util.Observable
31652  * This class represents the primary interface of a component based grid control.
31653  * <br><br>Usage:<pre><code>
31654  var grid = new Roo.grid.Grid("my-container-id", {
31655      ds: myDataStore,
31656      cm: myColModel,
31657      selModel: mySelectionModel,
31658      autoSizeColumns: true,
31659      monitorWindowResize: false,
31660      trackMouseOver: true
31661  });
31662  // set any options
31663  grid.render();
31664  * </code></pre>
31665  * <b>Common Problems:</b><br/>
31666  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31667  * element will correct this<br/>
31668  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31669  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31670  * are unpredictable.<br/>
31671  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31672  * grid to calculate dimensions/offsets.<br/>
31673   * @constructor
31674  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31675  * The container MUST have some type of size defined for the grid to fill. The container will be
31676  * automatically set to position relative if it isn't already.
31677  * @param {Object} config A config object that sets properties on this grid.
31678  */
31679 Roo.grid.Grid = function(container, config){
31680         // initialize the container
31681         this.container = Roo.get(container);
31682         this.container.update("");
31683         this.container.setStyle("overflow", "hidden");
31684     this.container.addClass('x-grid-container');
31685
31686     this.id = this.container.id;
31687
31688     Roo.apply(this, config);
31689     // check and correct shorthanded configs
31690     if(this.ds){
31691         this.dataSource = this.ds;
31692         delete this.ds;
31693     }
31694     if(this.cm){
31695         this.colModel = this.cm;
31696         delete this.cm;
31697     }
31698     if(this.sm){
31699         this.selModel = this.sm;
31700         delete this.sm;
31701     }
31702
31703     if (this.selModel) {
31704         this.selModel = Roo.factory(this.selModel, Roo.grid);
31705         this.sm = this.selModel;
31706         this.sm.xmodule = this.xmodule || false;
31707     }
31708     if (typeof(this.colModel.config) == 'undefined') {
31709         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31710         this.cm = this.colModel;
31711         this.cm.xmodule = this.xmodule || false;
31712     }
31713     if (this.dataSource) {
31714         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31715         this.ds = this.dataSource;
31716         this.ds.xmodule = this.xmodule || false;
31717          
31718     }
31719     
31720     
31721     
31722     if(this.width){
31723         this.container.setWidth(this.width);
31724     }
31725
31726     if(this.height){
31727         this.container.setHeight(this.height);
31728     }
31729     /** @private */
31730         this.addEvents({
31731         // raw events
31732         /**
31733          * @event click
31734          * The raw click event for the entire grid.
31735          * @param {Roo.EventObject} e
31736          */
31737         "click" : true,
31738         /**
31739          * @event dblclick
31740          * The raw dblclick event for the entire grid.
31741          * @param {Roo.EventObject} e
31742          */
31743         "dblclick" : true,
31744         /**
31745          * @event contextmenu
31746          * The raw contextmenu event for the entire grid.
31747          * @param {Roo.EventObject} e
31748          */
31749         "contextmenu" : true,
31750         /**
31751          * @event mousedown
31752          * The raw mousedown event for the entire grid.
31753          * @param {Roo.EventObject} e
31754          */
31755         "mousedown" : true,
31756         /**
31757          * @event mouseup
31758          * The raw mouseup event for the entire grid.
31759          * @param {Roo.EventObject} e
31760          */
31761         "mouseup" : true,
31762         /**
31763          * @event mouseover
31764          * The raw mouseover event for the entire grid.
31765          * @param {Roo.EventObject} e
31766          */
31767         "mouseover" : true,
31768         /**
31769          * @event mouseout
31770          * The raw mouseout event for the entire grid.
31771          * @param {Roo.EventObject} e
31772          */
31773         "mouseout" : true,
31774         /**
31775          * @event keypress
31776          * The raw keypress event for the entire grid.
31777          * @param {Roo.EventObject} e
31778          */
31779         "keypress" : true,
31780         /**
31781          * @event keydown
31782          * The raw keydown event for the entire grid.
31783          * @param {Roo.EventObject} e
31784          */
31785         "keydown" : true,
31786
31787         // custom events
31788
31789         /**
31790          * @event cellclick
31791          * Fires when a cell is clicked
31792          * @param {Grid} this
31793          * @param {Number} rowIndex
31794          * @param {Number} columnIndex
31795          * @param {Roo.EventObject} e
31796          */
31797         "cellclick" : true,
31798         /**
31799          * @event celldblclick
31800          * Fires when a cell is double clicked
31801          * @param {Grid} this
31802          * @param {Number} rowIndex
31803          * @param {Number} columnIndex
31804          * @param {Roo.EventObject} e
31805          */
31806         "celldblclick" : true,
31807         /**
31808          * @event rowclick
31809          * Fires when a row is clicked
31810          * @param {Grid} this
31811          * @param {Number} rowIndex
31812          * @param {Roo.EventObject} e
31813          */
31814         "rowclick" : true,
31815         /**
31816          * @event rowdblclick
31817          * Fires when a row is double clicked
31818          * @param {Grid} this
31819          * @param {Number} rowIndex
31820          * @param {Roo.EventObject} e
31821          */
31822         "rowdblclick" : true,
31823         /**
31824          * @event headerclick
31825          * Fires when a header is clicked
31826          * @param {Grid} this
31827          * @param {Number} columnIndex
31828          * @param {Roo.EventObject} e
31829          */
31830         "headerclick" : true,
31831         /**
31832          * @event headerdblclick
31833          * Fires when a header cell is double clicked
31834          * @param {Grid} this
31835          * @param {Number} columnIndex
31836          * @param {Roo.EventObject} e
31837          */
31838         "headerdblclick" : true,
31839         /**
31840          * @event rowcontextmenu
31841          * Fires when a row is right clicked
31842          * @param {Grid} this
31843          * @param {Number} rowIndex
31844          * @param {Roo.EventObject} e
31845          */
31846         "rowcontextmenu" : true,
31847         /**
31848          * @event cellcontextmenu
31849          * Fires when a cell is right clicked
31850          * @param {Grid} this
31851          * @param {Number} rowIndex
31852          * @param {Number} cellIndex
31853          * @param {Roo.EventObject} e
31854          */
31855          "cellcontextmenu" : true,
31856         /**
31857          * @event headercontextmenu
31858          * Fires when a header is right clicked
31859          * @param {Grid} this
31860          * @param {Number} columnIndex
31861          * @param {Roo.EventObject} e
31862          */
31863         "headercontextmenu" : true,
31864         /**
31865          * @event bodyscroll
31866          * Fires when the body element is scrolled
31867          * @param {Number} scrollLeft
31868          * @param {Number} scrollTop
31869          */
31870         "bodyscroll" : true,
31871         /**
31872          * @event columnresize
31873          * Fires when the user resizes a column
31874          * @param {Number} columnIndex
31875          * @param {Number} newSize
31876          */
31877         "columnresize" : true,
31878         /**
31879          * @event columnmove
31880          * Fires when the user moves a column
31881          * @param {Number} oldIndex
31882          * @param {Number} newIndex
31883          */
31884         "columnmove" : true,
31885         /**
31886          * @event startdrag
31887          * Fires when row(s) start being dragged
31888          * @param {Grid} this
31889          * @param {Roo.GridDD} dd The drag drop object
31890          * @param {event} e The raw browser event
31891          */
31892         "startdrag" : true,
31893         /**
31894          * @event enddrag
31895          * Fires when a drag operation is complete
31896          * @param {Grid} this
31897          * @param {Roo.GridDD} dd The drag drop object
31898          * @param {event} e The raw browser event
31899          */
31900         "enddrag" : true,
31901         /**
31902          * @event dragdrop
31903          * Fires when dragged row(s) are dropped on a valid DD target
31904          * @param {Grid} this
31905          * @param {Roo.GridDD} dd The drag drop object
31906          * @param {String} targetId The target drag drop object
31907          * @param {event} e The raw browser event
31908          */
31909         "dragdrop" : true,
31910         /**
31911          * @event dragover
31912          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31913          * @param {Grid} this
31914          * @param {Roo.GridDD} dd The drag drop object
31915          * @param {String} targetId The target drag drop object
31916          * @param {event} e The raw browser event
31917          */
31918         "dragover" : true,
31919         /**
31920          * @event dragenter
31921          *  Fires when the dragged row(s) first cross another DD target while being dragged
31922          * @param {Grid} this
31923          * @param {Roo.GridDD} dd The drag drop object
31924          * @param {String} targetId The target drag drop object
31925          * @param {event} e The raw browser event
31926          */
31927         "dragenter" : true,
31928         /**
31929          * @event dragout
31930          * Fires when the dragged row(s) leave another DD target while being dragged
31931          * @param {Grid} this
31932          * @param {Roo.GridDD} dd The drag drop object
31933          * @param {String} targetId The target drag drop object
31934          * @param {event} e The raw browser event
31935          */
31936         "dragout" : true,
31937         /**
31938          * @event rowclass
31939          * Fires when a row is rendered, so you can change add a style to it.
31940          * @param {GridView} gridview   The grid view
31941          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31942          */
31943         'rowclass' : true,
31944
31945         /**
31946          * @event render
31947          * Fires when the grid is rendered
31948          * @param {Grid} grid
31949          */
31950         'render' : true
31951     });
31952
31953     Roo.grid.Grid.superclass.constructor.call(this);
31954 };
31955 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31956     
31957     /**
31958      * @cfg {String} ddGroup - drag drop group.
31959      */
31960
31961     /**
31962      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31963      */
31964     minColumnWidth : 25,
31965
31966     /**
31967      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31968      * <b>on initial render.</b> It is more efficient to explicitly size the columns
31969      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31970      */
31971     autoSizeColumns : false,
31972
31973     /**
31974      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31975      */
31976     autoSizeHeaders : true,
31977
31978     /**
31979      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31980      */
31981     monitorWindowResize : true,
31982
31983     /**
31984      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31985      * rows measured to get a columns size. Default is 0 (all rows).
31986      */
31987     maxRowsToMeasure : 0,
31988
31989     /**
31990      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31991      */
31992     trackMouseOver : true,
31993
31994     /**
31995     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
31996     */
31997     
31998     /**
31999     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32000     */
32001     enableDragDrop : false,
32002     
32003     /**
32004     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32005     */
32006     enableColumnMove : true,
32007     
32008     /**
32009     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32010     */
32011     enableColumnHide : true,
32012     
32013     /**
32014     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32015     */
32016     enableRowHeightSync : false,
32017     
32018     /**
32019     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32020     */
32021     stripeRows : true,
32022     
32023     /**
32024     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32025     */
32026     autoHeight : false,
32027
32028     /**
32029      * @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.
32030      */
32031     autoExpandColumn : false,
32032
32033     /**
32034     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32035     * Default is 50.
32036     */
32037     autoExpandMin : 50,
32038
32039     /**
32040     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32041     */
32042     autoExpandMax : 1000,
32043
32044     /**
32045     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32046     */
32047     view : null,
32048
32049     /**
32050     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32051     */
32052     loadMask : false,
32053     /**
32054     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32055     */
32056     dropTarget: false,
32057     
32058    
32059     
32060     // private
32061     rendered : false,
32062
32063     /**
32064     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32065     * of a fixed width. Default is false.
32066     */
32067     /**
32068     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32069     */
32070     /**
32071      * Called once after all setup has been completed and the grid is ready to be rendered.
32072      * @return {Roo.grid.Grid} this
32073      */
32074     render : function()
32075     {
32076         var c = this.container;
32077         // try to detect autoHeight/width mode
32078         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32079             this.autoHeight = true;
32080         }
32081         var view = this.getView();
32082         view.init(this);
32083
32084         c.on("click", this.onClick, this);
32085         c.on("dblclick", this.onDblClick, this);
32086         c.on("contextmenu", this.onContextMenu, this);
32087         c.on("keydown", this.onKeyDown, this);
32088         if (Roo.isTouch) {
32089             c.on("touchstart", this.onTouchStart, this);
32090         }
32091
32092         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32093
32094         this.getSelectionModel().init(this);
32095
32096         view.render();
32097
32098         if(this.loadMask){
32099             this.loadMask = new Roo.LoadMask(this.container,
32100                     Roo.apply({store:this.dataSource}, this.loadMask));
32101         }
32102         
32103         
32104         if (this.toolbar && this.toolbar.xtype) {
32105             this.toolbar.container = this.getView().getHeaderPanel(true);
32106             this.toolbar = new Roo.Toolbar(this.toolbar);
32107         }
32108         if (this.footer && this.footer.xtype) {
32109             this.footer.dataSource = this.getDataSource();
32110             this.footer.container = this.getView().getFooterPanel(true);
32111             this.footer = Roo.factory(this.footer, Roo);
32112         }
32113         if (this.dropTarget && this.dropTarget.xtype) {
32114             delete this.dropTarget.xtype;
32115             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32116         }
32117         
32118         
32119         this.rendered = true;
32120         this.fireEvent('render', this);
32121         return this;
32122     },
32123
32124         /**
32125          * Reconfigures the grid to use a different Store and Column Model.
32126          * The View will be bound to the new objects and refreshed.
32127          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32128          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32129          */
32130     reconfigure : function(dataSource, colModel){
32131         if(this.loadMask){
32132             this.loadMask.destroy();
32133             this.loadMask = new Roo.LoadMask(this.container,
32134                     Roo.apply({store:dataSource}, this.loadMask));
32135         }
32136         this.view.bind(dataSource, colModel);
32137         this.dataSource = dataSource;
32138         this.colModel = colModel;
32139         this.view.refresh(true);
32140     },
32141
32142     // private
32143     onKeyDown : function(e){
32144         this.fireEvent("keydown", e);
32145     },
32146
32147     /**
32148      * Destroy this grid.
32149      * @param {Boolean} removeEl True to remove the element
32150      */
32151     destroy : function(removeEl, keepListeners){
32152         if(this.loadMask){
32153             this.loadMask.destroy();
32154         }
32155         var c = this.container;
32156         c.removeAllListeners();
32157         this.view.destroy();
32158         this.colModel.purgeListeners();
32159         if(!keepListeners){
32160             this.purgeListeners();
32161         }
32162         c.update("");
32163         if(removeEl === true){
32164             c.remove();
32165         }
32166     },
32167
32168     // private
32169     processEvent : function(name, e){
32170         // does this fire select???
32171         //Roo.log('grid:processEvent '  + name);
32172         
32173         if (name != 'touchstart' ) {
32174             this.fireEvent(name, e);    
32175         }
32176         
32177         var t = e.getTarget();
32178         var v = this.view;
32179         var header = v.findHeaderIndex(t);
32180         if(header !== false){
32181             var ename = name == 'touchstart' ? 'click' : name;
32182              
32183             this.fireEvent("header" + ename, this, header, e);
32184         }else{
32185             var row = v.findRowIndex(t);
32186             var cell = v.findCellIndex(t);
32187             if (name == 'touchstart') {
32188                 // first touch is always a click.
32189                 // hopefull this happens after selection is updated.?
32190                 name = false;
32191                 
32192                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32193                     var cs = this.selModel.getSelectedCell();
32194                     if (row == cs[0] && cell == cs[1]){
32195                         name = 'dblclick';
32196                     }
32197                 }
32198                 if (typeof(this.selModel.getSelections) != 'undefined') {
32199                     var cs = this.selModel.getSelections();
32200                     var ds = this.dataSource;
32201                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32202                         name = 'dblclick';
32203                     }
32204                 }
32205                 if (!name) {
32206                     return;
32207                 }
32208             }
32209             
32210             
32211             if(row !== false){
32212                 this.fireEvent("row" + name, this, row, e);
32213                 if(cell !== false){
32214                     this.fireEvent("cell" + name, this, row, cell, e);
32215                 }
32216             }
32217         }
32218     },
32219
32220     // private
32221     onClick : function(e){
32222         this.processEvent("click", e);
32223     },
32224    // private
32225     onTouchStart : function(e){
32226         this.processEvent("touchstart", e);
32227     },
32228
32229     // private
32230     onContextMenu : function(e, t){
32231         this.processEvent("contextmenu", e);
32232     },
32233
32234     // private
32235     onDblClick : function(e){
32236         this.processEvent("dblclick", e);
32237     },
32238
32239     // private
32240     walkCells : function(row, col, step, fn, scope){
32241         var cm = this.colModel, clen = cm.getColumnCount();
32242         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32243         if(step < 0){
32244             if(col < 0){
32245                 row--;
32246                 first = false;
32247             }
32248             while(row >= 0){
32249                 if(!first){
32250                     col = clen-1;
32251                 }
32252                 first = false;
32253                 while(col >= 0){
32254                     if(fn.call(scope || this, row, col, cm) === true){
32255                         return [row, col];
32256                     }
32257                     col--;
32258                 }
32259                 row--;
32260             }
32261         } else {
32262             if(col >= clen){
32263                 row++;
32264                 first = false;
32265             }
32266             while(row < rlen){
32267                 if(!first){
32268                     col = 0;
32269                 }
32270                 first = false;
32271                 while(col < clen){
32272                     if(fn.call(scope || this, row, col, cm) === true){
32273                         return [row, col];
32274                     }
32275                     col++;
32276                 }
32277                 row++;
32278             }
32279         }
32280         return null;
32281     },
32282
32283     // private
32284     getSelections : function(){
32285         return this.selModel.getSelections();
32286     },
32287
32288     /**
32289      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32290      * but if manual update is required this method will initiate it.
32291      */
32292     autoSize : function(){
32293         if(this.rendered){
32294             this.view.layout();
32295             if(this.view.adjustForScroll){
32296                 this.view.adjustForScroll();
32297             }
32298         }
32299     },
32300
32301     /**
32302      * Returns the grid's underlying element.
32303      * @return {Element} The element
32304      */
32305     getGridEl : function(){
32306         return this.container;
32307     },
32308
32309     // private for compatibility, overridden by editor grid
32310     stopEditing : function(){},
32311
32312     /**
32313      * Returns the grid's SelectionModel.
32314      * @return {SelectionModel}
32315      */
32316     getSelectionModel : function(){
32317         if(!this.selModel){
32318             this.selModel = new Roo.grid.RowSelectionModel();
32319         }
32320         return this.selModel;
32321     },
32322
32323     /**
32324      * Returns the grid's DataSource.
32325      * @return {DataSource}
32326      */
32327     getDataSource : function(){
32328         return this.dataSource;
32329     },
32330
32331     /**
32332      * Returns the grid's ColumnModel.
32333      * @return {ColumnModel}
32334      */
32335     getColumnModel : function(){
32336         return this.colModel;
32337     },
32338
32339     /**
32340      * Returns the grid's GridView object.
32341      * @return {GridView}
32342      */
32343     getView : function(){
32344         if(!this.view){
32345             this.view = new Roo.grid.GridView(this.viewConfig);
32346         }
32347         return this.view;
32348     },
32349     /**
32350      * Called to get grid's drag proxy text, by default returns this.ddText.
32351      * @return {String}
32352      */
32353     getDragDropText : function(){
32354         var count = this.selModel.getCount();
32355         return String.format(this.ddText, count, count == 1 ? '' : 's');
32356     }
32357 });
32358 /**
32359  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32360  * %0 is replaced with the number of selected rows.
32361  * @type String
32362  */
32363 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32364  * Based on:
32365  * Ext JS Library 1.1.1
32366  * Copyright(c) 2006-2007, Ext JS, LLC.
32367  *
32368  * Originally Released Under LGPL - original licence link has changed is not relivant.
32369  *
32370  * Fork - LGPL
32371  * <script type="text/javascript">
32372  */
32373  
32374 Roo.grid.AbstractGridView = function(){
32375         this.grid = null;
32376         
32377         this.events = {
32378             "beforerowremoved" : true,
32379             "beforerowsinserted" : true,
32380             "beforerefresh" : true,
32381             "rowremoved" : true,
32382             "rowsinserted" : true,
32383             "rowupdated" : true,
32384             "refresh" : true
32385         };
32386     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32387 };
32388
32389 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32390     rowClass : "x-grid-row",
32391     cellClass : "x-grid-cell",
32392     tdClass : "x-grid-td",
32393     hdClass : "x-grid-hd",
32394     splitClass : "x-grid-hd-split",
32395     
32396     init: function(grid){
32397         this.grid = grid;
32398                 var cid = this.grid.getGridEl().id;
32399         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32400         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32401         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32402         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32403         },
32404         
32405     getColumnRenderers : function(){
32406         var renderers = [];
32407         var cm = this.grid.colModel;
32408         var colCount = cm.getColumnCount();
32409         for(var i = 0; i < colCount; i++){
32410             renderers[i] = cm.getRenderer(i);
32411         }
32412         return renderers;
32413     },
32414     
32415     getColumnIds : function(){
32416         var ids = [];
32417         var cm = this.grid.colModel;
32418         var colCount = cm.getColumnCount();
32419         for(var i = 0; i < colCount; i++){
32420             ids[i] = cm.getColumnId(i);
32421         }
32422         return ids;
32423     },
32424     
32425     getDataIndexes : function(){
32426         if(!this.indexMap){
32427             this.indexMap = this.buildIndexMap();
32428         }
32429         return this.indexMap.colToData;
32430     },
32431     
32432     getColumnIndexByDataIndex : function(dataIndex){
32433         if(!this.indexMap){
32434             this.indexMap = this.buildIndexMap();
32435         }
32436         return this.indexMap.dataToCol[dataIndex];
32437     },
32438     
32439     /**
32440      * Set a css style for a column dynamically. 
32441      * @param {Number} colIndex The index of the column
32442      * @param {String} name The css property name
32443      * @param {String} value The css value
32444      */
32445     setCSSStyle : function(colIndex, name, value){
32446         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32447         Roo.util.CSS.updateRule(selector, name, value);
32448     },
32449     
32450     generateRules : function(cm){
32451         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32452         Roo.util.CSS.removeStyleSheet(rulesId);
32453         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32454             var cid = cm.getColumnId(i);
32455             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32456                          this.tdSelector, cid, " {\n}\n",
32457                          this.hdSelector, cid, " {\n}\n",
32458                          this.splitSelector, cid, " {\n}\n");
32459         }
32460         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32461     }
32462 });/*
32463  * Based on:
32464  * Ext JS Library 1.1.1
32465  * Copyright(c) 2006-2007, Ext JS, LLC.
32466  *
32467  * Originally Released Under LGPL - original licence link has changed is not relivant.
32468  *
32469  * Fork - LGPL
32470  * <script type="text/javascript">
32471  */
32472
32473 // private
32474 // This is a support class used internally by the Grid components
32475 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32476     this.grid = grid;
32477     this.view = grid.getView();
32478     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32479     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32480     if(hd2){
32481         this.setHandleElId(Roo.id(hd));
32482         this.setOuterHandleElId(Roo.id(hd2));
32483     }
32484     this.scroll = false;
32485 };
32486 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32487     maxDragWidth: 120,
32488     getDragData : function(e){
32489         var t = Roo.lib.Event.getTarget(e);
32490         var h = this.view.findHeaderCell(t);
32491         if(h){
32492             return {ddel: h.firstChild, header:h};
32493         }
32494         return false;
32495     },
32496
32497     onInitDrag : function(e){
32498         this.view.headersDisabled = true;
32499         var clone = this.dragData.ddel.cloneNode(true);
32500         clone.id = Roo.id();
32501         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32502         this.proxy.update(clone);
32503         return true;
32504     },
32505
32506     afterValidDrop : function(){
32507         var v = this.view;
32508         setTimeout(function(){
32509             v.headersDisabled = false;
32510         }, 50);
32511     },
32512
32513     afterInvalidDrop : function(){
32514         var v = this.view;
32515         setTimeout(function(){
32516             v.headersDisabled = false;
32517         }, 50);
32518     }
32519 });
32520 /*
32521  * Based on:
32522  * Ext JS Library 1.1.1
32523  * Copyright(c) 2006-2007, Ext JS, LLC.
32524  *
32525  * Originally Released Under LGPL - original licence link has changed is not relivant.
32526  *
32527  * Fork - LGPL
32528  * <script type="text/javascript">
32529  */
32530 // private
32531 // This is a support class used internally by the Grid components
32532 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32533     this.grid = grid;
32534     this.view = grid.getView();
32535     // split the proxies so they don't interfere with mouse events
32536     this.proxyTop = Roo.DomHelper.append(document.body, {
32537         cls:"col-move-top", html:"&#160;"
32538     }, true);
32539     this.proxyBottom = Roo.DomHelper.append(document.body, {
32540         cls:"col-move-bottom", html:"&#160;"
32541     }, true);
32542     this.proxyTop.hide = this.proxyBottom.hide = function(){
32543         this.setLeftTop(-100,-100);
32544         this.setStyle("visibility", "hidden");
32545     };
32546     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32547     // temporarily disabled
32548     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32549     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32550 };
32551 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32552     proxyOffsets : [-4, -9],
32553     fly: Roo.Element.fly,
32554
32555     getTargetFromEvent : function(e){
32556         var t = Roo.lib.Event.getTarget(e);
32557         var cindex = this.view.findCellIndex(t);
32558         if(cindex !== false){
32559             return this.view.getHeaderCell(cindex);
32560         }
32561         return null;
32562     },
32563
32564     nextVisible : function(h){
32565         var v = this.view, cm = this.grid.colModel;
32566         h = h.nextSibling;
32567         while(h){
32568             if(!cm.isHidden(v.getCellIndex(h))){
32569                 return h;
32570             }
32571             h = h.nextSibling;
32572         }
32573         return null;
32574     },
32575
32576     prevVisible : function(h){
32577         var v = this.view, cm = this.grid.colModel;
32578         h = h.prevSibling;
32579         while(h){
32580             if(!cm.isHidden(v.getCellIndex(h))){
32581                 return h;
32582             }
32583             h = h.prevSibling;
32584         }
32585         return null;
32586     },
32587
32588     positionIndicator : function(h, n, e){
32589         var x = Roo.lib.Event.getPageX(e);
32590         var r = Roo.lib.Dom.getRegion(n.firstChild);
32591         var px, pt, py = r.top + this.proxyOffsets[1];
32592         if((r.right - x) <= (r.right-r.left)/2){
32593             px = r.right+this.view.borderWidth;
32594             pt = "after";
32595         }else{
32596             px = r.left;
32597             pt = "before";
32598         }
32599         var oldIndex = this.view.getCellIndex(h);
32600         var newIndex = this.view.getCellIndex(n);
32601
32602         if(this.grid.colModel.isFixed(newIndex)){
32603             return false;
32604         }
32605
32606         var locked = this.grid.colModel.isLocked(newIndex);
32607
32608         if(pt == "after"){
32609             newIndex++;
32610         }
32611         if(oldIndex < newIndex){
32612             newIndex--;
32613         }
32614         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32615             return false;
32616         }
32617         px +=  this.proxyOffsets[0];
32618         this.proxyTop.setLeftTop(px, py);
32619         this.proxyTop.show();
32620         if(!this.bottomOffset){
32621             this.bottomOffset = this.view.mainHd.getHeight();
32622         }
32623         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32624         this.proxyBottom.show();
32625         return pt;
32626     },
32627
32628     onNodeEnter : function(n, dd, e, data){
32629         if(data.header != n){
32630             this.positionIndicator(data.header, n, e);
32631         }
32632     },
32633
32634     onNodeOver : function(n, dd, e, data){
32635         var result = false;
32636         if(data.header != n){
32637             result = this.positionIndicator(data.header, n, e);
32638         }
32639         if(!result){
32640             this.proxyTop.hide();
32641             this.proxyBottom.hide();
32642         }
32643         return result ? this.dropAllowed : this.dropNotAllowed;
32644     },
32645
32646     onNodeOut : function(n, dd, e, data){
32647         this.proxyTop.hide();
32648         this.proxyBottom.hide();
32649     },
32650
32651     onNodeDrop : function(n, dd, e, data){
32652         var h = data.header;
32653         if(h != n){
32654             var cm = this.grid.colModel;
32655             var x = Roo.lib.Event.getPageX(e);
32656             var r = Roo.lib.Dom.getRegion(n.firstChild);
32657             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32658             var oldIndex = this.view.getCellIndex(h);
32659             var newIndex = this.view.getCellIndex(n);
32660             var locked = cm.isLocked(newIndex);
32661             if(pt == "after"){
32662                 newIndex++;
32663             }
32664             if(oldIndex < newIndex){
32665                 newIndex--;
32666             }
32667             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32668                 return false;
32669             }
32670             cm.setLocked(oldIndex, locked, true);
32671             cm.moveColumn(oldIndex, newIndex);
32672             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32673             return true;
32674         }
32675         return false;
32676     }
32677 });
32678 /*
32679  * Based on:
32680  * Ext JS Library 1.1.1
32681  * Copyright(c) 2006-2007, Ext JS, LLC.
32682  *
32683  * Originally Released Under LGPL - original licence link has changed is not relivant.
32684  *
32685  * Fork - LGPL
32686  * <script type="text/javascript">
32687  */
32688   
32689 /**
32690  * @class Roo.grid.GridView
32691  * @extends Roo.util.Observable
32692  *
32693  * @constructor
32694  * @param {Object} config
32695  */
32696 Roo.grid.GridView = function(config){
32697     Roo.grid.GridView.superclass.constructor.call(this);
32698     this.el = null;
32699
32700     Roo.apply(this, config);
32701 };
32702
32703 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32704
32705     unselectable :  'unselectable="on"',
32706     unselectableCls :  'x-unselectable',
32707     
32708     
32709     rowClass : "x-grid-row",
32710
32711     cellClass : "x-grid-col",
32712
32713     tdClass : "x-grid-td",
32714
32715     hdClass : "x-grid-hd",
32716
32717     splitClass : "x-grid-split",
32718
32719     sortClasses : ["sort-asc", "sort-desc"],
32720
32721     enableMoveAnim : false,
32722
32723     hlColor: "C3DAF9",
32724
32725     dh : Roo.DomHelper,
32726
32727     fly : Roo.Element.fly,
32728
32729     css : Roo.util.CSS,
32730
32731     borderWidth: 1,
32732
32733     splitOffset: 3,
32734
32735     scrollIncrement : 22,
32736
32737     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32738
32739     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32740
32741     bind : function(ds, cm){
32742         if(this.ds){
32743             this.ds.un("load", this.onLoad, this);
32744             this.ds.un("datachanged", this.onDataChange, this);
32745             this.ds.un("add", this.onAdd, this);
32746             this.ds.un("remove", this.onRemove, this);
32747             this.ds.un("update", this.onUpdate, this);
32748             this.ds.un("clear", this.onClear, this);
32749         }
32750         if(ds){
32751             ds.on("load", this.onLoad, this);
32752             ds.on("datachanged", this.onDataChange, this);
32753             ds.on("add", this.onAdd, this);
32754             ds.on("remove", this.onRemove, this);
32755             ds.on("update", this.onUpdate, this);
32756             ds.on("clear", this.onClear, this);
32757         }
32758         this.ds = ds;
32759
32760         if(this.cm){
32761             this.cm.un("widthchange", this.onColWidthChange, this);
32762             this.cm.un("headerchange", this.onHeaderChange, this);
32763             this.cm.un("hiddenchange", this.onHiddenChange, this);
32764             this.cm.un("columnmoved", this.onColumnMove, this);
32765             this.cm.un("columnlockchange", this.onColumnLock, this);
32766         }
32767         if(cm){
32768             this.generateRules(cm);
32769             cm.on("widthchange", this.onColWidthChange, this);
32770             cm.on("headerchange", this.onHeaderChange, this);
32771             cm.on("hiddenchange", this.onHiddenChange, this);
32772             cm.on("columnmoved", this.onColumnMove, this);
32773             cm.on("columnlockchange", this.onColumnLock, this);
32774         }
32775         this.cm = cm;
32776     },
32777
32778     init: function(grid){
32779         Roo.grid.GridView.superclass.init.call(this, grid);
32780
32781         this.bind(grid.dataSource, grid.colModel);
32782
32783         grid.on("headerclick", this.handleHeaderClick, this);
32784
32785         if(grid.trackMouseOver){
32786             grid.on("mouseover", this.onRowOver, this);
32787             grid.on("mouseout", this.onRowOut, this);
32788         }
32789         grid.cancelTextSelection = function(){};
32790         this.gridId = grid.id;
32791
32792         var tpls = this.templates || {};
32793
32794         if(!tpls.master){
32795             tpls.master = new Roo.Template(
32796                '<div class="x-grid" hidefocus="true">',
32797                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32798                   '<div class="x-grid-topbar"></div>',
32799                   '<div class="x-grid-scroller"><div></div></div>',
32800                   '<div class="x-grid-locked">',
32801                       '<div class="x-grid-header">{lockedHeader}</div>',
32802                       '<div class="x-grid-body">{lockedBody}</div>',
32803                   "</div>",
32804                   '<div class="x-grid-viewport">',
32805                       '<div class="x-grid-header">{header}</div>',
32806                       '<div class="x-grid-body">{body}</div>',
32807                   "</div>",
32808                   '<div class="x-grid-bottombar"></div>',
32809                  
32810                   '<div class="x-grid-resize-proxy">&#160;</div>',
32811                "</div>"
32812             );
32813             tpls.master.disableformats = true;
32814         }
32815
32816         if(!tpls.header){
32817             tpls.header = new Roo.Template(
32818                '<table border="0" cellspacing="0" cellpadding="0">',
32819                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32820                "</table>{splits}"
32821             );
32822             tpls.header.disableformats = true;
32823         }
32824         tpls.header.compile();
32825
32826         if(!tpls.hcell){
32827             tpls.hcell = new Roo.Template(
32828                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32829                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32830                 "</div></td>"
32831              );
32832              tpls.hcell.disableFormats = true;
32833         }
32834         tpls.hcell.compile();
32835
32836         if(!tpls.hsplit){
32837             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32838                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32839             tpls.hsplit.disableFormats = true;
32840         }
32841         tpls.hsplit.compile();
32842
32843         if(!tpls.body){
32844             tpls.body = new Roo.Template(
32845                '<table border="0" cellspacing="0" cellpadding="0">',
32846                "<tbody>{rows}</tbody>",
32847                "</table>"
32848             );
32849             tpls.body.disableFormats = true;
32850         }
32851         tpls.body.compile();
32852
32853         if(!tpls.row){
32854             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32855             tpls.row.disableFormats = true;
32856         }
32857         tpls.row.compile();
32858
32859         if(!tpls.cell){
32860             tpls.cell = new Roo.Template(
32861                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32862                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32863                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32864                 "</td>"
32865             );
32866             tpls.cell.disableFormats = true;
32867         }
32868         tpls.cell.compile();
32869
32870         this.templates = tpls;
32871     },
32872
32873     // remap these for backwards compat
32874     onColWidthChange : function(){
32875         this.updateColumns.apply(this, arguments);
32876     },
32877     onHeaderChange : function(){
32878         this.updateHeaders.apply(this, arguments);
32879     }, 
32880     onHiddenChange : function(){
32881         this.handleHiddenChange.apply(this, arguments);
32882     },
32883     onColumnMove : function(){
32884         this.handleColumnMove.apply(this, arguments);
32885     },
32886     onColumnLock : function(){
32887         this.handleLockChange.apply(this, arguments);
32888     },
32889
32890     onDataChange : function(){
32891         this.refresh();
32892         this.updateHeaderSortState();
32893     },
32894
32895     onClear : function(){
32896         this.refresh();
32897     },
32898
32899     onUpdate : function(ds, record){
32900         this.refreshRow(record);
32901     },
32902
32903     refreshRow : function(record){
32904         var ds = this.ds, index;
32905         if(typeof record == 'number'){
32906             index = record;
32907             record = ds.getAt(index);
32908         }else{
32909             index = ds.indexOf(record);
32910         }
32911         this.insertRows(ds, index, index, true);
32912         this.onRemove(ds, record, index+1, true);
32913         this.syncRowHeights(index, index);
32914         this.layout();
32915         this.fireEvent("rowupdated", this, index, record);
32916     },
32917
32918     onAdd : function(ds, records, index){
32919         this.insertRows(ds, index, index + (records.length-1));
32920     },
32921
32922     onRemove : function(ds, record, index, isUpdate){
32923         if(isUpdate !== true){
32924             this.fireEvent("beforerowremoved", this, index, record);
32925         }
32926         var bt = this.getBodyTable(), lt = this.getLockedTable();
32927         if(bt.rows[index]){
32928             bt.firstChild.removeChild(bt.rows[index]);
32929         }
32930         if(lt.rows[index]){
32931             lt.firstChild.removeChild(lt.rows[index]);
32932         }
32933         if(isUpdate !== true){
32934             this.stripeRows(index);
32935             this.syncRowHeights(index, index);
32936             this.layout();
32937             this.fireEvent("rowremoved", this, index, record);
32938         }
32939     },
32940
32941     onLoad : function(){
32942         this.scrollToTop();
32943     },
32944
32945     /**
32946      * Scrolls the grid to the top
32947      */
32948     scrollToTop : function(){
32949         if(this.scroller){
32950             this.scroller.dom.scrollTop = 0;
32951             this.syncScroll();
32952         }
32953     },
32954
32955     /**
32956      * Gets a panel in the header of the grid that can be used for toolbars etc.
32957      * After modifying the contents of this panel a call to grid.autoSize() may be
32958      * required to register any changes in size.
32959      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32960      * @return Roo.Element
32961      */
32962     getHeaderPanel : function(doShow){
32963         if(doShow){
32964             this.headerPanel.show();
32965         }
32966         return this.headerPanel;
32967     },
32968
32969     /**
32970      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32971      * After modifying the contents of this panel a call to grid.autoSize() may be
32972      * required to register any changes in size.
32973      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32974      * @return Roo.Element
32975      */
32976     getFooterPanel : function(doShow){
32977         if(doShow){
32978             this.footerPanel.show();
32979         }
32980         return this.footerPanel;
32981     },
32982
32983     initElements : function(){
32984         var E = Roo.Element;
32985         var el = this.grid.getGridEl().dom.firstChild;
32986         var cs = el.childNodes;
32987
32988         this.el = new E(el);
32989         
32990          this.focusEl = new E(el.firstChild);
32991         this.focusEl.swallowEvent("click", true);
32992         
32993         this.headerPanel = new E(cs[1]);
32994         this.headerPanel.enableDisplayMode("block");
32995
32996         this.scroller = new E(cs[2]);
32997         this.scrollSizer = new E(this.scroller.dom.firstChild);
32998
32999         this.lockedWrap = new E(cs[3]);
33000         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33001         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33002
33003         this.mainWrap = new E(cs[4]);
33004         this.mainHd = new E(this.mainWrap.dom.firstChild);
33005         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33006
33007         this.footerPanel = new E(cs[5]);
33008         this.footerPanel.enableDisplayMode("block");
33009
33010         this.resizeProxy = new E(cs[6]);
33011
33012         this.headerSelector = String.format(
33013            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33014            this.lockedHd.id, this.mainHd.id
33015         );
33016
33017         this.splitterSelector = String.format(
33018            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33019            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33020         );
33021     },
33022     idToCssName : function(s)
33023     {
33024         return s.replace(/[^a-z0-9]+/ig, '-');
33025     },
33026
33027     getHeaderCell : function(index){
33028         return Roo.DomQuery.select(this.headerSelector)[index];
33029     },
33030
33031     getHeaderCellMeasure : function(index){
33032         return this.getHeaderCell(index).firstChild;
33033     },
33034
33035     getHeaderCellText : function(index){
33036         return this.getHeaderCell(index).firstChild.firstChild;
33037     },
33038
33039     getLockedTable : function(){
33040         return this.lockedBody.dom.firstChild;
33041     },
33042
33043     getBodyTable : function(){
33044         return this.mainBody.dom.firstChild;
33045     },
33046
33047     getLockedRow : function(index){
33048         return this.getLockedTable().rows[index];
33049     },
33050
33051     getRow : function(index){
33052         return this.getBodyTable().rows[index];
33053     },
33054
33055     getRowComposite : function(index){
33056         if(!this.rowEl){
33057             this.rowEl = new Roo.CompositeElementLite();
33058         }
33059         var els = [], lrow, mrow;
33060         if(lrow = this.getLockedRow(index)){
33061             els.push(lrow);
33062         }
33063         if(mrow = this.getRow(index)){
33064             els.push(mrow);
33065         }
33066         this.rowEl.elements = els;
33067         return this.rowEl;
33068     },
33069     /**
33070      * Gets the 'td' of the cell
33071      * 
33072      * @param {Integer} rowIndex row to select
33073      * @param {Integer} colIndex column to select
33074      * 
33075      * @return {Object} 
33076      */
33077     getCell : function(rowIndex, colIndex){
33078         var locked = this.cm.getLockedCount();
33079         var source;
33080         if(colIndex < locked){
33081             source = this.lockedBody.dom.firstChild;
33082         }else{
33083             source = this.mainBody.dom.firstChild;
33084             colIndex -= locked;
33085         }
33086         return source.rows[rowIndex].childNodes[colIndex];
33087     },
33088
33089     getCellText : function(rowIndex, colIndex){
33090         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33091     },
33092
33093     getCellBox : function(cell){
33094         var b = this.fly(cell).getBox();
33095         if(Roo.isOpera){ // opera fails to report the Y
33096             b.y = cell.offsetTop + this.mainBody.getY();
33097         }
33098         return b;
33099     },
33100
33101     getCellIndex : function(cell){
33102         var id = String(cell.className).match(this.cellRE);
33103         if(id){
33104             return parseInt(id[1], 10);
33105         }
33106         return 0;
33107     },
33108
33109     findHeaderIndex : function(n){
33110         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33111         return r ? this.getCellIndex(r) : false;
33112     },
33113
33114     findHeaderCell : function(n){
33115         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33116         return r ? r : false;
33117     },
33118
33119     findRowIndex : function(n){
33120         if(!n){
33121             return false;
33122         }
33123         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33124         return r ? r.rowIndex : false;
33125     },
33126
33127     findCellIndex : function(node){
33128         var stop = this.el.dom;
33129         while(node && node != stop){
33130             if(this.findRE.test(node.className)){
33131                 return this.getCellIndex(node);
33132             }
33133             node = node.parentNode;
33134         }
33135         return false;
33136     },
33137
33138     getColumnId : function(index){
33139         return this.cm.getColumnId(index);
33140     },
33141
33142     getSplitters : function()
33143     {
33144         if(this.splitterSelector){
33145            return Roo.DomQuery.select(this.splitterSelector);
33146         }else{
33147             return null;
33148       }
33149     },
33150
33151     getSplitter : function(index){
33152         return this.getSplitters()[index];
33153     },
33154
33155     onRowOver : function(e, t){
33156         var row;
33157         if((row = this.findRowIndex(t)) !== false){
33158             this.getRowComposite(row).addClass("x-grid-row-over");
33159         }
33160     },
33161
33162     onRowOut : function(e, t){
33163         var row;
33164         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33165             this.getRowComposite(row).removeClass("x-grid-row-over");
33166         }
33167     },
33168
33169     renderHeaders : function(){
33170         var cm = this.cm;
33171         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33172         var cb = [], lb = [], sb = [], lsb = [], p = {};
33173         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33174             p.cellId = "x-grid-hd-0-" + i;
33175             p.splitId = "x-grid-csplit-0-" + i;
33176             p.id = cm.getColumnId(i);
33177             p.value = cm.getColumnHeader(i) || "";
33178             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33179             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33180             if(!cm.isLocked(i)){
33181                 cb[cb.length] = ct.apply(p);
33182                 sb[sb.length] = st.apply(p);
33183             }else{
33184                 lb[lb.length] = ct.apply(p);
33185                 lsb[lsb.length] = st.apply(p);
33186             }
33187         }
33188         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33189                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33190     },
33191
33192     updateHeaders : function(){
33193         var html = this.renderHeaders();
33194         this.lockedHd.update(html[0]);
33195         this.mainHd.update(html[1]);
33196     },
33197
33198     /**
33199      * Focuses the specified row.
33200      * @param {Number} row The row index
33201      */
33202     focusRow : function(row)
33203     {
33204         //Roo.log('GridView.focusRow');
33205         var x = this.scroller.dom.scrollLeft;
33206         this.focusCell(row, 0, false);
33207         this.scroller.dom.scrollLeft = x;
33208     },
33209
33210     /**
33211      * Focuses the specified cell.
33212      * @param {Number} row The row index
33213      * @param {Number} col The column index
33214      * @param {Boolean} hscroll false to disable horizontal scrolling
33215      */
33216     focusCell : function(row, col, hscroll)
33217     {
33218         //Roo.log('GridView.focusCell');
33219         var el = this.ensureVisible(row, col, hscroll);
33220         this.focusEl.alignTo(el, "tl-tl");
33221         if(Roo.isGecko){
33222             this.focusEl.focus();
33223         }else{
33224             this.focusEl.focus.defer(1, this.focusEl);
33225         }
33226     },
33227
33228     /**
33229      * Scrolls the specified cell into view
33230      * @param {Number} row The row index
33231      * @param {Number} col The column index
33232      * @param {Boolean} hscroll false to disable horizontal scrolling
33233      */
33234     ensureVisible : function(row, col, hscroll)
33235     {
33236         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33237         //return null; //disable for testing.
33238         if(typeof row != "number"){
33239             row = row.rowIndex;
33240         }
33241         if(row < 0 && row >= this.ds.getCount()){
33242             return  null;
33243         }
33244         col = (col !== undefined ? col : 0);
33245         var cm = this.grid.colModel;
33246         while(cm.isHidden(col)){
33247             col++;
33248         }
33249
33250         var el = this.getCell(row, col);
33251         if(!el){
33252             return null;
33253         }
33254         var c = this.scroller.dom;
33255
33256         var ctop = parseInt(el.offsetTop, 10);
33257         var cleft = parseInt(el.offsetLeft, 10);
33258         var cbot = ctop + el.offsetHeight;
33259         var cright = cleft + el.offsetWidth;
33260         
33261         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33262         var stop = parseInt(c.scrollTop, 10);
33263         var sleft = parseInt(c.scrollLeft, 10);
33264         var sbot = stop + ch;
33265         var sright = sleft + c.clientWidth;
33266         /*
33267         Roo.log('GridView.ensureVisible:' +
33268                 ' ctop:' + ctop +
33269                 ' c.clientHeight:' + c.clientHeight +
33270                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33271                 ' stop:' + stop +
33272                 ' cbot:' + cbot +
33273                 ' sbot:' + sbot +
33274                 ' ch:' + ch  
33275                 );
33276         */
33277         if(ctop < stop){
33278              c.scrollTop = ctop;
33279             //Roo.log("set scrolltop to ctop DISABLE?");
33280         }else if(cbot > sbot){
33281             //Roo.log("set scrolltop to cbot-ch");
33282             c.scrollTop = cbot-ch;
33283         }
33284         
33285         if(hscroll !== false){
33286             if(cleft < sleft){
33287                 c.scrollLeft = cleft;
33288             }else if(cright > sright){
33289                 c.scrollLeft = cright-c.clientWidth;
33290             }
33291         }
33292          
33293         return el;
33294     },
33295
33296     updateColumns : function(){
33297         this.grid.stopEditing();
33298         var cm = this.grid.colModel, colIds = this.getColumnIds();
33299         //var totalWidth = cm.getTotalWidth();
33300         var pos = 0;
33301         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33302             //if(cm.isHidden(i)) continue;
33303             var w = cm.getColumnWidth(i);
33304             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33305             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33306         }
33307         this.updateSplitters();
33308     },
33309
33310     generateRules : function(cm){
33311         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33312         Roo.util.CSS.removeStyleSheet(rulesId);
33313         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33314             var cid = cm.getColumnId(i);
33315             var align = '';
33316             if(cm.config[i].align){
33317                 align = 'text-align:'+cm.config[i].align+';';
33318             }
33319             var hidden = '';
33320             if(cm.isHidden(i)){
33321                 hidden = 'display:none;';
33322             }
33323             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33324             ruleBuf.push(
33325                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33326                     this.hdSelector, cid, " {\n", align, width, "}\n",
33327                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33328                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33329         }
33330         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33331     },
33332
33333     updateSplitters : function(){
33334         var cm = this.cm, s = this.getSplitters();
33335         if(s){ // splitters not created yet
33336             var pos = 0, locked = true;
33337             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33338                 if(cm.isHidden(i)) {
33339                     continue;
33340                 }
33341                 var w = cm.getColumnWidth(i); // make sure it's a number
33342                 if(!cm.isLocked(i) && locked){
33343                     pos = 0;
33344                     locked = false;
33345                 }
33346                 pos += w;
33347                 s[i].style.left = (pos-this.splitOffset) + "px";
33348             }
33349         }
33350     },
33351
33352     handleHiddenChange : function(colModel, colIndex, hidden){
33353         if(hidden){
33354             this.hideColumn(colIndex);
33355         }else{
33356             this.unhideColumn(colIndex);
33357         }
33358     },
33359
33360     hideColumn : function(colIndex){
33361         var cid = this.getColumnId(colIndex);
33362         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33363         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33364         if(Roo.isSafari){
33365             this.updateHeaders();
33366         }
33367         this.updateSplitters();
33368         this.layout();
33369     },
33370
33371     unhideColumn : function(colIndex){
33372         var cid = this.getColumnId(colIndex);
33373         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33374         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33375
33376         if(Roo.isSafari){
33377             this.updateHeaders();
33378         }
33379         this.updateSplitters();
33380         this.layout();
33381     },
33382
33383     insertRows : function(dm, firstRow, lastRow, isUpdate){
33384         if(firstRow == 0 && lastRow == dm.getCount()-1){
33385             this.refresh();
33386         }else{
33387             if(!isUpdate){
33388                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33389             }
33390             var s = this.getScrollState();
33391             var markup = this.renderRows(firstRow, lastRow);
33392             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33393             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33394             this.restoreScroll(s);
33395             if(!isUpdate){
33396                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33397                 this.syncRowHeights(firstRow, lastRow);
33398                 this.stripeRows(firstRow);
33399                 this.layout();
33400             }
33401         }
33402     },
33403
33404     bufferRows : function(markup, target, index){
33405         var before = null, trows = target.rows, tbody = target.tBodies[0];
33406         if(index < trows.length){
33407             before = trows[index];
33408         }
33409         var b = document.createElement("div");
33410         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33411         var rows = b.firstChild.rows;
33412         for(var i = 0, len = rows.length; i < len; i++){
33413             if(before){
33414                 tbody.insertBefore(rows[0], before);
33415             }else{
33416                 tbody.appendChild(rows[0]);
33417             }
33418         }
33419         b.innerHTML = "";
33420         b = null;
33421     },
33422
33423     deleteRows : function(dm, firstRow, lastRow){
33424         if(dm.getRowCount()<1){
33425             this.fireEvent("beforerefresh", this);
33426             this.mainBody.update("");
33427             this.lockedBody.update("");
33428             this.fireEvent("refresh", this);
33429         }else{
33430             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33431             var bt = this.getBodyTable();
33432             var tbody = bt.firstChild;
33433             var rows = bt.rows;
33434             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33435                 tbody.removeChild(rows[firstRow]);
33436             }
33437             this.stripeRows(firstRow);
33438             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33439         }
33440     },
33441
33442     updateRows : function(dataSource, firstRow, lastRow){
33443         var s = this.getScrollState();
33444         this.refresh();
33445         this.restoreScroll(s);
33446     },
33447
33448     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33449         if(!noRefresh){
33450            this.refresh();
33451         }
33452         this.updateHeaderSortState();
33453     },
33454
33455     getScrollState : function(){
33456         
33457         var sb = this.scroller.dom;
33458         return {left: sb.scrollLeft, top: sb.scrollTop};
33459     },
33460
33461     stripeRows : function(startRow){
33462         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33463             return;
33464         }
33465         startRow = startRow || 0;
33466         var rows = this.getBodyTable().rows;
33467         var lrows = this.getLockedTable().rows;
33468         var cls = ' x-grid-row-alt ';
33469         for(var i = startRow, len = rows.length; i < len; i++){
33470             var row = rows[i], lrow = lrows[i];
33471             var isAlt = ((i+1) % 2 == 0);
33472             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33473             if(isAlt == hasAlt){
33474                 continue;
33475             }
33476             if(isAlt){
33477                 row.className += " x-grid-row-alt";
33478             }else{
33479                 row.className = row.className.replace("x-grid-row-alt", "");
33480             }
33481             if(lrow){
33482                 lrow.className = row.className;
33483             }
33484         }
33485     },
33486
33487     restoreScroll : function(state){
33488         //Roo.log('GridView.restoreScroll');
33489         var sb = this.scroller.dom;
33490         sb.scrollLeft = state.left;
33491         sb.scrollTop = state.top;
33492         this.syncScroll();
33493     },
33494
33495     syncScroll : function(){
33496         //Roo.log('GridView.syncScroll');
33497         var sb = this.scroller.dom;
33498         var sh = this.mainHd.dom;
33499         var bs = this.mainBody.dom;
33500         var lv = this.lockedBody.dom;
33501         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33502         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33503     },
33504
33505     handleScroll : function(e){
33506         this.syncScroll();
33507         var sb = this.scroller.dom;
33508         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33509         e.stopEvent();
33510     },
33511
33512     handleWheel : function(e){
33513         var d = e.getWheelDelta();
33514         this.scroller.dom.scrollTop -= d*22;
33515         // set this here to prevent jumpy scrolling on large tables
33516         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33517         e.stopEvent();
33518     },
33519
33520     renderRows : function(startRow, endRow){
33521         // pull in all the crap needed to render rows
33522         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33523         var colCount = cm.getColumnCount();
33524
33525         if(ds.getCount() < 1){
33526             return ["", ""];
33527         }
33528
33529         // build a map for all the columns
33530         var cs = [];
33531         for(var i = 0; i < colCount; i++){
33532             var name = cm.getDataIndex(i);
33533             cs[i] = {
33534                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33535                 renderer : cm.getRenderer(i),
33536                 id : cm.getColumnId(i),
33537                 locked : cm.isLocked(i),
33538                 has_editor : cm.isCellEditable(i)
33539             };
33540         }
33541
33542         startRow = startRow || 0;
33543         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33544
33545         // records to render
33546         var rs = ds.getRange(startRow, endRow);
33547
33548         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33549     },
33550
33551     // As much as I hate to duplicate code, this was branched because FireFox really hates
33552     // [].join("") on strings. The performance difference was substantial enough to
33553     // branch this function
33554     doRender : Roo.isGecko ?
33555             function(cs, rs, ds, startRow, colCount, stripe){
33556                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33557                 // buffers
33558                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33559                 
33560                 var hasListener = this.grid.hasListener('rowclass');
33561                 var rowcfg = {};
33562                 for(var j = 0, len = rs.length; j < len; j++){
33563                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33564                     for(var i = 0; i < colCount; i++){
33565                         c = cs[i];
33566                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33567                         p.id = c.id;
33568                         p.css = p.attr = "";
33569                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33570                         if(p.value == undefined || p.value === "") {
33571                             p.value = "&#160;";
33572                         }
33573                         if(c.has_editor){
33574                             p.css += ' x-grid-editable-cell';
33575                         }
33576                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33577                             p.css +=  ' x-grid-dirty-cell';
33578                         }
33579                         var markup = ct.apply(p);
33580                         if(!c.locked){
33581                             cb+= markup;
33582                         }else{
33583                             lcb+= markup;
33584                         }
33585                     }
33586                     var alt = [];
33587                     if(stripe && ((rowIndex+1) % 2 == 0)){
33588                         alt.push("x-grid-row-alt")
33589                     }
33590                     if(r.dirty){
33591                         alt.push(  " x-grid-dirty-row");
33592                     }
33593                     rp.cells = lcb;
33594                     if(this.getRowClass){
33595                         alt.push(this.getRowClass(r, rowIndex));
33596                     }
33597                     if (hasListener) {
33598                         rowcfg = {
33599                              
33600                             record: r,
33601                             rowIndex : rowIndex,
33602                             rowClass : ''
33603                         };
33604                         this.grid.fireEvent('rowclass', this, rowcfg);
33605                         alt.push(rowcfg.rowClass);
33606                     }
33607                     rp.alt = alt.join(" ");
33608                     lbuf+= rt.apply(rp);
33609                     rp.cells = cb;
33610                     buf+=  rt.apply(rp);
33611                 }
33612                 return [lbuf, buf];
33613             } :
33614             function(cs, rs, ds, startRow, colCount, stripe){
33615                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33616                 // buffers
33617                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33618                 var hasListener = this.grid.hasListener('rowclass');
33619  
33620                 var rowcfg = {};
33621                 for(var j = 0, len = rs.length; j < len; j++){
33622                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33623                     for(var i = 0; i < colCount; i++){
33624                         c = cs[i];
33625                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33626                         p.id = c.id;
33627                         p.css = p.attr = "";
33628                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33629                         if(p.value == undefined || p.value === "") {
33630                             p.value = "&#160;";
33631                         }
33632                         //Roo.log(c);
33633                          if(c.has_editor){
33634                             p.css += ' x-grid-editable-cell';
33635                         }
33636                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33637                             p.css += ' x-grid-dirty-cell' 
33638                         }
33639                         
33640                         var markup = ct.apply(p);
33641                         if(!c.locked){
33642                             cb[cb.length] = markup;
33643                         }else{
33644                             lcb[lcb.length] = markup;
33645                         }
33646                     }
33647                     var alt = [];
33648                     if(stripe && ((rowIndex+1) % 2 == 0)){
33649                         alt.push( "x-grid-row-alt");
33650                     }
33651                     if(r.dirty){
33652                         alt.push(" x-grid-dirty-row");
33653                     }
33654                     rp.cells = lcb;
33655                     if(this.getRowClass){
33656                         alt.push( this.getRowClass(r, rowIndex));
33657                     }
33658                     if (hasListener) {
33659                         rowcfg = {
33660                              
33661                             record: r,
33662                             rowIndex : rowIndex,
33663                             rowClass : ''
33664                         };
33665                         this.grid.fireEvent('rowclass', this, rowcfg);
33666                         alt.push(rowcfg.rowClass);
33667                     }
33668                     
33669                     rp.alt = alt.join(" ");
33670                     rp.cells = lcb.join("");
33671                     lbuf[lbuf.length] = rt.apply(rp);
33672                     rp.cells = cb.join("");
33673                     buf[buf.length] =  rt.apply(rp);
33674                 }
33675                 return [lbuf.join(""), buf.join("")];
33676             },
33677
33678     renderBody : function(){
33679         var markup = this.renderRows();
33680         var bt = this.templates.body;
33681         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33682     },
33683
33684     /**
33685      * Refreshes the grid
33686      * @param {Boolean} headersToo
33687      */
33688     refresh : function(headersToo){
33689         this.fireEvent("beforerefresh", this);
33690         this.grid.stopEditing();
33691         var result = this.renderBody();
33692         this.lockedBody.update(result[0]);
33693         this.mainBody.update(result[1]);
33694         if(headersToo === true){
33695             this.updateHeaders();
33696             this.updateColumns();
33697             this.updateSplitters();
33698             this.updateHeaderSortState();
33699         }
33700         this.syncRowHeights();
33701         this.layout();
33702         this.fireEvent("refresh", this);
33703     },
33704
33705     handleColumnMove : function(cm, oldIndex, newIndex){
33706         this.indexMap = null;
33707         var s = this.getScrollState();
33708         this.refresh(true);
33709         this.restoreScroll(s);
33710         this.afterMove(newIndex);
33711     },
33712
33713     afterMove : function(colIndex){
33714         if(this.enableMoveAnim && Roo.enableFx){
33715             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33716         }
33717         // if multisort - fix sortOrder, and reload..
33718         if (this.grid.dataSource.multiSort) {
33719             // the we can call sort again..
33720             var dm = this.grid.dataSource;
33721             var cm = this.grid.colModel;
33722             var so = [];
33723             for(var i = 0; i < cm.config.length; i++ ) {
33724                 
33725                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33726                     continue; // dont' bother, it's not in sort list or being set.
33727                 }
33728                 
33729                 so.push(cm.config[i].dataIndex);
33730             };
33731             dm.sortOrder = so;
33732             dm.load(dm.lastOptions);
33733             
33734             
33735         }
33736         
33737     },
33738
33739     updateCell : function(dm, rowIndex, dataIndex){
33740         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33741         if(typeof colIndex == "undefined"){ // not present in grid
33742             return;
33743         }
33744         var cm = this.grid.colModel;
33745         var cell = this.getCell(rowIndex, colIndex);
33746         var cellText = this.getCellText(rowIndex, colIndex);
33747
33748         var p = {
33749             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33750             id : cm.getColumnId(colIndex),
33751             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33752         };
33753         var renderer = cm.getRenderer(colIndex);
33754         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33755         if(typeof val == "undefined" || val === "") {
33756             val = "&#160;";
33757         }
33758         cellText.innerHTML = val;
33759         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33760         this.syncRowHeights(rowIndex, rowIndex);
33761     },
33762
33763     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33764         var maxWidth = 0;
33765         if(this.grid.autoSizeHeaders){
33766             var h = this.getHeaderCellMeasure(colIndex);
33767             maxWidth = Math.max(maxWidth, h.scrollWidth);
33768         }
33769         var tb, index;
33770         if(this.cm.isLocked(colIndex)){
33771             tb = this.getLockedTable();
33772             index = colIndex;
33773         }else{
33774             tb = this.getBodyTable();
33775             index = colIndex - this.cm.getLockedCount();
33776         }
33777         if(tb && tb.rows){
33778             var rows = tb.rows;
33779             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33780             for(var i = 0; i < stopIndex; i++){
33781                 var cell = rows[i].childNodes[index].firstChild;
33782                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33783             }
33784         }
33785         return maxWidth + /*margin for error in IE*/ 5;
33786     },
33787     /**
33788      * Autofit a column to its content.
33789      * @param {Number} colIndex
33790      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33791      */
33792      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33793          if(this.cm.isHidden(colIndex)){
33794              return; // can't calc a hidden column
33795          }
33796         if(forceMinSize){
33797             var cid = this.cm.getColumnId(colIndex);
33798             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33799            if(this.grid.autoSizeHeaders){
33800                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33801            }
33802         }
33803         var newWidth = this.calcColumnWidth(colIndex);
33804         this.cm.setColumnWidth(colIndex,
33805             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33806         if(!suppressEvent){
33807             this.grid.fireEvent("columnresize", colIndex, newWidth);
33808         }
33809     },
33810
33811     /**
33812      * Autofits all columns to their content and then expands to fit any extra space in the grid
33813      */
33814      autoSizeColumns : function(){
33815         var cm = this.grid.colModel;
33816         var colCount = cm.getColumnCount();
33817         for(var i = 0; i < colCount; i++){
33818             this.autoSizeColumn(i, true, true);
33819         }
33820         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33821             this.fitColumns();
33822         }else{
33823             this.updateColumns();
33824             this.layout();
33825         }
33826     },
33827
33828     /**
33829      * Autofits all columns to the grid's width proportionate with their current size
33830      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33831      */
33832     fitColumns : function(reserveScrollSpace){
33833         var cm = this.grid.colModel;
33834         var colCount = cm.getColumnCount();
33835         var cols = [];
33836         var width = 0;
33837         var i, w;
33838         for (i = 0; i < colCount; i++){
33839             if(!cm.isHidden(i) && !cm.isFixed(i)){
33840                 w = cm.getColumnWidth(i);
33841                 cols.push(i);
33842                 cols.push(w);
33843                 width += w;
33844             }
33845         }
33846         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33847         if(reserveScrollSpace){
33848             avail -= 17;
33849         }
33850         var frac = (avail - cm.getTotalWidth())/width;
33851         while (cols.length){
33852             w = cols.pop();
33853             i = cols.pop();
33854             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33855         }
33856         this.updateColumns();
33857         this.layout();
33858     },
33859
33860     onRowSelect : function(rowIndex){
33861         var row = this.getRowComposite(rowIndex);
33862         row.addClass("x-grid-row-selected");
33863     },
33864
33865     onRowDeselect : function(rowIndex){
33866         var row = this.getRowComposite(rowIndex);
33867         row.removeClass("x-grid-row-selected");
33868     },
33869
33870     onCellSelect : function(row, col){
33871         var cell = this.getCell(row, col);
33872         if(cell){
33873             Roo.fly(cell).addClass("x-grid-cell-selected");
33874         }
33875     },
33876
33877     onCellDeselect : function(row, col){
33878         var cell = this.getCell(row, col);
33879         if(cell){
33880             Roo.fly(cell).removeClass("x-grid-cell-selected");
33881         }
33882     },
33883
33884     updateHeaderSortState : function(){
33885         
33886         // sort state can be single { field: xxx, direction : yyy}
33887         // or   { xxx=>ASC , yyy : DESC ..... }
33888         
33889         var mstate = {};
33890         if (!this.ds.multiSort) { 
33891             var state = this.ds.getSortState();
33892             if(!state){
33893                 return;
33894             }
33895             mstate[state.field] = state.direction;
33896             // FIXME... - this is not used here.. but might be elsewhere..
33897             this.sortState = state;
33898             
33899         } else {
33900             mstate = this.ds.sortToggle;
33901         }
33902         //remove existing sort classes..
33903         
33904         var sc = this.sortClasses;
33905         var hds = this.el.select(this.headerSelector).removeClass(sc);
33906         
33907         for(var f in mstate) {
33908         
33909             var sortColumn = this.cm.findColumnIndex(f);
33910             
33911             if(sortColumn != -1){
33912                 var sortDir = mstate[f];        
33913                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33914             }
33915         }
33916         
33917          
33918         
33919     },
33920
33921
33922     handleHeaderClick : function(g, index,e){
33923         
33924         Roo.log("header click");
33925         
33926         if (Roo.isTouch) {
33927             // touch events on header are handled by context
33928             this.handleHdCtx(g,index,e);
33929             return;
33930         }
33931         
33932         
33933         if(this.headersDisabled){
33934             return;
33935         }
33936         var dm = g.dataSource, cm = g.colModel;
33937         if(!cm.isSortable(index)){
33938             return;
33939         }
33940         g.stopEditing();
33941         
33942         if (dm.multiSort) {
33943             // update the sortOrder
33944             var so = [];
33945             for(var i = 0; i < cm.config.length; i++ ) {
33946                 
33947                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33948                     continue; // dont' bother, it's not in sort list or being set.
33949                 }
33950                 
33951                 so.push(cm.config[i].dataIndex);
33952             };
33953             dm.sortOrder = so;
33954         }
33955         
33956         
33957         dm.sort(cm.getDataIndex(index));
33958     },
33959
33960
33961     destroy : function(){
33962         if(this.colMenu){
33963             this.colMenu.removeAll();
33964             Roo.menu.MenuMgr.unregister(this.colMenu);
33965             this.colMenu.getEl().remove();
33966             delete this.colMenu;
33967         }
33968         if(this.hmenu){
33969             this.hmenu.removeAll();
33970             Roo.menu.MenuMgr.unregister(this.hmenu);
33971             this.hmenu.getEl().remove();
33972             delete this.hmenu;
33973         }
33974         if(this.grid.enableColumnMove){
33975             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33976             if(dds){
33977                 for(var dd in dds){
33978                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33979                         var elid = dds[dd].dragElId;
33980                         dds[dd].unreg();
33981                         Roo.get(elid).remove();
33982                     } else if(dds[dd].config.isTarget){
33983                         dds[dd].proxyTop.remove();
33984                         dds[dd].proxyBottom.remove();
33985                         dds[dd].unreg();
33986                     }
33987                     if(Roo.dd.DDM.locationCache[dd]){
33988                         delete Roo.dd.DDM.locationCache[dd];
33989                     }
33990                 }
33991                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33992             }
33993         }
33994         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33995         this.bind(null, null);
33996         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33997     },
33998
33999     handleLockChange : function(){
34000         this.refresh(true);
34001     },
34002
34003     onDenyColumnLock : function(){
34004
34005     },
34006
34007     onDenyColumnHide : function(){
34008
34009     },
34010
34011     handleHdMenuClick : function(item){
34012         var index = this.hdCtxIndex;
34013         var cm = this.cm, ds = this.ds;
34014         switch(item.id){
34015             case "asc":
34016                 ds.sort(cm.getDataIndex(index), "ASC");
34017                 break;
34018             case "desc":
34019                 ds.sort(cm.getDataIndex(index), "DESC");
34020                 break;
34021             case "lock":
34022                 var lc = cm.getLockedCount();
34023                 if(cm.getColumnCount(true) <= lc+1){
34024                     this.onDenyColumnLock();
34025                     return;
34026                 }
34027                 if(lc != index){
34028                     cm.setLocked(index, true, true);
34029                     cm.moveColumn(index, lc);
34030                     this.grid.fireEvent("columnmove", index, lc);
34031                 }else{
34032                     cm.setLocked(index, true);
34033                 }
34034             break;
34035             case "unlock":
34036                 var lc = cm.getLockedCount();
34037                 if((lc-1) != index){
34038                     cm.setLocked(index, false, true);
34039                     cm.moveColumn(index, lc-1);
34040                     this.grid.fireEvent("columnmove", index, lc-1);
34041                 }else{
34042                     cm.setLocked(index, false);
34043                 }
34044             break;
34045             case 'wider': // used to expand cols on touch..
34046             case 'narrow':
34047                 var cw = cm.getColumnWidth(index);
34048                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34049                 cw = Math.max(0, cw);
34050                 cw = Math.min(cw,4000);
34051                 cm.setColumnWidth(index, cw);
34052                 break;
34053                 
34054             default:
34055                 index = cm.getIndexById(item.id.substr(4));
34056                 if(index != -1){
34057                     if(item.checked && cm.getColumnCount(true) <= 1){
34058                         this.onDenyColumnHide();
34059                         return false;
34060                     }
34061                     cm.setHidden(index, item.checked);
34062                 }
34063         }
34064         return true;
34065     },
34066
34067     beforeColMenuShow : function(){
34068         var cm = this.cm,  colCount = cm.getColumnCount();
34069         this.colMenu.removeAll();
34070         for(var i = 0; i < colCount; i++){
34071             this.colMenu.add(new Roo.menu.CheckItem({
34072                 id: "col-"+cm.getColumnId(i),
34073                 text: cm.getColumnHeader(i),
34074                 checked: !cm.isHidden(i),
34075                 hideOnClick:false
34076             }));
34077         }
34078     },
34079
34080     handleHdCtx : function(g, index, e){
34081         e.stopEvent();
34082         var hd = this.getHeaderCell(index);
34083         this.hdCtxIndex = index;
34084         var ms = this.hmenu.items, cm = this.cm;
34085         ms.get("asc").setDisabled(!cm.isSortable(index));
34086         ms.get("desc").setDisabled(!cm.isSortable(index));
34087         if(this.grid.enableColLock !== false){
34088             ms.get("lock").setDisabled(cm.isLocked(index));
34089             ms.get("unlock").setDisabled(!cm.isLocked(index));
34090         }
34091         this.hmenu.show(hd, "tl-bl");
34092     },
34093
34094     handleHdOver : function(e){
34095         var hd = this.findHeaderCell(e.getTarget());
34096         if(hd && !this.headersDisabled){
34097             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34098                this.fly(hd).addClass("x-grid-hd-over");
34099             }
34100         }
34101     },
34102
34103     handleHdOut : function(e){
34104         var hd = this.findHeaderCell(e.getTarget());
34105         if(hd){
34106             this.fly(hd).removeClass("x-grid-hd-over");
34107         }
34108     },
34109
34110     handleSplitDblClick : function(e, t){
34111         var i = this.getCellIndex(t);
34112         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34113             this.autoSizeColumn(i, true);
34114             this.layout();
34115         }
34116     },
34117
34118     render : function(){
34119
34120         var cm = this.cm;
34121         var colCount = cm.getColumnCount();
34122
34123         if(this.grid.monitorWindowResize === true){
34124             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34125         }
34126         var header = this.renderHeaders();
34127         var body = this.templates.body.apply({rows:""});
34128         var html = this.templates.master.apply({
34129             lockedBody: body,
34130             body: body,
34131             lockedHeader: header[0],
34132             header: header[1]
34133         });
34134
34135         //this.updateColumns();
34136
34137         this.grid.getGridEl().dom.innerHTML = html;
34138
34139         this.initElements();
34140         
34141         // a kludge to fix the random scolling effect in webkit
34142         this.el.on("scroll", function() {
34143             this.el.dom.scrollTop=0; // hopefully not recursive..
34144         },this);
34145
34146         this.scroller.on("scroll", this.handleScroll, this);
34147         this.lockedBody.on("mousewheel", this.handleWheel, this);
34148         this.mainBody.on("mousewheel", this.handleWheel, this);
34149
34150         this.mainHd.on("mouseover", this.handleHdOver, this);
34151         this.mainHd.on("mouseout", this.handleHdOut, this);
34152         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34153                 {delegate: "."+this.splitClass});
34154
34155         this.lockedHd.on("mouseover", this.handleHdOver, this);
34156         this.lockedHd.on("mouseout", this.handleHdOut, this);
34157         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34158                 {delegate: "."+this.splitClass});
34159
34160         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34161             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34162         }
34163
34164         this.updateSplitters();
34165
34166         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34167             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34168             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34169         }
34170
34171         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34172             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34173             this.hmenu.add(
34174                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34175                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34176             );
34177             if(this.grid.enableColLock !== false){
34178                 this.hmenu.add('-',
34179                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34180                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34181                 );
34182             }
34183             if (Roo.isTouch) {
34184                  this.hmenu.add('-',
34185                     {id:"wider", text: this.columnsWiderText},
34186                     {id:"narrow", text: this.columnsNarrowText }
34187                 );
34188                 
34189                  
34190             }
34191             
34192             if(this.grid.enableColumnHide !== false){
34193
34194                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34195                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34196                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34197
34198                 this.hmenu.add('-',
34199                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34200                 );
34201             }
34202             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34203
34204             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34205         }
34206
34207         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34208             this.dd = new Roo.grid.GridDragZone(this.grid, {
34209                 ddGroup : this.grid.ddGroup || 'GridDD'
34210             });
34211             
34212         }
34213
34214         /*
34215         for(var i = 0; i < colCount; i++){
34216             if(cm.isHidden(i)){
34217                 this.hideColumn(i);
34218             }
34219             if(cm.config[i].align){
34220                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34221                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34222             }
34223         }*/
34224         
34225         this.updateHeaderSortState();
34226
34227         this.beforeInitialResize();
34228         this.layout(true);
34229
34230         // two part rendering gives faster view to the user
34231         this.renderPhase2.defer(1, this);
34232     },
34233
34234     renderPhase2 : function(){
34235         // render the rows now
34236         this.refresh();
34237         if(this.grid.autoSizeColumns){
34238             this.autoSizeColumns();
34239         }
34240     },
34241
34242     beforeInitialResize : function(){
34243
34244     },
34245
34246     onColumnSplitterMoved : function(i, w){
34247         this.userResized = true;
34248         var cm = this.grid.colModel;
34249         cm.setColumnWidth(i, w, true);
34250         var cid = cm.getColumnId(i);
34251         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34252         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34253         this.updateSplitters();
34254         this.layout();
34255         this.grid.fireEvent("columnresize", i, w);
34256     },
34257
34258     syncRowHeights : function(startIndex, endIndex){
34259         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34260             startIndex = startIndex || 0;
34261             var mrows = this.getBodyTable().rows;
34262             var lrows = this.getLockedTable().rows;
34263             var len = mrows.length-1;
34264             endIndex = Math.min(endIndex || len, len);
34265             for(var i = startIndex; i <= endIndex; i++){
34266                 var m = mrows[i], l = lrows[i];
34267                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34268                 m.style.height = l.style.height = h + "px";
34269             }
34270         }
34271     },
34272
34273     layout : function(initialRender, is2ndPass){
34274         var g = this.grid;
34275         var auto = g.autoHeight;
34276         var scrollOffset = 16;
34277         var c = g.getGridEl(), cm = this.cm,
34278                 expandCol = g.autoExpandColumn,
34279                 gv = this;
34280         //c.beginMeasure();
34281
34282         if(!c.dom.offsetWidth){ // display:none?
34283             if(initialRender){
34284                 this.lockedWrap.show();
34285                 this.mainWrap.show();
34286             }
34287             return;
34288         }
34289
34290         var hasLock = this.cm.isLocked(0);
34291
34292         var tbh = this.headerPanel.getHeight();
34293         var bbh = this.footerPanel.getHeight();
34294
34295         if(auto){
34296             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34297             var newHeight = ch + c.getBorderWidth("tb");
34298             if(g.maxHeight){
34299                 newHeight = Math.min(g.maxHeight, newHeight);
34300             }
34301             c.setHeight(newHeight);
34302         }
34303
34304         if(g.autoWidth){
34305             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34306         }
34307
34308         var s = this.scroller;
34309
34310         var csize = c.getSize(true);
34311
34312         this.el.setSize(csize.width, csize.height);
34313
34314         this.headerPanel.setWidth(csize.width);
34315         this.footerPanel.setWidth(csize.width);
34316
34317         var hdHeight = this.mainHd.getHeight();
34318         var vw = csize.width;
34319         var vh = csize.height - (tbh + bbh);
34320
34321         s.setSize(vw, vh);
34322
34323         var bt = this.getBodyTable();
34324         
34325         if(cm.getLockedCount() == cm.config.length){
34326             bt = this.getLockedTable();
34327         }
34328         
34329         var ltWidth = hasLock ?
34330                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34331
34332         var scrollHeight = bt.offsetHeight;
34333         var scrollWidth = ltWidth + bt.offsetWidth;
34334         var vscroll = false, hscroll = false;
34335
34336         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34337
34338         var lw = this.lockedWrap, mw = this.mainWrap;
34339         var lb = this.lockedBody, mb = this.mainBody;
34340
34341         setTimeout(function(){
34342             var t = s.dom.offsetTop;
34343             var w = s.dom.clientWidth,
34344                 h = s.dom.clientHeight;
34345
34346             lw.setTop(t);
34347             lw.setSize(ltWidth, h);
34348
34349             mw.setLeftTop(ltWidth, t);
34350             mw.setSize(w-ltWidth, h);
34351
34352             lb.setHeight(h-hdHeight);
34353             mb.setHeight(h-hdHeight);
34354
34355             if(is2ndPass !== true && !gv.userResized && expandCol){
34356                 // high speed resize without full column calculation
34357                 
34358                 var ci = cm.getIndexById(expandCol);
34359                 if (ci < 0) {
34360                     ci = cm.findColumnIndex(expandCol);
34361                 }
34362                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34363                 var expandId = cm.getColumnId(ci);
34364                 var  tw = cm.getTotalWidth(false);
34365                 var currentWidth = cm.getColumnWidth(ci);
34366                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34367                 if(currentWidth != cw){
34368                     cm.setColumnWidth(ci, cw, true);
34369                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34370                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34371                     gv.updateSplitters();
34372                     gv.layout(false, true);
34373                 }
34374             }
34375
34376             if(initialRender){
34377                 lw.show();
34378                 mw.show();
34379             }
34380             //c.endMeasure();
34381         }, 10);
34382     },
34383
34384     onWindowResize : function(){
34385         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34386             return;
34387         }
34388         this.layout();
34389     },
34390
34391     appendFooter : function(parentEl){
34392         return null;
34393     },
34394
34395     sortAscText : "Sort Ascending",
34396     sortDescText : "Sort Descending",
34397     lockText : "Lock Column",
34398     unlockText : "Unlock Column",
34399     columnsText : "Columns",
34400  
34401     columnsWiderText : "Wider",
34402     columnsNarrowText : "Thinner"
34403 });
34404
34405
34406 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34407     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34408     this.proxy.el.addClass('x-grid3-col-dd');
34409 };
34410
34411 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34412     handleMouseDown : function(e){
34413
34414     },
34415
34416     callHandleMouseDown : function(e){
34417         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34418     }
34419 });
34420 /*
34421  * Based on:
34422  * Ext JS Library 1.1.1
34423  * Copyright(c) 2006-2007, Ext JS, LLC.
34424  *
34425  * Originally Released Under LGPL - original licence link has changed is not relivant.
34426  *
34427  * Fork - LGPL
34428  * <script type="text/javascript">
34429  */
34430  
34431 // private
34432 // This is a support class used internally by the Grid components
34433 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34434     this.grid = grid;
34435     this.view = grid.getView();
34436     this.proxy = this.view.resizeProxy;
34437     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34438         "gridSplitters" + this.grid.getGridEl().id, {
34439         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34440     });
34441     this.setHandleElId(Roo.id(hd));
34442     this.setOuterHandleElId(Roo.id(hd2));
34443     this.scroll = false;
34444 };
34445 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34446     fly: Roo.Element.fly,
34447
34448     b4StartDrag : function(x, y){
34449         this.view.headersDisabled = true;
34450         this.proxy.setHeight(this.view.mainWrap.getHeight());
34451         var w = this.cm.getColumnWidth(this.cellIndex);
34452         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34453         this.resetConstraints();
34454         this.setXConstraint(minw, 1000);
34455         this.setYConstraint(0, 0);
34456         this.minX = x - minw;
34457         this.maxX = x + 1000;
34458         this.startPos = x;
34459         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34460     },
34461
34462
34463     handleMouseDown : function(e){
34464         ev = Roo.EventObject.setEvent(e);
34465         var t = this.fly(ev.getTarget());
34466         if(t.hasClass("x-grid-split")){
34467             this.cellIndex = this.view.getCellIndex(t.dom);
34468             this.split = t.dom;
34469             this.cm = this.grid.colModel;
34470             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34471                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34472             }
34473         }
34474     },
34475
34476     endDrag : function(e){
34477         this.view.headersDisabled = false;
34478         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34479         var diff = endX - this.startPos;
34480         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34481     },
34482
34483     autoOffset : function(){
34484         this.setDelta(0,0);
34485     }
34486 });/*
34487  * Based on:
34488  * Ext JS Library 1.1.1
34489  * Copyright(c) 2006-2007, Ext JS, LLC.
34490  *
34491  * Originally Released Under LGPL - original licence link has changed is not relivant.
34492  *
34493  * Fork - LGPL
34494  * <script type="text/javascript">
34495  */
34496  
34497 // private
34498 // This is a support class used internally by the Grid components
34499 Roo.grid.GridDragZone = function(grid, config){
34500     this.view = grid.getView();
34501     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34502     if(this.view.lockedBody){
34503         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34504         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34505     }
34506     this.scroll = false;
34507     this.grid = grid;
34508     this.ddel = document.createElement('div');
34509     this.ddel.className = 'x-grid-dd-wrap';
34510 };
34511
34512 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34513     ddGroup : "GridDD",
34514
34515     getDragData : function(e){
34516         var t = Roo.lib.Event.getTarget(e);
34517         var rowIndex = this.view.findRowIndex(t);
34518         var sm = this.grid.selModel;
34519             
34520         //Roo.log(rowIndex);
34521         
34522         if (sm.getSelectedCell) {
34523             // cell selection..
34524             if (!sm.getSelectedCell()) {
34525                 return false;
34526             }
34527             if (rowIndex != sm.getSelectedCell()[0]) {
34528                 return false;
34529             }
34530         
34531         }
34532         
34533         if(rowIndex !== false){
34534             
34535             // if editorgrid.. 
34536             
34537             
34538             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34539                
34540             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34541               //  
34542             //}
34543             if (e.hasModifier()){
34544                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34545             }
34546             
34547             Roo.log("getDragData");
34548             
34549             return {
34550                 grid: this.grid,
34551                 ddel: this.ddel,
34552                 rowIndex: rowIndex,
34553                 selections:sm.getSelections ? sm.getSelections() : (
34554                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34555                 )
34556             };
34557         }
34558         return false;
34559     },
34560
34561     onInitDrag : function(e){
34562         var data = this.dragData;
34563         this.ddel.innerHTML = this.grid.getDragDropText();
34564         this.proxy.update(this.ddel);
34565         // fire start drag?
34566     },
34567
34568     afterRepair : function(){
34569         this.dragging = false;
34570     },
34571
34572     getRepairXY : function(e, data){
34573         return false;
34574     },
34575
34576     onEndDrag : function(data, e){
34577         // fire end drag?
34578     },
34579
34580     onValidDrop : function(dd, e, id){
34581         // fire drag drop?
34582         this.hideProxy();
34583     },
34584
34585     beforeInvalidDrop : function(e, id){
34586
34587     }
34588 });/*
34589  * Based on:
34590  * Ext JS Library 1.1.1
34591  * Copyright(c) 2006-2007, Ext JS, LLC.
34592  *
34593  * Originally Released Under LGPL - original licence link has changed is not relivant.
34594  *
34595  * Fork - LGPL
34596  * <script type="text/javascript">
34597  */
34598  
34599
34600 /**
34601  * @class Roo.grid.ColumnModel
34602  * @extends Roo.util.Observable
34603  * This is the default implementation of a ColumnModel used by the Grid. It defines
34604  * the columns in the grid.
34605  * <br>Usage:<br>
34606  <pre><code>
34607  var colModel = new Roo.grid.ColumnModel([
34608         {header: "Ticker", width: 60, sortable: true, locked: true},
34609         {header: "Company Name", width: 150, sortable: true},
34610         {header: "Market Cap.", width: 100, sortable: true},
34611         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34612         {header: "Employees", width: 100, sortable: true, resizable: false}
34613  ]);
34614  </code></pre>
34615  * <p>
34616  
34617  * The config options listed for this class are options which may appear in each
34618  * individual column definition.
34619  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34620  * @constructor
34621  * @param {Object} config An Array of column config objects. See this class's
34622  * config objects for details.
34623 */
34624 Roo.grid.ColumnModel = function(config){
34625         /**
34626      * The config passed into the constructor
34627      */
34628     this.config = config;
34629     this.lookup = {};
34630
34631     // if no id, create one
34632     // if the column does not have a dataIndex mapping,
34633     // map it to the order it is in the config
34634     for(var i = 0, len = config.length; i < len; i++){
34635         var c = config[i];
34636         if(typeof c.dataIndex == "undefined"){
34637             c.dataIndex = i;
34638         }
34639         if(typeof c.renderer == "string"){
34640             c.renderer = Roo.util.Format[c.renderer];
34641         }
34642         if(typeof c.id == "undefined"){
34643             c.id = Roo.id();
34644         }
34645         if(c.editor && c.editor.xtype){
34646             c.editor  = Roo.factory(c.editor, Roo.grid);
34647         }
34648         if(c.editor && c.editor.isFormField){
34649             c.editor = new Roo.grid.GridEditor(c.editor);
34650         }
34651         this.lookup[c.id] = c;
34652     }
34653
34654     /**
34655      * The width of columns which have no width specified (defaults to 100)
34656      * @type Number
34657      */
34658     this.defaultWidth = 100;
34659
34660     /**
34661      * Default sortable of columns which have no sortable specified (defaults to false)
34662      * @type Boolean
34663      */
34664     this.defaultSortable = false;
34665
34666     this.addEvents({
34667         /**
34668              * @event widthchange
34669              * Fires when the width of a column changes.
34670              * @param {ColumnModel} this
34671              * @param {Number} columnIndex The column index
34672              * @param {Number} newWidth The new width
34673              */
34674             "widthchange": true,
34675         /**
34676              * @event headerchange
34677              * Fires when the text of a header changes.
34678              * @param {ColumnModel} this
34679              * @param {Number} columnIndex The column index
34680              * @param {Number} newText The new header text
34681              */
34682             "headerchange": true,
34683         /**
34684              * @event hiddenchange
34685              * Fires when a column is hidden or "unhidden".
34686              * @param {ColumnModel} this
34687              * @param {Number} columnIndex The column index
34688              * @param {Boolean} hidden true if hidden, false otherwise
34689              */
34690             "hiddenchange": true,
34691             /**
34692          * @event columnmoved
34693          * Fires when a column is moved.
34694          * @param {ColumnModel} this
34695          * @param {Number} oldIndex
34696          * @param {Number} newIndex
34697          */
34698         "columnmoved" : true,
34699         /**
34700          * @event columlockchange
34701          * Fires when a column's locked state is changed
34702          * @param {ColumnModel} this
34703          * @param {Number} colIndex
34704          * @param {Boolean} locked true if locked
34705          */
34706         "columnlockchange" : true
34707     });
34708     Roo.grid.ColumnModel.superclass.constructor.call(this);
34709 };
34710 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34711     /**
34712      * @cfg {String} header The header text to display in the Grid view.
34713      */
34714     /**
34715      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34716      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34717      * specified, the column's index is used as an index into the Record's data Array.
34718      */
34719     /**
34720      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34721      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34722      */
34723     /**
34724      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34725      * Defaults to the value of the {@link #defaultSortable} property.
34726      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34727      */
34728     /**
34729      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34730      */
34731     /**
34732      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34733      */
34734     /**
34735      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34736      */
34737     /**
34738      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34739      */
34740     /**
34741      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34742      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34743      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34744      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34745      */
34746        /**
34747      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34748      */
34749     /**
34750      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34751      */
34752     /**
34753      * @cfg {String} cursor (Optional)
34754      */
34755     /**
34756      * @cfg {String} tooltip (Optional)
34757      */
34758     /**
34759      * @cfg {Number} xs (Optional)
34760      */
34761     /**
34762      * @cfg {Number} sm (Optional)
34763      */
34764     /**
34765      * @cfg {Number} md (Optional)
34766      */
34767     /**
34768      * @cfg {Number} lg (Optional)
34769      */
34770     /**
34771      * Returns the id of the column at the specified index.
34772      * @param {Number} index The column index
34773      * @return {String} the id
34774      */
34775     getColumnId : function(index){
34776         return this.config[index].id;
34777     },
34778
34779     /**
34780      * Returns the column for a specified id.
34781      * @param {String} id The column id
34782      * @return {Object} the column
34783      */
34784     getColumnById : function(id){
34785         return this.lookup[id];
34786     },
34787
34788     
34789     /**
34790      * Returns the column for a specified dataIndex.
34791      * @param {String} dataIndex The column dataIndex
34792      * @return {Object|Boolean} the column or false if not found
34793      */
34794     getColumnByDataIndex: function(dataIndex){
34795         var index = this.findColumnIndex(dataIndex);
34796         return index > -1 ? this.config[index] : false;
34797     },
34798     
34799     /**
34800      * Returns the index for a specified column id.
34801      * @param {String} id The column id
34802      * @return {Number} the index, or -1 if not found
34803      */
34804     getIndexById : function(id){
34805         for(var i = 0, len = this.config.length; i < len; i++){
34806             if(this.config[i].id == id){
34807                 return i;
34808             }
34809         }
34810         return -1;
34811     },
34812     
34813     /**
34814      * Returns the index for a specified column dataIndex.
34815      * @param {String} dataIndex The column dataIndex
34816      * @return {Number} the index, or -1 if not found
34817      */
34818     
34819     findColumnIndex : function(dataIndex){
34820         for(var i = 0, len = this.config.length; i < len; i++){
34821             if(this.config[i].dataIndex == dataIndex){
34822                 return i;
34823             }
34824         }
34825         return -1;
34826     },
34827     
34828     
34829     moveColumn : function(oldIndex, newIndex){
34830         var c = this.config[oldIndex];
34831         this.config.splice(oldIndex, 1);
34832         this.config.splice(newIndex, 0, c);
34833         this.dataMap = null;
34834         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34835     },
34836
34837     isLocked : function(colIndex){
34838         return this.config[colIndex].locked === true;
34839     },
34840
34841     setLocked : function(colIndex, value, suppressEvent){
34842         if(this.isLocked(colIndex) == value){
34843             return;
34844         }
34845         this.config[colIndex].locked = value;
34846         if(!suppressEvent){
34847             this.fireEvent("columnlockchange", this, colIndex, value);
34848         }
34849     },
34850
34851     getTotalLockedWidth : function(){
34852         var totalWidth = 0;
34853         for(var i = 0; i < this.config.length; i++){
34854             if(this.isLocked(i) && !this.isHidden(i)){
34855                 this.totalWidth += this.getColumnWidth(i);
34856             }
34857         }
34858         return totalWidth;
34859     },
34860
34861     getLockedCount : function(){
34862         for(var i = 0, len = this.config.length; i < len; i++){
34863             if(!this.isLocked(i)){
34864                 return i;
34865             }
34866         }
34867         
34868         return this.config.length;
34869     },
34870
34871     /**
34872      * Returns the number of columns.
34873      * @return {Number}
34874      */
34875     getColumnCount : function(visibleOnly){
34876         if(visibleOnly === true){
34877             var c = 0;
34878             for(var i = 0, len = this.config.length; i < len; i++){
34879                 if(!this.isHidden(i)){
34880                     c++;
34881                 }
34882             }
34883             return c;
34884         }
34885         return this.config.length;
34886     },
34887
34888     /**
34889      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34890      * @param {Function} fn
34891      * @param {Object} scope (optional)
34892      * @return {Array} result
34893      */
34894     getColumnsBy : function(fn, scope){
34895         var r = [];
34896         for(var i = 0, len = this.config.length; i < len; i++){
34897             var c = this.config[i];
34898             if(fn.call(scope||this, c, i) === true){
34899                 r[r.length] = c;
34900             }
34901         }
34902         return r;
34903     },
34904
34905     /**
34906      * Returns true if the specified column is sortable.
34907      * @param {Number} col The column index
34908      * @return {Boolean}
34909      */
34910     isSortable : function(col){
34911         if(typeof this.config[col].sortable == "undefined"){
34912             return this.defaultSortable;
34913         }
34914         return this.config[col].sortable;
34915     },
34916
34917     /**
34918      * Returns the rendering (formatting) function defined for the column.
34919      * @param {Number} col The column index.
34920      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34921      */
34922     getRenderer : function(col){
34923         if(!this.config[col].renderer){
34924             return Roo.grid.ColumnModel.defaultRenderer;
34925         }
34926         return this.config[col].renderer;
34927     },
34928
34929     /**
34930      * Sets the rendering (formatting) function for a column.
34931      * @param {Number} col The column index
34932      * @param {Function} fn The function to use to process the cell's raw data
34933      * to return HTML markup for the grid view. The render function is called with
34934      * the following parameters:<ul>
34935      * <li>Data value.</li>
34936      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34937      * <li>css A CSS style string to apply to the table cell.</li>
34938      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34939      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34940      * <li>Row index</li>
34941      * <li>Column index</li>
34942      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34943      */
34944     setRenderer : function(col, fn){
34945         this.config[col].renderer = fn;
34946     },
34947
34948     /**
34949      * Returns the width for the specified column.
34950      * @param {Number} col The column index
34951      * @return {Number}
34952      */
34953     getColumnWidth : function(col){
34954         return this.config[col].width * 1 || this.defaultWidth;
34955     },
34956
34957     /**
34958      * Sets the width for a column.
34959      * @param {Number} col The column index
34960      * @param {Number} width The new width
34961      */
34962     setColumnWidth : function(col, width, suppressEvent){
34963         this.config[col].width = width;
34964         this.totalWidth = null;
34965         if(!suppressEvent){
34966              this.fireEvent("widthchange", this, col, width);
34967         }
34968     },
34969
34970     /**
34971      * Returns the total width of all columns.
34972      * @param {Boolean} includeHidden True to include hidden column widths
34973      * @return {Number}
34974      */
34975     getTotalWidth : function(includeHidden){
34976         if(!this.totalWidth){
34977             this.totalWidth = 0;
34978             for(var i = 0, len = this.config.length; i < len; i++){
34979                 if(includeHidden || !this.isHidden(i)){
34980                     this.totalWidth += this.getColumnWidth(i);
34981                 }
34982             }
34983         }
34984         return this.totalWidth;
34985     },
34986
34987     /**
34988      * Returns the header for the specified column.
34989      * @param {Number} col The column index
34990      * @return {String}
34991      */
34992     getColumnHeader : function(col){
34993         return this.config[col].header;
34994     },
34995
34996     /**
34997      * Sets the header for a column.
34998      * @param {Number} col The column index
34999      * @param {String} header The new header
35000      */
35001     setColumnHeader : function(col, header){
35002         this.config[col].header = header;
35003         this.fireEvent("headerchange", this, col, header);
35004     },
35005
35006     /**
35007      * Returns the tooltip for the specified column.
35008      * @param {Number} col The column index
35009      * @return {String}
35010      */
35011     getColumnTooltip : function(col){
35012             return this.config[col].tooltip;
35013     },
35014     /**
35015      * Sets the tooltip for a column.
35016      * @param {Number} col The column index
35017      * @param {String} tooltip The new tooltip
35018      */
35019     setColumnTooltip : function(col, tooltip){
35020             this.config[col].tooltip = tooltip;
35021     },
35022
35023     /**
35024      * Returns the dataIndex for the specified column.
35025      * @param {Number} col The column index
35026      * @return {Number}
35027      */
35028     getDataIndex : function(col){
35029         return this.config[col].dataIndex;
35030     },
35031
35032     /**
35033      * Sets the dataIndex for a column.
35034      * @param {Number} col The column index
35035      * @param {Number} dataIndex The new dataIndex
35036      */
35037     setDataIndex : function(col, dataIndex){
35038         this.config[col].dataIndex = dataIndex;
35039     },
35040
35041     
35042     
35043     /**
35044      * Returns true if the cell is editable.
35045      * @param {Number} colIndex The column index
35046      * @param {Number} rowIndex The row index - this is nto actually used..?
35047      * @return {Boolean}
35048      */
35049     isCellEditable : function(colIndex, rowIndex){
35050         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35051     },
35052
35053     /**
35054      * Returns the editor defined for the cell/column.
35055      * return false or null to disable editing.
35056      * @param {Number} colIndex The column index
35057      * @param {Number} rowIndex The row index
35058      * @return {Object}
35059      */
35060     getCellEditor : function(colIndex, rowIndex){
35061         return this.config[colIndex].editor;
35062     },
35063
35064     /**
35065      * Sets if a column is editable.
35066      * @param {Number} col The column index
35067      * @param {Boolean} editable True if the column is editable
35068      */
35069     setEditable : function(col, editable){
35070         this.config[col].editable = editable;
35071     },
35072
35073
35074     /**
35075      * Returns true if the column is hidden.
35076      * @param {Number} colIndex The column index
35077      * @return {Boolean}
35078      */
35079     isHidden : function(colIndex){
35080         return this.config[colIndex].hidden;
35081     },
35082
35083
35084     /**
35085      * Returns true if the column width cannot be changed
35086      */
35087     isFixed : function(colIndex){
35088         return this.config[colIndex].fixed;
35089     },
35090
35091     /**
35092      * Returns true if the column can be resized
35093      * @return {Boolean}
35094      */
35095     isResizable : function(colIndex){
35096         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35097     },
35098     /**
35099      * Sets if a column is hidden.
35100      * @param {Number} colIndex The column index
35101      * @param {Boolean} hidden True if the column is hidden
35102      */
35103     setHidden : function(colIndex, hidden){
35104         this.config[colIndex].hidden = hidden;
35105         this.totalWidth = null;
35106         this.fireEvent("hiddenchange", this, colIndex, hidden);
35107     },
35108
35109     /**
35110      * Sets the editor for a column.
35111      * @param {Number} col The column index
35112      * @param {Object} editor The editor object
35113      */
35114     setEditor : function(col, editor){
35115         this.config[col].editor = editor;
35116     }
35117 });
35118
35119 Roo.grid.ColumnModel.defaultRenderer = function(value)
35120 {
35121     if(typeof value == "object") {
35122         return value;
35123     }
35124         if(typeof value == "string" && value.length < 1){
35125             return "&#160;";
35126         }
35127     
35128         return String.format("{0}", value);
35129 };
35130
35131 // Alias for backwards compatibility
35132 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35133 /*
35134  * Based on:
35135  * Ext JS Library 1.1.1
35136  * Copyright(c) 2006-2007, Ext JS, LLC.
35137  *
35138  * Originally Released Under LGPL - original licence link has changed is not relivant.
35139  *
35140  * Fork - LGPL
35141  * <script type="text/javascript">
35142  */
35143
35144 /**
35145  * @class Roo.grid.AbstractSelectionModel
35146  * @extends Roo.util.Observable
35147  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35148  * implemented by descendant classes.  This class should not be directly instantiated.
35149  * @constructor
35150  */
35151 Roo.grid.AbstractSelectionModel = function(){
35152     this.locked = false;
35153     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35154 };
35155
35156 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35157     /** @ignore Called by the grid automatically. Do not call directly. */
35158     init : function(grid){
35159         this.grid = grid;
35160         this.initEvents();
35161     },
35162
35163     /**
35164      * Locks the selections.
35165      */
35166     lock : function(){
35167         this.locked = true;
35168     },
35169
35170     /**
35171      * Unlocks the selections.
35172      */
35173     unlock : function(){
35174         this.locked = false;
35175     },
35176
35177     /**
35178      * Returns true if the selections are locked.
35179      * @return {Boolean}
35180      */
35181     isLocked : function(){
35182         return this.locked;
35183     }
35184 });/*
35185  * Based on:
35186  * Ext JS Library 1.1.1
35187  * Copyright(c) 2006-2007, Ext JS, LLC.
35188  *
35189  * Originally Released Under LGPL - original licence link has changed is not relivant.
35190  *
35191  * Fork - LGPL
35192  * <script type="text/javascript">
35193  */
35194 /**
35195  * @extends Roo.grid.AbstractSelectionModel
35196  * @class Roo.grid.RowSelectionModel
35197  * The default SelectionModel used by {@link Roo.grid.Grid}.
35198  * It supports multiple selections and keyboard selection/navigation. 
35199  * @constructor
35200  * @param {Object} config
35201  */
35202 Roo.grid.RowSelectionModel = function(config){
35203     Roo.apply(this, config);
35204     this.selections = new Roo.util.MixedCollection(false, function(o){
35205         return o.id;
35206     });
35207
35208     this.last = false;
35209     this.lastActive = false;
35210
35211     this.addEvents({
35212         /**
35213              * @event selectionchange
35214              * Fires when the selection changes
35215              * @param {SelectionModel} this
35216              */
35217             "selectionchange" : true,
35218         /**
35219              * @event afterselectionchange
35220              * Fires after the selection changes (eg. by key press or clicking)
35221              * @param {SelectionModel} this
35222              */
35223             "afterselectionchange" : true,
35224         /**
35225              * @event beforerowselect
35226              * Fires when a row is selected being selected, return false to cancel.
35227              * @param {SelectionModel} this
35228              * @param {Number} rowIndex The selected index
35229              * @param {Boolean} keepExisting False if other selections will be cleared
35230              */
35231             "beforerowselect" : true,
35232         /**
35233              * @event rowselect
35234              * Fires when a row is selected.
35235              * @param {SelectionModel} this
35236              * @param {Number} rowIndex The selected index
35237              * @param {Roo.data.Record} r The record
35238              */
35239             "rowselect" : true,
35240         /**
35241              * @event rowdeselect
35242              * Fires when a row is deselected.
35243              * @param {SelectionModel} this
35244              * @param {Number} rowIndex The selected index
35245              */
35246         "rowdeselect" : true
35247     });
35248     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35249     this.locked = false;
35250 };
35251
35252 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35253     /**
35254      * @cfg {Boolean} singleSelect
35255      * True to allow selection of only one row at a time (defaults to false)
35256      */
35257     singleSelect : false,
35258
35259     // private
35260     initEvents : function(){
35261
35262         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35263             this.grid.on("mousedown", this.handleMouseDown, this);
35264         }else{ // allow click to work like normal
35265             this.grid.on("rowclick", this.handleDragableRowClick, this);
35266         }
35267
35268         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35269             "up" : function(e){
35270                 if(!e.shiftKey){
35271                     this.selectPrevious(e.shiftKey);
35272                 }else if(this.last !== false && this.lastActive !== false){
35273                     var last = this.last;
35274                     this.selectRange(this.last,  this.lastActive-1);
35275                     this.grid.getView().focusRow(this.lastActive);
35276                     if(last !== false){
35277                         this.last = last;
35278                     }
35279                 }else{
35280                     this.selectFirstRow();
35281                 }
35282                 this.fireEvent("afterselectionchange", this);
35283             },
35284             "down" : function(e){
35285                 if(!e.shiftKey){
35286                     this.selectNext(e.shiftKey);
35287                 }else if(this.last !== false && this.lastActive !== false){
35288                     var last = this.last;
35289                     this.selectRange(this.last,  this.lastActive+1);
35290                     this.grid.getView().focusRow(this.lastActive);
35291                     if(last !== false){
35292                         this.last = last;
35293                     }
35294                 }else{
35295                     this.selectFirstRow();
35296                 }
35297                 this.fireEvent("afterselectionchange", this);
35298             },
35299             scope: this
35300         });
35301
35302         var view = this.grid.view;
35303         view.on("refresh", this.onRefresh, this);
35304         view.on("rowupdated", this.onRowUpdated, this);
35305         view.on("rowremoved", this.onRemove, this);
35306     },
35307
35308     // private
35309     onRefresh : function(){
35310         var ds = this.grid.dataSource, i, v = this.grid.view;
35311         var s = this.selections;
35312         s.each(function(r){
35313             if((i = ds.indexOfId(r.id)) != -1){
35314                 v.onRowSelect(i);
35315                 s.add(ds.getAt(i)); // updating the selection relate data
35316             }else{
35317                 s.remove(r);
35318             }
35319         });
35320     },
35321
35322     // private
35323     onRemove : function(v, index, r){
35324         this.selections.remove(r);
35325     },
35326
35327     // private
35328     onRowUpdated : function(v, index, r){
35329         if(this.isSelected(r)){
35330             v.onRowSelect(index);
35331         }
35332     },
35333
35334     /**
35335      * Select records.
35336      * @param {Array} records The records to select
35337      * @param {Boolean} keepExisting (optional) True to keep existing selections
35338      */
35339     selectRecords : function(records, keepExisting){
35340         if(!keepExisting){
35341             this.clearSelections();
35342         }
35343         var ds = this.grid.dataSource;
35344         for(var i = 0, len = records.length; i < len; i++){
35345             this.selectRow(ds.indexOf(records[i]), true);
35346         }
35347     },
35348
35349     /**
35350      * Gets the number of selected rows.
35351      * @return {Number}
35352      */
35353     getCount : function(){
35354         return this.selections.length;
35355     },
35356
35357     /**
35358      * Selects the first row in the grid.
35359      */
35360     selectFirstRow : function(){
35361         this.selectRow(0);
35362     },
35363
35364     /**
35365      * Select the last row.
35366      * @param {Boolean} keepExisting (optional) True to keep existing selections
35367      */
35368     selectLastRow : function(keepExisting){
35369         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35370     },
35371
35372     /**
35373      * Selects the row immediately following the last selected row.
35374      * @param {Boolean} keepExisting (optional) True to keep existing selections
35375      */
35376     selectNext : function(keepExisting){
35377         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35378             this.selectRow(this.last+1, keepExisting);
35379             this.grid.getView().focusRow(this.last);
35380         }
35381     },
35382
35383     /**
35384      * Selects the row that precedes the last selected row.
35385      * @param {Boolean} keepExisting (optional) True to keep existing selections
35386      */
35387     selectPrevious : function(keepExisting){
35388         if(this.last){
35389             this.selectRow(this.last-1, keepExisting);
35390             this.grid.getView().focusRow(this.last);
35391         }
35392     },
35393
35394     /**
35395      * Returns the selected records
35396      * @return {Array} Array of selected records
35397      */
35398     getSelections : function(){
35399         return [].concat(this.selections.items);
35400     },
35401
35402     /**
35403      * Returns the first selected record.
35404      * @return {Record}
35405      */
35406     getSelected : function(){
35407         return this.selections.itemAt(0);
35408     },
35409
35410
35411     /**
35412      * Clears all selections.
35413      */
35414     clearSelections : function(fast){
35415         if(this.locked) {
35416             return;
35417         }
35418         if(fast !== true){
35419             var ds = this.grid.dataSource;
35420             var s = this.selections;
35421             s.each(function(r){
35422                 this.deselectRow(ds.indexOfId(r.id));
35423             }, this);
35424             s.clear();
35425         }else{
35426             this.selections.clear();
35427         }
35428         this.last = false;
35429     },
35430
35431
35432     /**
35433      * Selects all rows.
35434      */
35435     selectAll : function(){
35436         if(this.locked) {
35437             return;
35438         }
35439         this.selections.clear();
35440         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35441             this.selectRow(i, true);
35442         }
35443     },
35444
35445     /**
35446      * Returns True if there is a selection.
35447      * @return {Boolean}
35448      */
35449     hasSelection : function(){
35450         return this.selections.length > 0;
35451     },
35452
35453     /**
35454      * Returns True if the specified row is selected.
35455      * @param {Number/Record} record The record or index of the record to check
35456      * @return {Boolean}
35457      */
35458     isSelected : function(index){
35459         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35460         return (r && this.selections.key(r.id) ? true : false);
35461     },
35462
35463     /**
35464      * Returns True if the specified record id is selected.
35465      * @param {String} id The id of record to check
35466      * @return {Boolean}
35467      */
35468     isIdSelected : function(id){
35469         return (this.selections.key(id) ? true : false);
35470     },
35471
35472     // private
35473     handleMouseDown : function(e, t){
35474         var view = this.grid.getView(), rowIndex;
35475         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35476             return;
35477         };
35478         if(e.shiftKey && this.last !== false){
35479             var last = this.last;
35480             this.selectRange(last, rowIndex, e.ctrlKey);
35481             this.last = last; // reset the last
35482             view.focusRow(rowIndex);
35483         }else{
35484             var isSelected = this.isSelected(rowIndex);
35485             if(e.button !== 0 && isSelected){
35486                 view.focusRow(rowIndex);
35487             }else if(e.ctrlKey && isSelected){
35488                 this.deselectRow(rowIndex);
35489             }else if(!isSelected){
35490                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35491                 view.focusRow(rowIndex);
35492             }
35493         }
35494         this.fireEvent("afterselectionchange", this);
35495     },
35496     // private
35497     handleDragableRowClick :  function(grid, rowIndex, e) 
35498     {
35499         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35500             this.selectRow(rowIndex, false);
35501             grid.view.focusRow(rowIndex);
35502              this.fireEvent("afterselectionchange", this);
35503         }
35504     },
35505     
35506     /**
35507      * Selects multiple rows.
35508      * @param {Array} rows Array of the indexes of the row to select
35509      * @param {Boolean} keepExisting (optional) True to keep existing selections
35510      */
35511     selectRows : function(rows, keepExisting){
35512         if(!keepExisting){
35513             this.clearSelections();
35514         }
35515         for(var i = 0, len = rows.length; i < len; i++){
35516             this.selectRow(rows[i], true);
35517         }
35518     },
35519
35520     /**
35521      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35522      * @param {Number} startRow The index of the first row in the range
35523      * @param {Number} endRow The index of the last row in the range
35524      * @param {Boolean} keepExisting (optional) True to retain existing selections
35525      */
35526     selectRange : function(startRow, endRow, keepExisting){
35527         if(this.locked) {
35528             return;
35529         }
35530         if(!keepExisting){
35531             this.clearSelections();
35532         }
35533         if(startRow <= endRow){
35534             for(var i = startRow; i <= endRow; i++){
35535                 this.selectRow(i, true);
35536             }
35537         }else{
35538             for(var i = startRow; i >= endRow; i--){
35539                 this.selectRow(i, true);
35540             }
35541         }
35542     },
35543
35544     /**
35545      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35546      * @param {Number} startRow The index of the first row in the range
35547      * @param {Number} endRow The index of the last row in the range
35548      */
35549     deselectRange : function(startRow, endRow, preventViewNotify){
35550         if(this.locked) {
35551             return;
35552         }
35553         for(var i = startRow; i <= endRow; i++){
35554             this.deselectRow(i, preventViewNotify);
35555         }
35556     },
35557
35558     /**
35559      * Selects a row.
35560      * @param {Number} row The index of the row to select
35561      * @param {Boolean} keepExisting (optional) True to keep existing selections
35562      */
35563     selectRow : function(index, keepExisting, preventViewNotify){
35564         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35565             return;
35566         }
35567         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35568             if(!keepExisting || this.singleSelect){
35569                 this.clearSelections();
35570             }
35571             var r = this.grid.dataSource.getAt(index);
35572             this.selections.add(r);
35573             this.last = this.lastActive = index;
35574             if(!preventViewNotify){
35575                 this.grid.getView().onRowSelect(index);
35576             }
35577             this.fireEvent("rowselect", this, index, r);
35578             this.fireEvent("selectionchange", this);
35579         }
35580     },
35581
35582     /**
35583      * Deselects a row.
35584      * @param {Number} row The index of the row to deselect
35585      */
35586     deselectRow : function(index, preventViewNotify){
35587         if(this.locked) {
35588             return;
35589         }
35590         if(this.last == index){
35591             this.last = false;
35592         }
35593         if(this.lastActive == index){
35594             this.lastActive = false;
35595         }
35596         var r = this.grid.dataSource.getAt(index);
35597         this.selections.remove(r);
35598         if(!preventViewNotify){
35599             this.grid.getView().onRowDeselect(index);
35600         }
35601         this.fireEvent("rowdeselect", this, index);
35602         this.fireEvent("selectionchange", this);
35603     },
35604
35605     // private
35606     restoreLast : function(){
35607         if(this._last){
35608             this.last = this._last;
35609         }
35610     },
35611
35612     // private
35613     acceptsNav : function(row, col, cm){
35614         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35615     },
35616
35617     // private
35618     onEditorKey : function(field, e){
35619         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35620         if(k == e.TAB){
35621             e.stopEvent();
35622             ed.completeEdit();
35623             if(e.shiftKey){
35624                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35625             }else{
35626                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35627             }
35628         }else if(k == e.ENTER && !e.ctrlKey){
35629             e.stopEvent();
35630             ed.completeEdit();
35631             if(e.shiftKey){
35632                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35633             }else{
35634                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35635             }
35636         }else if(k == e.ESC){
35637             ed.cancelEdit();
35638         }
35639         if(newCell){
35640             g.startEditing(newCell[0], newCell[1]);
35641         }
35642     }
35643 });/*
35644  * Based on:
35645  * Ext JS Library 1.1.1
35646  * Copyright(c) 2006-2007, Ext JS, LLC.
35647  *
35648  * Originally Released Under LGPL - original licence link has changed is not relivant.
35649  *
35650  * Fork - LGPL
35651  * <script type="text/javascript">
35652  */
35653 /**
35654  * @class Roo.grid.CellSelectionModel
35655  * @extends Roo.grid.AbstractSelectionModel
35656  * This class provides the basic implementation for cell selection in a grid.
35657  * @constructor
35658  * @param {Object} config The object containing the configuration of this model.
35659  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35660  */
35661 Roo.grid.CellSelectionModel = function(config){
35662     Roo.apply(this, config);
35663
35664     this.selection = null;
35665
35666     this.addEvents({
35667         /**
35668              * @event beforerowselect
35669              * Fires before a cell is selected.
35670              * @param {SelectionModel} this
35671              * @param {Number} rowIndex The selected row index
35672              * @param {Number} colIndex The selected cell index
35673              */
35674             "beforecellselect" : true,
35675         /**
35676              * @event cellselect
35677              * Fires when a cell is selected.
35678              * @param {SelectionModel} this
35679              * @param {Number} rowIndex The selected row index
35680              * @param {Number} colIndex The selected cell index
35681              */
35682             "cellselect" : true,
35683         /**
35684              * @event selectionchange
35685              * Fires when the active selection changes.
35686              * @param {SelectionModel} this
35687              * @param {Object} selection null for no selection or an object (o) with two properties
35688                 <ul>
35689                 <li>o.record: the record object for the row the selection is in</li>
35690                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35691                 </ul>
35692              */
35693             "selectionchange" : true,
35694         /**
35695              * @event tabend
35696              * Fires when the tab (or enter) was pressed on the last editable cell
35697              * You can use this to trigger add new row.
35698              * @param {SelectionModel} this
35699              */
35700             "tabend" : true,
35701          /**
35702              * @event beforeeditnext
35703              * Fires before the next editable sell is made active
35704              * You can use this to skip to another cell or fire the tabend
35705              *    if you set cell to false
35706              * @param {Object} eventdata object : { cell : [ row, col ] } 
35707              */
35708             "beforeeditnext" : true
35709     });
35710     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35711 };
35712
35713 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35714     
35715     enter_is_tab: false,
35716
35717     /** @ignore */
35718     initEvents : function(){
35719         this.grid.on("mousedown", this.handleMouseDown, this);
35720         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35721         var view = this.grid.view;
35722         view.on("refresh", this.onViewChange, this);
35723         view.on("rowupdated", this.onRowUpdated, this);
35724         view.on("beforerowremoved", this.clearSelections, this);
35725         view.on("beforerowsinserted", this.clearSelections, this);
35726         if(this.grid.isEditor){
35727             this.grid.on("beforeedit", this.beforeEdit,  this);
35728         }
35729     },
35730
35731         //private
35732     beforeEdit : function(e){
35733         this.select(e.row, e.column, false, true, e.record);
35734     },
35735
35736         //private
35737     onRowUpdated : function(v, index, r){
35738         if(this.selection && this.selection.record == r){
35739             v.onCellSelect(index, this.selection.cell[1]);
35740         }
35741     },
35742
35743         //private
35744     onViewChange : function(){
35745         this.clearSelections(true);
35746     },
35747
35748         /**
35749          * Returns the currently selected cell,.
35750          * @return {Array} The selected cell (row, column) or null if none selected.
35751          */
35752     getSelectedCell : function(){
35753         return this.selection ? this.selection.cell : null;
35754     },
35755
35756     /**
35757      * Clears all selections.
35758      * @param {Boolean} true to prevent the gridview from being notified about the change.
35759      */
35760     clearSelections : function(preventNotify){
35761         var s = this.selection;
35762         if(s){
35763             if(preventNotify !== true){
35764                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35765             }
35766             this.selection = null;
35767             this.fireEvent("selectionchange", this, null);
35768         }
35769     },
35770
35771     /**
35772      * Returns true if there is a selection.
35773      * @return {Boolean}
35774      */
35775     hasSelection : function(){
35776         return this.selection ? true : false;
35777     },
35778
35779     /** @ignore */
35780     handleMouseDown : function(e, t){
35781         var v = this.grid.getView();
35782         if(this.isLocked()){
35783             return;
35784         };
35785         var row = v.findRowIndex(t);
35786         var cell = v.findCellIndex(t);
35787         if(row !== false && cell !== false){
35788             this.select(row, cell);
35789         }
35790     },
35791
35792     /**
35793      * Selects a cell.
35794      * @param {Number} rowIndex
35795      * @param {Number} collIndex
35796      */
35797     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35798         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35799             this.clearSelections();
35800             r = r || this.grid.dataSource.getAt(rowIndex);
35801             this.selection = {
35802                 record : r,
35803                 cell : [rowIndex, colIndex]
35804             };
35805             if(!preventViewNotify){
35806                 var v = this.grid.getView();
35807                 v.onCellSelect(rowIndex, colIndex);
35808                 if(preventFocus !== true){
35809                     v.focusCell(rowIndex, colIndex);
35810                 }
35811             }
35812             this.fireEvent("cellselect", this, rowIndex, colIndex);
35813             this.fireEvent("selectionchange", this, this.selection);
35814         }
35815     },
35816
35817         //private
35818     isSelectable : function(rowIndex, colIndex, cm){
35819         return !cm.isHidden(colIndex);
35820     },
35821
35822     /** @ignore */
35823     handleKeyDown : function(e){
35824         //Roo.log('Cell Sel Model handleKeyDown');
35825         if(!e.isNavKeyPress()){
35826             return;
35827         }
35828         var g = this.grid, s = this.selection;
35829         if(!s){
35830             e.stopEvent();
35831             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35832             if(cell){
35833                 this.select(cell[0], cell[1]);
35834             }
35835             return;
35836         }
35837         var sm = this;
35838         var walk = function(row, col, step){
35839             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35840         };
35841         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35842         var newCell;
35843
35844       
35845
35846         switch(k){
35847             case e.TAB:
35848                 // handled by onEditorKey
35849                 if (g.isEditor && g.editing) {
35850                     return;
35851                 }
35852                 if(e.shiftKey) {
35853                     newCell = walk(r, c-1, -1);
35854                 } else {
35855                     newCell = walk(r, c+1, 1);
35856                 }
35857                 break;
35858             
35859             case e.DOWN:
35860                newCell = walk(r+1, c, 1);
35861                 break;
35862             
35863             case e.UP:
35864                 newCell = walk(r-1, c, -1);
35865                 break;
35866             
35867             case e.RIGHT:
35868                 newCell = walk(r, c+1, 1);
35869                 break;
35870             
35871             case e.LEFT:
35872                 newCell = walk(r, c-1, -1);
35873                 break;
35874             
35875             case e.ENTER:
35876                 
35877                 if(g.isEditor && !g.editing){
35878                    g.startEditing(r, c);
35879                    e.stopEvent();
35880                    return;
35881                 }
35882                 
35883                 
35884              break;
35885         };
35886         if(newCell){
35887             this.select(newCell[0], newCell[1]);
35888             e.stopEvent();
35889             
35890         }
35891     },
35892
35893     acceptsNav : function(row, col, cm){
35894         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35895     },
35896     /**
35897      * Selects a cell.
35898      * @param {Number} field (not used) - as it's normally used as a listener
35899      * @param {Number} e - event - fake it by using
35900      *
35901      * var e = Roo.EventObjectImpl.prototype;
35902      * e.keyCode = e.TAB
35903      *
35904      * 
35905      */
35906     onEditorKey : function(field, e){
35907         
35908         var k = e.getKey(),
35909             newCell,
35910             g = this.grid,
35911             ed = g.activeEditor,
35912             forward = false;
35913         ///Roo.log('onEditorKey' + k);
35914         
35915         
35916         if (this.enter_is_tab && k == e.ENTER) {
35917             k = e.TAB;
35918         }
35919         
35920         if(k == e.TAB){
35921             if(e.shiftKey){
35922                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35923             }else{
35924                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35925                 forward = true;
35926             }
35927             
35928             e.stopEvent();
35929             
35930         } else if(k == e.ENTER &&  !e.ctrlKey){
35931             ed.completeEdit();
35932             e.stopEvent();
35933             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35934         
35935                 } else if(k == e.ESC){
35936             ed.cancelEdit();
35937         }
35938                 
35939         if (newCell) {
35940             var ecall = { cell : newCell, forward : forward };
35941             this.fireEvent('beforeeditnext', ecall );
35942             newCell = ecall.cell;
35943                         forward = ecall.forward;
35944         }
35945                 
35946         if(newCell){
35947             //Roo.log('next cell after edit');
35948             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35949         } else if (forward) {
35950             // tabbed past last
35951             this.fireEvent.defer(100, this, ['tabend',this]);
35952         }
35953     }
35954 });/*
35955  * Based on:
35956  * Ext JS Library 1.1.1
35957  * Copyright(c) 2006-2007, Ext JS, LLC.
35958  *
35959  * Originally Released Under LGPL - original licence link has changed is not relivant.
35960  *
35961  * Fork - LGPL
35962  * <script type="text/javascript">
35963  */
35964  
35965 /**
35966  * @class Roo.grid.EditorGrid
35967  * @extends Roo.grid.Grid
35968  * Class for creating and editable grid.
35969  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35970  * The container MUST have some type of size defined for the grid to fill. The container will be 
35971  * automatically set to position relative if it isn't already.
35972  * @param {Object} dataSource The data model to bind to
35973  * @param {Object} colModel The column model with info about this grid's columns
35974  */
35975 Roo.grid.EditorGrid = function(container, config){
35976     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35977     this.getGridEl().addClass("xedit-grid");
35978
35979     if(!this.selModel){
35980         this.selModel = new Roo.grid.CellSelectionModel();
35981     }
35982
35983     this.activeEditor = null;
35984
35985         this.addEvents({
35986             /**
35987              * @event beforeedit
35988              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35989              * <ul style="padding:5px;padding-left:16px;">
35990              * <li>grid - This grid</li>
35991              * <li>record - The record being edited</li>
35992              * <li>field - The field name being edited</li>
35993              * <li>value - The value for the field being edited.</li>
35994              * <li>row - The grid row index</li>
35995              * <li>column - The grid column index</li>
35996              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35997              * </ul>
35998              * @param {Object} e An edit event (see above for description)
35999              */
36000             "beforeedit" : true,
36001             /**
36002              * @event afteredit
36003              * Fires after a cell is edited. <br />
36004              * <ul style="padding:5px;padding-left:16px;">
36005              * <li>grid - This grid</li>
36006              * <li>record - The record being edited</li>
36007              * <li>field - The field name being edited</li>
36008              * <li>value - The value being set</li>
36009              * <li>originalValue - The original value for the field, before the edit.</li>
36010              * <li>row - The grid row index</li>
36011              * <li>column - The grid column index</li>
36012              * </ul>
36013              * @param {Object} e An edit event (see above for description)
36014              */
36015             "afteredit" : true,
36016             /**
36017              * @event validateedit
36018              * Fires after a cell is edited, but before the value is set in the record. 
36019          * You can use this to modify the value being set in the field, Return false
36020              * to cancel the change. The edit event object has the following properties <br />
36021              * <ul style="padding:5px;padding-left:16px;">
36022          * <li>editor - This editor</li>
36023              * <li>grid - This grid</li>
36024              * <li>record - The record being edited</li>
36025              * <li>field - The field name being edited</li>
36026              * <li>value - The value being set</li>
36027              * <li>originalValue - The original value for the field, before the edit.</li>
36028              * <li>row - The grid row index</li>
36029              * <li>column - The grid column index</li>
36030              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36031              * </ul>
36032              * @param {Object} e An edit event (see above for description)
36033              */
36034             "validateedit" : true
36035         });
36036     this.on("bodyscroll", this.stopEditing,  this);
36037     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36038 };
36039
36040 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36041     /**
36042      * @cfg {Number} clicksToEdit
36043      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36044      */
36045     clicksToEdit: 2,
36046
36047     // private
36048     isEditor : true,
36049     // private
36050     trackMouseOver: false, // causes very odd FF errors
36051
36052     onCellDblClick : function(g, row, col){
36053         this.startEditing(row, col);
36054     },
36055
36056     onEditComplete : function(ed, value, startValue){
36057         this.editing = false;
36058         this.activeEditor = null;
36059         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36060         var r = ed.record;
36061         var field = this.colModel.getDataIndex(ed.col);
36062         var e = {
36063             grid: this,
36064             record: r,
36065             field: field,
36066             originalValue: startValue,
36067             value: value,
36068             row: ed.row,
36069             column: ed.col,
36070             cancel:false,
36071             editor: ed
36072         };
36073         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36074         cell.show();
36075           
36076         if(String(value) !== String(startValue)){
36077             
36078             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36079                 r.set(field, e.value);
36080                 // if we are dealing with a combo box..
36081                 // then we also set the 'name' colum to be the displayField
36082                 if (ed.field.displayField && ed.field.name) {
36083                     r.set(ed.field.name, ed.field.el.dom.value);
36084                 }
36085                 
36086                 delete e.cancel; //?? why!!!
36087                 this.fireEvent("afteredit", e);
36088             }
36089         } else {
36090             this.fireEvent("afteredit", e); // always fire it!
36091         }
36092         this.view.focusCell(ed.row, ed.col);
36093     },
36094
36095     /**
36096      * Starts editing the specified for the specified row/column
36097      * @param {Number} rowIndex
36098      * @param {Number} colIndex
36099      */
36100     startEditing : function(row, col){
36101         this.stopEditing();
36102         if(this.colModel.isCellEditable(col, row)){
36103             this.view.ensureVisible(row, col, true);
36104           
36105             var r = this.dataSource.getAt(row);
36106             var field = this.colModel.getDataIndex(col);
36107             var cell = Roo.get(this.view.getCell(row,col));
36108             var e = {
36109                 grid: this,
36110                 record: r,
36111                 field: field,
36112                 value: r.data[field],
36113                 row: row,
36114                 column: col,
36115                 cancel:false 
36116             };
36117             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36118                 this.editing = true;
36119                 var ed = this.colModel.getCellEditor(col, row);
36120                 
36121                 if (!ed) {
36122                     return;
36123                 }
36124                 if(!ed.rendered){
36125                     ed.render(ed.parentEl || document.body);
36126                 }
36127                 ed.field.reset();
36128                
36129                 cell.hide();
36130                 
36131                 (function(){ // complex but required for focus issues in safari, ie and opera
36132                     ed.row = row;
36133                     ed.col = col;
36134                     ed.record = r;
36135                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36136                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36137                     this.activeEditor = ed;
36138                     var v = r.data[field];
36139                     ed.startEdit(this.view.getCell(row, col), v);
36140                     // combo's with 'displayField and name set
36141                     if (ed.field.displayField && ed.field.name) {
36142                         ed.field.el.dom.value = r.data[ed.field.name];
36143                     }
36144                     
36145                     
36146                 }).defer(50, this);
36147             }
36148         }
36149     },
36150         
36151     /**
36152      * Stops any active editing
36153      */
36154     stopEditing : function(){
36155         if(this.activeEditor){
36156             this.activeEditor.completeEdit();
36157         }
36158         this.activeEditor = null;
36159     },
36160         
36161          /**
36162      * Called to get grid's drag proxy text, by default returns this.ddText.
36163      * @return {String}
36164      */
36165     getDragDropText : function(){
36166         var count = this.selModel.getSelectedCell() ? 1 : 0;
36167         return String.format(this.ddText, count, count == 1 ? '' : 's');
36168     }
36169         
36170 });/*
36171  * Based on:
36172  * Ext JS Library 1.1.1
36173  * Copyright(c) 2006-2007, Ext JS, LLC.
36174  *
36175  * Originally Released Under LGPL - original licence link has changed is not relivant.
36176  *
36177  * Fork - LGPL
36178  * <script type="text/javascript">
36179  */
36180
36181 // private - not really -- you end up using it !
36182 // This is a support class used internally by the Grid components
36183
36184 /**
36185  * @class Roo.grid.GridEditor
36186  * @extends Roo.Editor
36187  * Class for creating and editable grid elements.
36188  * @param {Object} config any settings (must include field)
36189  */
36190 Roo.grid.GridEditor = function(field, config){
36191     if (!config && field.field) {
36192         config = field;
36193         field = Roo.factory(config.field, Roo.form);
36194     }
36195     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36196     field.monitorTab = false;
36197 };
36198
36199 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36200     
36201     /**
36202      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36203      */
36204     
36205     alignment: "tl-tl",
36206     autoSize: "width",
36207     hideEl : false,
36208     cls: "x-small-editor x-grid-editor",
36209     shim:false,
36210     shadow:"frame"
36211 });/*
36212  * Based on:
36213  * Ext JS Library 1.1.1
36214  * Copyright(c) 2006-2007, Ext JS, LLC.
36215  *
36216  * Originally Released Under LGPL - original licence link has changed is not relivant.
36217  *
36218  * Fork - LGPL
36219  * <script type="text/javascript">
36220  */
36221   
36222
36223   
36224 Roo.grid.PropertyRecord = Roo.data.Record.create([
36225     {name:'name',type:'string'},  'value'
36226 ]);
36227
36228
36229 Roo.grid.PropertyStore = function(grid, source){
36230     this.grid = grid;
36231     this.store = new Roo.data.Store({
36232         recordType : Roo.grid.PropertyRecord
36233     });
36234     this.store.on('update', this.onUpdate,  this);
36235     if(source){
36236         this.setSource(source);
36237     }
36238     Roo.grid.PropertyStore.superclass.constructor.call(this);
36239 };
36240
36241
36242
36243 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36244     setSource : function(o){
36245         this.source = o;
36246         this.store.removeAll();
36247         var data = [];
36248         for(var k in o){
36249             if(this.isEditableValue(o[k])){
36250                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36251             }
36252         }
36253         this.store.loadRecords({records: data}, {}, true);
36254     },
36255
36256     onUpdate : function(ds, record, type){
36257         if(type == Roo.data.Record.EDIT){
36258             var v = record.data['value'];
36259             var oldValue = record.modified['value'];
36260             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36261                 this.source[record.id] = v;
36262                 record.commit();
36263                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36264             }else{
36265                 record.reject();
36266             }
36267         }
36268     },
36269
36270     getProperty : function(row){
36271        return this.store.getAt(row);
36272     },
36273
36274     isEditableValue: function(val){
36275         if(val && val instanceof Date){
36276             return true;
36277         }else if(typeof val == 'object' || typeof val == 'function'){
36278             return false;
36279         }
36280         return true;
36281     },
36282
36283     setValue : function(prop, value){
36284         this.source[prop] = value;
36285         this.store.getById(prop).set('value', value);
36286     },
36287
36288     getSource : function(){
36289         return this.source;
36290     }
36291 });
36292
36293 Roo.grid.PropertyColumnModel = function(grid, store){
36294     this.grid = grid;
36295     var g = Roo.grid;
36296     g.PropertyColumnModel.superclass.constructor.call(this, [
36297         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36298         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36299     ]);
36300     this.store = store;
36301     this.bselect = Roo.DomHelper.append(document.body, {
36302         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36303             {tag: 'option', value: 'true', html: 'true'},
36304             {tag: 'option', value: 'false', html: 'false'}
36305         ]
36306     });
36307     Roo.id(this.bselect);
36308     var f = Roo.form;
36309     this.editors = {
36310         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36311         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36312         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36313         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36314         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36315     };
36316     this.renderCellDelegate = this.renderCell.createDelegate(this);
36317     this.renderPropDelegate = this.renderProp.createDelegate(this);
36318 };
36319
36320 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36321     
36322     
36323     nameText : 'Name',
36324     valueText : 'Value',
36325     
36326     dateFormat : 'm/j/Y',
36327     
36328     
36329     renderDate : function(dateVal){
36330         return dateVal.dateFormat(this.dateFormat);
36331     },
36332
36333     renderBool : function(bVal){
36334         return bVal ? 'true' : 'false';
36335     },
36336
36337     isCellEditable : function(colIndex, rowIndex){
36338         return colIndex == 1;
36339     },
36340
36341     getRenderer : function(col){
36342         return col == 1 ?
36343             this.renderCellDelegate : this.renderPropDelegate;
36344     },
36345
36346     renderProp : function(v){
36347         return this.getPropertyName(v);
36348     },
36349
36350     renderCell : function(val){
36351         var rv = val;
36352         if(val instanceof Date){
36353             rv = this.renderDate(val);
36354         }else if(typeof val == 'boolean'){
36355             rv = this.renderBool(val);
36356         }
36357         return Roo.util.Format.htmlEncode(rv);
36358     },
36359
36360     getPropertyName : function(name){
36361         var pn = this.grid.propertyNames;
36362         return pn && pn[name] ? pn[name] : name;
36363     },
36364
36365     getCellEditor : function(colIndex, rowIndex){
36366         var p = this.store.getProperty(rowIndex);
36367         var n = p.data['name'], val = p.data['value'];
36368         
36369         if(typeof(this.grid.customEditors[n]) == 'string'){
36370             return this.editors[this.grid.customEditors[n]];
36371         }
36372         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36373             return this.grid.customEditors[n];
36374         }
36375         if(val instanceof Date){
36376             return this.editors['date'];
36377         }else if(typeof val == 'number'){
36378             return this.editors['number'];
36379         }else if(typeof val == 'boolean'){
36380             return this.editors['boolean'];
36381         }else{
36382             return this.editors['string'];
36383         }
36384     }
36385 });
36386
36387 /**
36388  * @class Roo.grid.PropertyGrid
36389  * @extends Roo.grid.EditorGrid
36390  * This class represents the  interface of a component based property grid control.
36391  * <br><br>Usage:<pre><code>
36392  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36393       
36394  });
36395  // set any options
36396  grid.render();
36397  * </code></pre>
36398   
36399  * @constructor
36400  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36401  * The container MUST have some type of size defined for the grid to fill. The container will be
36402  * automatically set to position relative if it isn't already.
36403  * @param {Object} config A config object that sets properties on this grid.
36404  */
36405 Roo.grid.PropertyGrid = function(container, config){
36406     config = config || {};
36407     var store = new Roo.grid.PropertyStore(this);
36408     this.store = store;
36409     var cm = new Roo.grid.PropertyColumnModel(this, store);
36410     store.store.sort('name', 'ASC');
36411     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36412         ds: store.store,
36413         cm: cm,
36414         enableColLock:false,
36415         enableColumnMove:false,
36416         stripeRows:false,
36417         trackMouseOver: false,
36418         clicksToEdit:1
36419     }, config));
36420     this.getGridEl().addClass('x-props-grid');
36421     this.lastEditRow = null;
36422     this.on('columnresize', this.onColumnResize, this);
36423     this.addEvents({
36424          /**
36425              * @event beforepropertychange
36426              * Fires before a property changes (return false to stop?)
36427              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36428              * @param {String} id Record Id
36429              * @param {String} newval New Value
36430          * @param {String} oldval Old Value
36431              */
36432         "beforepropertychange": true,
36433         /**
36434              * @event propertychange
36435              * Fires after a property changes
36436              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36437              * @param {String} id Record Id
36438              * @param {String} newval New Value
36439          * @param {String} oldval Old Value
36440              */
36441         "propertychange": true
36442     });
36443     this.customEditors = this.customEditors || {};
36444 };
36445 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36446     
36447      /**
36448      * @cfg {Object} customEditors map of colnames=> custom editors.
36449      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36450      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36451      * false disables editing of the field.
36452          */
36453     
36454       /**
36455      * @cfg {Object} propertyNames map of property Names to their displayed value
36456          */
36457     
36458     render : function(){
36459         Roo.grid.PropertyGrid.superclass.render.call(this);
36460         this.autoSize.defer(100, this);
36461     },
36462
36463     autoSize : function(){
36464         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36465         if(this.view){
36466             this.view.fitColumns();
36467         }
36468     },
36469
36470     onColumnResize : function(){
36471         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36472         this.autoSize();
36473     },
36474     /**
36475      * Sets the data for the Grid
36476      * accepts a Key => Value object of all the elements avaiable.
36477      * @param {Object} data  to appear in grid.
36478      */
36479     setSource : function(source){
36480         this.store.setSource(source);
36481         //this.autoSize();
36482     },
36483     /**
36484      * Gets all the data from the grid.
36485      * @return {Object} data  data stored in grid
36486      */
36487     getSource : function(){
36488         return this.store.getSource();
36489     }
36490 });/*
36491   
36492  * Licence LGPL
36493  
36494  */
36495  
36496 /**
36497  * @class Roo.grid.Calendar
36498  * @extends Roo.util.Grid
36499  * This class extends the Grid to provide a calendar widget
36500  * <br><br>Usage:<pre><code>
36501  var grid = new Roo.grid.Calendar("my-container-id", {
36502      ds: myDataStore,
36503      cm: myColModel,
36504      selModel: mySelectionModel,
36505      autoSizeColumns: true,
36506      monitorWindowResize: false,
36507      trackMouseOver: true
36508      eventstore : real data store..
36509  });
36510  // set any options
36511  grid.render();
36512   
36513   * @constructor
36514  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36515  * The container MUST have some type of size defined for the grid to fill. The container will be
36516  * automatically set to position relative if it isn't already.
36517  * @param {Object} config A config object that sets properties on this grid.
36518  */
36519 Roo.grid.Calendar = function(container, config){
36520         // initialize the container
36521         this.container = Roo.get(container);
36522         this.container.update("");
36523         this.container.setStyle("overflow", "hidden");
36524     this.container.addClass('x-grid-container');
36525
36526     this.id = this.container.id;
36527
36528     Roo.apply(this, config);
36529     // check and correct shorthanded configs
36530     
36531     var rows = [];
36532     var d =1;
36533     for (var r = 0;r < 6;r++) {
36534         
36535         rows[r]=[];
36536         for (var c =0;c < 7;c++) {
36537             rows[r][c]= '';
36538         }
36539     }
36540     if (this.eventStore) {
36541         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36542         this.eventStore.on('load',this.onLoad, this);
36543         this.eventStore.on('beforeload',this.clearEvents, this);
36544          
36545     }
36546     
36547     this.dataSource = new Roo.data.Store({
36548             proxy: new Roo.data.MemoryProxy(rows),
36549             reader: new Roo.data.ArrayReader({}, [
36550                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36551     });
36552
36553     this.dataSource.load();
36554     this.ds = this.dataSource;
36555     this.ds.xmodule = this.xmodule || false;
36556     
36557     
36558     var cellRender = function(v,x,r)
36559     {
36560         return String.format(
36561             '<div class="fc-day  fc-widget-content"><div>' +
36562                 '<div class="fc-event-container"></div>' +
36563                 '<div class="fc-day-number">{0}</div>'+
36564                 
36565                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36566             '</div></div>', v);
36567     
36568     }
36569     
36570     
36571     this.colModel = new Roo.grid.ColumnModel( [
36572         {
36573             xtype: 'ColumnModel',
36574             xns: Roo.grid,
36575             dataIndex : 'weekday0',
36576             header : 'Sunday',
36577             renderer : cellRender
36578         },
36579         {
36580             xtype: 'ColumnModel',
36581             xns: Roo.grid,
36582             dataIndex : 'weekday1',
36583             header : 'Monday',
36584             renderer : cellRender
36585         },
36586         {
36587             xtype: 'ColumnModel',
36588             xns: Roo.grid,
36589             dataIndex : 'weekday2',
36590             header : 'Tuesday',
36591             renderer : cellRender
36592         },
36593         {
36594             xtype: 'ColumnModel',
36595             xns: Roo.grid,
36596             dataIndex : 'weekday3',
36597             header : 'Wednesday',
36598             renderer : cellRender
36599         },
36600         {
36601             xtype: 'ColumnModel',
36602             xns: Roo.grid,
36603             dataIndex : 'weekday4',
36604             header : 'Thursday',
36605             renderer : cellRender
36606         },
36607         {
36608             xtype: 'ColumnModel',
36609             xns: Roo.grid,
36610             dataIndex : 'weekday5',
36611             header : 'Friday',
36612             renderer : cellRender
36613         },
36614         {
36615             xtype: 'ColumnModel',
36616             xns: Roo.grid,
36617             dataIndex : 'weekday6',
36618             header : 'Saturday',
36619             renderer : cellRender
36620         }
36621     ]);
36622     this.cm = this.colModel;
36623     this.cm.xmodule = this.xmodule || false;
36624  
36625         
36626           
36627     //this.selModel = new Roo.grid.CellSelectionModel();
36628     //this.sm = this.selModel;
36629     //this.selModel.init(this);
36630     
36631     
36632     if(this.width){
36633         this.container.setWidth(this.width);
36634     }
36635
36636     if(this.height){
36637         this.container.setHeight(this.height);
36638     }
36639     /** @private */
36640         this.addEvents({
36641         // raw events
36642         /**
36643          * @event click
36644          * The raw click event for the entire grid.
36645          * @param {Roo.EventObject} e
36646          */
36647         "click" : true,
36648         /**
36649          * @event dblclick
36650          * The raw dblclick event for the entire grid.
36651          * @param {Roo.EventObject} e
36652          */
36653         "dblclick" : true,
36654         /**
36655          * @event contextmenu
36656          * The raw contextmenu event for the entire grid.
36657          * @param {Roo.EventObject} e
36658          */
36659         "contextmenu" : true,
36660         /**
36661          * @event mousedown
36662          * The raw mousedown event for the entire grid.
36663          * @param {Roo.EventObject} e
36664          */
36665         "mousedown" : true,
36666         /**
36667          * @event mouseup
36668          * The raw mouseup event for the entire grid.
36669          * @param {Roo.EventObject} e
36670          */
36671         "mouseup" : true,
36672         /**
36673          * @event mouseover
36674          * The raw mouseover event for the entire grid.
36675          * @param {Roo.EventObject} e
36676          */
36677         "mouseover" : true,
36678         /**
36679          * @event mouseout
36680          * The raw mouseout event for the entire grid.
36681          * @param {Roo.EventObject} e
36682          */
36683         "mouseout" : true,
36684         /**
36685          * @event keypress
36686          * The raw keypress event for the entire grid.
36687          * @param {Roo.EventObject} e
36688          */
36689         "keypress" : true,
36690         /**
36691          * @event keydown
36692          * The raw keydown event for the entire grid.
36693          * @param {Roo.EventObject} e
36694          */
36695         "keydown" : true,
36696
36697         // custom events
36698
36699         /**
36700          * @event cellclick
36701          * Fires when a cell is clicked
36702          * @param {Grid} this
36703          * @param {Number} rowIndex
36704          * @param {Number} columnIndex
36705          * @param {Roo.EventObject} e
36706          */
36707         "cellclick" : true,
36708         /**
36709          * @event celldblclick
36710          * Fires when a cell is double clicked
36711          * @param {Grid} this
36712          * @param {Number} rowIndex
36713          * @param {Number} columnIndex
36714          * @param {Roo.EventObject} e
36715          */
36716         "celldblclick" : true,
36717         /**
36718          * @event rowclick
36719          * Fires when a row is clicked
36720          * @param {Grid} this
36721          * @param {Number} rowIndex
36722          * @param {Roo.EventObject} e
36723          */
36724         "rowclick" : true,
36725         /**
36726          * @event rowdblclick
36727          * Fires when a row is double clicked
36728          * @param {Grid} this
36729          * @param {Number} rowIndex
36730          * @param {Roo.EventObject} e
36731          */
36732         "rowdblclick" : true,
36733         /**
36734          * @event headerclick
36735          * Fires when a header is clicked
36736          * @param {Grid} this
36737          * @param {Number} columnIndex
36738          * @param {Roo.EventObject} e
36739          */
36740         "headerclick" : true,
36741         /**
36742          * @event headerdblclick
36743          * Fires when a header cell is double clicked
36744          * @param {Grid} this
36745          * @param {Number} columnIndex
36746          * @param {Roo.EventObject} e
36747          */
36748         "headerdblclick" : true,
36749         /**
36750          * @event rowcontextmenu
36751          * Fires when a row is right clicked
36752          * @param {Grid} this
36753          * @param {Number} rowIndex
36754          * @param {Roo.EventObject} e
36755          */
36756         "rowcontextmenu" : true,
36757         /**
36758          * @event cellcontextmenu
36759          * Fires when a cell is right clicked
36760          * @param {Grid} this
36761          * @param {Number} rowIndex
36762          * @param {Number} cellIndex
36763          * @param {Roo.EventObject} e
36764          */
36765          "cellcontextmenu" : true,
36766         /**
36767          * @event headercontextmenu
36768          * Fires when a header is right clicked
36769          * @param {Grid} this
36770          * @param {Number} columnIndex
36771          * @param {Roo.EventObject} e
36772          */
36773         "headercontextmenu" : true,
36774         /**
36775          * @event bodyscroll
36776          * Fires when the body element is scrolled
36777          * @param {Number} scrollLeft
36778          * @param {Number} scrollTop
36779          */
36780         "bodyscroll" : true,
36781         /**
36782          * @event columnresize
36783          * Fires when the user resizes a column
36784          * @param {Number} columnIndex
36785          * @param {Number} newSize
36786          */
36787         "columnresize" : true,
36788         /**
36789          * @event columnmove
36790          * Fires when the user moves a column
36791          * @param {Number} oldIndex
36792          * @param {Number} newIndex
36793          */
36794         "columnmove" : true,
36795         /**
36796          * @event startdrag
36797          * Fires when row(s) start being dragged
36798          * @param {Grid} this
36799          * @param {Roo.GridDD} dd The drag drop object
36800          * @param {event} e The raw browser event
36801          */
36802         "startdrag" : true,
36803         /**
36804          * @event enddrag
36805          * Fires when a drag operation is complete
36806          * @param {Grid} this
36807          * @param {Roo.GridDD} dd The drag drop object
36808          * @param {event} e The raw browser event
36809          */
36810         "enddrag" : true,
36811         /**
36812          * @event dragdrop
36813          * Fires when dragged row(s) are dropped on a valid DD target
36814          * @param {Grid} this
36815          * @param {Roo.GridDD} dd The drag drop object
36816          * @param {String} targetId The target drag drop object
36817          * @param {event} e The raw browser event
36818          */
36819         "dragdrop" : true,
36820         /**
36821          * @event dragover
36822          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36823          * @param {Grid} this
36824          * @param {Roo.GridDD} dd The drag drop object
36825          * @param {String} targetId The target drag drop object
36826          * @param {event} e The raw browser event
36827          */
36828         "dragover" : true,
36829         /**
36830          * @event dragenter
36831          *  Fires when the dragged row(s) first cross another DD target while being dragged
36832          * @param {Grid} this
36833          * @param {Roo.GridDD} dd The drag drop object
36834          * @param {String} targetId The target drag drop object
36835          * @param {event} e The raw browser event
36836          */
36837         "dragenter" : true,
36838         /**
36839          * @event dragout
36840          * Fires when the dragged row(s) leave another DD target while being dragged
36841          * @param {Grid} this
36842          * @param {Roo.GridDD} dd The drag drop object
36843          * @param {String} targetId The target drag drop object
36844          * @param {event} e The raw browser event
36845          */
36846         "dragout" : true,
36847         /**
36848          * @event rowclass
36849          * Fires when a row is rendered, so you can change add a style to it.
36850          * @param {GridView} gridview   The grid view
36851          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36852          */
36853         'rowclass' : true,
36854
36855         /**
36856          * @event render
36857          * Fires when the grid is rendered
36858          * @param {Grid} grid
36859          */
36860         'render' : true,
36861             /**
36862              * @event select
36863              * Fires when a date is selected
36864              * @param {DatePicker} this
36865              * @param {Date} date The selected date
36866              */
36867         'select': true,
36868         /**
36869              * @event monthchange
36870              * Fires when the displayed month changes 
36871              * @param {DatePicker} this
36872              * @param {Date} date The selected month
36873              */
36874         'monthchange': true,
36875         /**
36876              * @event evententer
36877              * Fires when mouse over an event
36878              * @param {Calendar} this
36879              * @param {event} Event
36880              */
36881         'evententer': true,
36882         /**
36883              * @event eventleave
36884              * Fires when the mouse leaves an
36885              * @param {Calendar} this
36886              * @param {event}
36887              */
36888         'eventleave': true,
36889         /**
36890              * @event eventclick
36891              * Fires when the mouse click an
36892              * @param {Calendar} this
36893              * @param {event}
36894              */
36895         'eventclick': true,
36896         /**
36897              * @event eventrender
36898              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36899              * @param {Calendar} this
36900              * @param {data} data to be modified
36901              */
36902         'eventrender': true
36903         
36904     });
36905
36906     Roo.grid.Grid.superclass.constructor.call(this);
36907     this.on('render', function() {
36908         this.view.el.addClass('x-grid-cal'); 
36909         
36910         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36911
36912     },this);
36913     
36914     if (!Roo.grid.Calendar.style) {
36915         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36916             
36917             
36918             '.x-grid-cal .x-grid-col' :  {
36919                 height: 'auto !important',
36920                 'vertical-align': 'top'
36921             },
36922             '.x-grid-cal  .fc-event-hori' : {
36923                 height: '14px'
36924             }
36925              
36926             
36927         }, Roo.id());
36928     }
36929
36930     
36931     
36932 };
36933 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36934     /**
36935      * @cfg {Store} eventStore The store that loads events.
36936      */
36937     eventStore : 25,
36938
36939      
36940     activeDate : false,
36941     startDay : 0,
36942     autoWidth : true,
36943     monitorWindowResize : false,
36944
36945     
36946     resizeColumns : function() {
36947         var col = (this.view.el.getWidth() / 7) - 3;
36948         // loop through cols, and setWidth
36949         for(var i =0 ; i < 7 ; i++){
36950             this.cm.setColumnWidth(i, col);
36951         }
36952     },
36953      setDate :function(date) {
36954         
36955         Roo.log('setDate?');
36956         
36957         this.resizeColumns();
36958         var vd = this.activeDate;
36959         this.activeDate = date;
36960 //        if(vd && this.el){
36961 //            var t = date.getTime();
36962 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36963 //                Roo.log('using add remove');
36964 //                
36965 //                this.fireEvent('monthchange', this, date);
36966 //                
36967 //                this.cells.removeClass("fc-state-highlight");
36968 //                this.cells.each(function(c){
36969 //                   if(c.dateValue == t){
36970 //                       c.addClass("fc-state-highlight");
36971 //                       setTimeout(function(){
36972 //                            try{c.dom.firstChild.focus();}catch(e){}
36973 //                       }, 50);
36974 //                       return false;
36975 //                   }
36976 //                   return true;
36977 //                });
36978 //                return;
36979 //            }
36980 //        }
36981         
36982         var days = date.getDaysInMonth();
36983         
36984         var firstOfMonth = date.getFirstDateOfMonth();
36985         var startingPos = firstOfMonth.getDay()-this.startDay;
36986         
36987         if(startingPos < this.startDay){
36988             startingPos += 7;
36989         }
36990         
36991         var pm = date.add(Date.MONTH, -1);
36992         var prevStart = pm.getDaysInMonth()-startingPos;
36993 //        
36994         
36995         
36996         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
36997         
36998         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
36999         //this.cells.addClassOnOver('fc-state-hover');
37000         
37001         var cells = this.cells.elements;
37002         var textEls = this.textNodes;
37003         
37004         //Roo.each(cells, function(cell){
37005         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37006         //});
37007         
37008         days += startingPos;
37009
37010         // convert everything to numbers so it's fast
37011         var day = 86400000;
37012         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37013         //Roo.log(d);
37014         //Roo.log(pm);
37015         //Roo.log(prevStart);
37016         
37017         var today = new Date().clearTime().getTime();
37018         var sel = date.clearTime().getTime();
37019         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37020         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37021         var ddMatch = this.disabledDatesRE;
37022         var ddText = this.disabledDatesText;
37023         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37024         var ddaysText = this.disabledDaysText;
37025         var format = this.format;
37026         
37027         var setCellClass = function(cal, cell){
37028             
37029             //Roo.log('set Cell Class');
37030             cell.title = "";
37031             var t = d.getTime();
37032             
37033             //Roo.log(d);
37034             
37035             
37036             cell.dateValue = t;
37037             if(t == today){
37038                 cell.className += " fc-today";
37039                 cell.className += " fc-state-highlight";
37040                 cell.title = cal.todayText;
37041             }
37042             if(t == sel){
37043                 // disable highlight in other month..
37044                 cell.className += " fc-state-highlight";
37045                 
37046             }
37047             // disabling
37048             if(t < min) {
37049                 //cell.className = " fc-state-disabled";
37050                 cell.title = cal.minText;
37051                 return;
37052             }
37053             if(t > max) {
37054                 //cell.className = " fc-state-disabled";
37055                 cell.title = cal.maxText;
37056                 return;
37057             }
37058             if(ddays){
37059                 if(ddays.indexOf(d.getDay()) != -1){
37060                     // cell.title = ddaysText;
37061                    // cell.className = " fc-state-disabled";
37062                 }
37063             }
37064             if(ddMatch && format){
37065                 var fvalue = d.dateFormat(format);
37066                 if(ddMatch.test(fvalue)){
37067                     cell.title = ddText.replace("%0", fvalue);
37068                    cell.className = " fc-state-disabled";
37069                 }
37070             }
37071             
37072             if (!cell.initialClassName) {
37073                 cell.initialClassName = cell.dom.className;
37074             }
37075             
37076             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37077         };
37078
37079         var i = 0;
37080         
37081         for(; i < startingPos; i++) {
37082             cells[i].dayName =  (++prevStart);
37083             Roo.log(textEls[i]);
37084             d.setDate(d.getDate()+1);
37085             
37086             //cells[i].className = "fc-past fc-other-month";
37087             setCellClass(this, cells[i]);
37088         }
37089         
37090         var intDay = 0;
37091         
37092         for(; i < days; i++){
37093             intDay = i - startingPos + 1;
37094             cells[i].dayName =  (intDay);
37095             d.setDate(d.getDate()+1);
37096             
37097             cells[i].className = ''; // "x-date-active";
37098             setCellClass(this, cells[i]);
37099         }
37100         var extraDays = 0;
37101         
37102         for(; i < 42; i++) {
37103             //textEls[i].innerHTML = (++extraDays);
37104             
37105             d.setDate(d.getDate()+1);
37106             cells[i].dayName = (++extraDays);
37107             cells[i].className = "fc-future fc-other-month";
37108             setCellClass(this, cells[i]);
37109         }
37110         
37111         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37112         
37113         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37114         
37115         // this will cause all the cells to mis
37116         var rows= [];
37117         var i =0;
37118         for (var r = 0;r < 6;r++) {
37119             for (var c =0;c < 7;c++) {
37120                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37121             }    
37122         }
37123         
37124         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37125         for(i=0;i<cells.length;i++) {
37126             
37127             this.cells.elements[i].dayName = cells[i].dayName ;
37128             this.cells.elements[i].className = cells[i].className;
37129             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37130             this.cells.elements[i].title = cells[i].title ;
37131             this.cells.elements[i].dateValue = cells[i].dateValue ;
37132         }
37133         
37134         
37135         
37136         
37137         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37138         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37139         
37140         ////if(totalRows != 6){
37141             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37142            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37143        // }
37144         
37145         this.fireEvent('monthchange', this, date);
37146         
37147         
37148     },
37149  /**
37150      * Returns the grid's SelectionModel.
37151      * @return {SelectionModel}
37152      */
37153     getSelectionModel : function(){
37154         if(!this.selModel){
37155             this.selModel = new Roo.grid.CellSelectionModel();
37156         }
37157         return this.selModel;
37158     },
37159
37160     load: function() {
37161         this.eventStore.load()
37162         
37163         
37164         
37165     },
37166     
37167     findCell : function(dt) {
37168         dt = dt.clearTime().getTime();
37169         var ret = false;
37170         this.cells.each(function(c){
37171             //Roo.log("check " +c.dateValue + '?=' + dt);
37172             if(c.dateValue == dt){
37173                 ret = c;
37174                 return false;
37175             }
37176             return true;
37177         });
37178         
37179         return ret;
37180     },
37181     
37182     findCells : function(rec) {
37183         var s = rec.data.start_dt.clone().clearTime().getTime();
37184        // Roo.log(s);
37185         var e= rec.data.end_dt.clone().clearTime().getTime();
37186        // Roo.log(e);
37187         var ret = [];
37188         this.cells.each(function(c){
37189              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37190             
37191             if(c.dateValue > e){
37192                 return ;
37193             }
37194             if(c.dateValue < s){
37195                 return ;
37196             }
37197             ret.push(c);
37198         });
37199         
37200         return ret;    
37201     },
37202     
37203     findBestRow: function(cells)
37204     {
37205         var ret = 0;
37206         
37207         for (var i =0 ; i < cells.length;i++) {
37208             ret  = Math.max(cells[i].rows || 0,ret);
37209         }
37210         return ret;
37211         
37212     },
37213     
37214     
37215     addItem : function(rec)
37216     {
37217         // look for vertical location slot in
37218         var cells = this.findCells(rec);
37219         
37220         rec.row = this.findBestRow(cells);
37221         
37222         // work out the location.
37223         
37224         var crow = false;
37225         var rows = [];
37226         for(var i =0; i < cells.length; i++) {
37227             if (!crow) {
37228                 crow = {
37229                     start : cells[i],
37230                     end :  cells[i]
37231                 };
37232                 continue;
37233             }
37234             if (crow.start.getY() == cells[i].getY()) {
37235                 // on same row.
37236                 crow.end = cells[i];
37237                 continue;
37238             }
37239             // different row.
37240             rows.push(crow);
37241             crow = {
37242                 start: cells[i],
37243                 end : cells[i]
37244             };
37245             
37246         }
37247         
37248         rows.push(crow);
37249         rec.els = [];
37250         rec.rows = rows;
37251         rec.cells = cells;
37252         for (var i = 0; i < cells.length;i++) {
37253             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37254             
37255         }
37256         
37257         
37258     },
37259     
37260     clearEvents: function() {
37261         
37262         if (!this.eventStore.getCount()) {
37263             return;
37264         }
37265         // reset number of rows in cells.
37266         Roo.each(this.cells.elements, function(c){
37267             c.rows = 0;
37268         });
37269         
37270         this.eventStore.each(function(e) {
37271             this.clearEvent(e);
37272         },this);
37273         
37274     },
37275     
37276     clearEvent : function(ev)
37277     {
37278         if (ev.els) {
37279             Roo.each(ev.els, function(el) {
37280                 el.un('mouseenter' ,this.onEventEnter, this);
37281                 el.un('mouseleave' ,this.onEventLeave, this);
37282                 el.remove();
37283             },this);
37284             ev.els = [];
37285         }
37286     },
37287     
37288     
37289     renderEvent : function(ev,ctr) {
37290         if (!ctr) {
37291              ctr = this.view.el.select('.fc-event-container',true).first();
37292         }
37293         
37294          
37295         this.clearEvent(ev);
37296             //code
37297        
37298         
37299         
37300         ev.els = [];
37301         var cells = ev.cells;
37302         var rows = ev.rows;
37303         this.fireEvent('eventrender', this, ev);
37304         
37305         for(var i =0; i < rows.length; i++) {
37306             
37307             cls = '';
37308             if (i == 0) {
37309                 cls += ' fc-event-start';
37310             }
37311             if ((i+1) == rows.length) {
37312                 cls += ' fc-event-end';
37313             }
37314             
37315             //Roo.log(ev.data);
37316             // how many rows should it span..
37317             var cg = this.eventTmpl.append(ctr,Roo.apply({
37318                 fccls : cls
37319                 
37320             }, ev.data) , true);
37321             
37322             
37323             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37324             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37325             cg.on('click', this.onEventClick, this, ev);
37326             
37327             ev.els.push(cg);
37328             
37329             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37330             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37331             //Roo.log(cg);
37332              
37333             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37334             cg.setWidth(ebox.right - sbox.x -2);
37335         }
37336     },
37337     
37338     renderEvents: function()
37339     {   
37340         // first make sure there is enough space..
37341         
37342         if (!this.eventTmpl) {
37343             this.eventTmpl = new Roo.Template(
37344                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37345                     '<div class="fc-event-inner">' +
37346                         '<span class="fc-event-time">{time}</span>' +
37347                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37348                     '</div>' +
37349                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37350                 '</div>'
37351             );
37352                 
37353         }
37354                
37355         
37356         
37357         this.cells.each(function(c) {
37358             //Roo.log(c.select('.fc-day-content div',true).first());
37359             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37360         });
37361         
37362         var ctr = this.view.el.select('.fc-event-container',true).first();
37363         
37364         var cls;
37365         this.eventStore.each(function(ev){
37366             
37367             this.renderEvent(ev);
37368              
37369              
37370         }, this);
37371         this.view.layout();
37372         
37373     },
37374     
37375     onEventEnter: function (e, el,event,d) {
37376         this.fireEvent('evententer', this, el, event);
37377     },
37378     
37379     onEventLeave: function (e, el,event,d) {
37380         this.fireEvent('eventleave', this, el, event);
37381     },
37382     
37383     onEventClick: function (e, el,event,d) {
37384         this.fireEvent('eventclick', this, el, event);
37385     },
37386     
37387     onMonthChange: function () {
37388         this.store.load();
37389     },
37390     
37391     onLoad: function () {
37392         
37393         //Roo.log('calendar onload');
37394 //         
37395         if(this.eventStore.getCount() > 0){
37396             
37397            
37398             
37399             this.eventStore.each(function(d){
37400                 
37401                 
37402                 // FIXME..
37403                 var add =   d.data;
37404                 if (typeof(add.end_dt) == 'undefined')  {
37405                     Roo.log("Missing End time in calendar data: ");
37406                     Roo.log(d);
37407                     return;
37408                 }
37409                 if (typeof(add.start_dt) == 'undefined')  {
37410                     Roo.log("Missing Start time in calendar data: ");
37411                     Roo.log(d);
37412                     return;
37413                 }
37414                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37415                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37416                 add.id = add.id || d.id;
37417                 add.title = add.title || '??';
37418                 
37419                 this.addItem(d);
37420                 
37421              
37422             },this);
37423         }
37424         
37425         this.renderEvents();
37426     }
37427     
37428
37429 });
37430 /*
37431  grid : {
37432                 xtype: 'Grid',
37433                 xns: Roo.grid,
37434                 listeners : {
37435                     render : function ()
37436                     {
37437                         _this.grid = this;
37438                         
37439                         if (!this.view.el.hasClass('course-timesheet')) {
37440                             this.view.el.addClass('course-timesheet');
37441                         }
37442                         if (this.tsStyle) {
37443                             this.ds.load({});
37444                             return; 
37445                         }
37446                         Roo.log('width');
37447                         Roo.log(_this.grid.view.el.getWidth());
37448                         
37449                         
37450                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37451                             '.course-timesheet .x-grid-row' : {
37452                                 height: '80px'
37453                             },
37454                             '.x-grid-row td' : {
37455                                 'vertical-align' : 0
37456                             },
37457                             '.course-edit-link' : {
37458                                 'color' : 'blue',
37459                                 'text-overflow' : 'ellipsis',
37460                                 'overflow' : 'hidden',
37461                                 'white-space' : 'nowrap',
37462                                 'cursor' : 'pointer'
37463                             },
37464                             '.sub-link' : {
37465                                 'color' : 'green'
37466                             },
37467                             '.de-act-sup-link' : {
37468                                 'color' : 'purple',
37469                                 'text-decoration' : 'line-through'
37470                             },
37471                             '.de-act-link' : {
37472                                 'color' : 'red',
37473                                 'text-decoration' : 'line-through'
37474                             },
37475                             '.course-timesheet .course-highlight' : {
37476                                 'border-top-style': 'dashed !important',
37477                                 'border-bottom-bottom': 'dashed !important'
37478                             },
37479                             '.course-timesheet .course-item' : {
37480                                 'font-family'   : 'tahoma, arial, helvetica',
37481                                 'font-size'     : '11px',
37482                                 'overflow'      : 'hidden',
37483                                 'padding-left'  : '10px',
37484                                 'padding-right' : '10px',
37485                                 'padding-top' : '10px' 
37486                             }
37487                             
37488                         }, Roo.id());
37489                                 this.ds.load({});
37490                     }
37491                 },
37492                 autoWidth : true,
37493                 monitorWindowResize : false,
37494                 cellrenderer : function(v,x,r)
37495                 {
37496                     return v;
37497                 },
37498                 sm : {
37499                     xtype: 'CellSelectionModel',
37500                     xns: Roo.grid
37501                 },
37502                 dataSource : {
37503                     xtype: 'Store',
37504                     xns: Roo.data,
37505                     listeners : {
37506                         beforeload : function (_self, options)
37507                         {
37508                             options.params = options.params || {};
37509                             options.params._month = _this.monthField.getValue();
37510                             options.params.limit = 9999;
37511                             options.params['sort'] = 'when_dt';    
37512                             options.params['dir'] = 'ASC';    
37513                             this.proxy.loadResponse = this.loadResponse;
37514                             Roo.log("load?");
37515                             //this.addColumns();
37516                         },
37517                         load : function (_self, records, options)
37518                         {
37519                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37520                                 // if you click on the translation.. you can edit it...
37521                                 var el = Roo.get(this);
37522                                 var id = el.dom.getAttribute('data-id');
37523                                 var d = el.dom.getAttribute('data-date');
37524                                 var t = el.dom.getAttribute('data-time');
37525                                 //var id = this.child('span').dom.textContent;
37526                                 
37527                                 //Roo.log(this);
37528                                 Pman.Dialog.CourseCalendar.show({
37529                                     id : id,
37530                                     when_d : d,
37531                                     when_t : t,
37532                                     productitem_active : id ? 1 : 0
37533                                 }, function() {
37534                                     _this.grid.ds.load({});
37535                                 });
37536                            
37537                            });
37538                            
37539                            _this.panel.fireEvent('resize', [ '', '' ]);
37540                         }
37541                     },
37542                     loadResponse : function(o, success, response){
37543                             // this is overridden on before load..
37544                             
37545                             Roo.log("our code?");       
37546                             //Roo.log(success);
37547                             //Roo.log(response)
37548                             delete this.activeRequest;
37549                             if(!success){
37550                                 this.fireEvent("loadexception", this, o, response);
37551                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37552                                 return;
37553                             }
37554                             var result;
37555                             try {
37556                                 result = o.reader.read(response);
37557                             }catch(e){
37558                                 Roo.log("load exception?");
37559                                 this.fireEvent("loadexception", this, o, response, e);
37560                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37561                                 return;
37562                             }
37563                             Roo.log("ready...");        
37564                             // loop through result.records;
37565                             // and set this.tdate[date] = [] << array of records..
37566                             _this.tdata  = {};
37567                             Roo.each(result.records, function(r){
37568                                 //Roo.log(r.data);
37569                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37570                                     _this.tdata[r.data.when_dt.format('j')] = [];
37571                                 }
37572                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37573                             });
37574                             
37575                             //Roo.log(_this.tdata);
37576                             
37577                             result.records = [];
37578                             result.totalRecords = 6;
37579                     
37580                             // let's generate some duumy records for the rows.
37581                             //var st = _this.dateField.getValue();
37582                             
37583                             // work out monday..
37584                             //st = st.add(Date.DAY, -1 * st.format('w'));
37585                             
37586                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37587                             
37588                             var firstOfMonth = date.getFirstDayOfMonth();
37589                             var days = date.getDaysInMonth();
37590                             var d = 1;
37591                             var firstAdded = false;
37592                             for (var i = 0; i < result.totalRecords ; i++) {
37593                                 //var d= st.add(Date.DAY, i);
37594                                 var row = {};
37595                                 var added = 0;
37596                                 for(var w = 0 ; w < 7 ; w++){
37597                                     if(!firstAdded && firstOfMonth != w){
37598                                         continue;
37599                                     }
37600                                     if(d > days){
37601                                         continue;
37602                                     }
37603                                     firstAdded = true;
37604                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37605                                     row['weekday'+w] = String.format(
37606                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37607                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37608                                                     d,
37609                                                     date.format('Y-m-')+dd
37610                                                 );
37611                                     added++;
37612                                     if(typeof(_this.tdata[d]) != 'undefined'){
37613                                         Roo.each(_this.tdata[d], function(r){
37614                                             var is_sub = '';
37615                                             var deactive = '';
37616                                             var id = r.id;
37617                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37618                                             if(r.parent_id*1>0){
37619                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37620                                                 id = r.parent_id;
37621                                             }
37622                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37623                                                 deactive = 'de-act-link';
37624                                             }
37625                                             
37626                                             row['weekday'+w] += String.format(
37627                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37628                                                     id, //0
37629                                                     r.product_id_name, //1
37630                                                     r.when_dt.format('h:ia'), //2
37631                                                     is_sub, //3
37632                                                     deactive, //4
37633                                                     desc // 5
37634                                             );
37635                                         });
37636                                     }
37637                                     d++;
37638                                 }
37639                                 
37640                                 // only do this if something added..
37641                                 if(added > 0){ 
37642                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37643                                 }
37644                                 
37645                                 
37646                                 // push it twice. (second one with an hour..
37647                                 
37648                             }
37649                             //Roo.log(result);
37650                             this.fireEvent("load", this, o, o.request.arg);
37651                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37652                         },
37653                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37654                     proxy : {
37655                         xtype: 'HttpProxy',
37656                         xns: Roo.data,
37657                         method : 'GET',
37658                         url : baseURL + '/Roo/Shop_course.php'
37659                     },
37660                     reader : {
37661                         xtype: 'JsonReader',
37662                         xns: Roo.data,
37663                         id : 'id',
37664                         fields : [
37665                             {
37666                                 'name': 'id',
37667                                 'type': 'int'
37668                             },
37669                             {
37670                                 'name': 'when_dt',
37671                                 'type': 'string'
37672                             },
37673                             {
37674                                 'name': 'end_dt',
37675                                 'type': 'string'
37676                             },
37677                             {
37678                                 'name': 'parent_id',
37679                                 'type': 'int'
37680                             },
37681                             {
37682                                 'name': 'product_id',
37683                                 'type': 'int'
37684                             },
37685                             {
37686                                 'name': 'productitem_id',
37687                                 'type': 'int'
37688                             },
37689                             {
37690                                 'name': 'guid',
37691                                 'type': 'int'
37692                             }
37693                         ]
37694                     }
37695                 },
37696                 toolbar : {
37697                     xtype: 'Toolbar',
37698                     xns: Roo,
37699                     items : [
37700                         {
37701                             xtype: 'Button',
37702                             xns: Roo.Toolbar,
37703                             listeners : {
37704                                 click : function (_self, e)
37705                                 {
37706                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37707                                     sd.setMonth(sd.getMonth()-1);
37708                                     _this.monthField.setValue(sd.format('Y-m-d'));
37709                                     _this.grid.ds.load({});
37710                                 }
37711                             },
37712                             text : "Back"
37713                         },
37714                         {
37715                             xtype: 'Separator',
37716                             xns: Roo.Toolbar
37717                         },
37718                         {
37719                             xtype: 'MonthField',
37720                             xns: Roo.form,
37721                             listeners : {
37722                                 render : function (_self)
37723                                 {
37724                                     _this.monthField = _self;
37725                                    // _this.monthField.set  today
37726                                 },
37727                                 select : function (combo, date)
37728                                 {
37729                                     _this.grid.ds.load({});
37730                                 }
37731                             },
37732                             value : (function() { return new Date(); })()
37733                         },
37734                         {
37735                             xtype: 'Separator',
37736                             xns: Roo.Toolbar
37737                         },
37738                         {
37739                             xtype: 'TextItem',
37740                             xns: Roo.Toolbar,
37741                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37742                         },
37743                         {
37744                             xtype: 'Fill',
37745                             xns: Roo.Toolbar
37746                         },
37747                         {
37748                             xtype: 'Button',
37749                             xns: Roo.Toolbar,
37750                             listeners : {
37751                                 click : function (_self, e)
37752                                 {
37753                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37754                                     sd.setMonth(sd.getMonth()+1);
37755                                     _this.monthField.setValue(sd.format('Y-m-d'));
37756                                     _this.grid.ds.load({});
37757                                 }
37758                             },
37759                             text : "Next"
37760                         }
37761                     ]
37762                 },
37763                  
37764             }
37765         };
37766         
37767         *//*
37768  * Based on:
37769  * Ext JS Library 1.1.1
37770  * Copyright(c) 2006-2007, Ext JS, LLC.
37771  *
37772  * Originally Released Under LGPL - original licence link has changed is not relivant.
37773  *
37774  * Fork - LGPL
37775  * <script type="text/javascript">
37776  */
37777  
37778 /**
37779  * @class Roo.LoadMask
37780  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37781  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37782  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37783  * element's UpdateManager load indicator and will be destroyed after the initial load.
37784  * @constructor
37785  * Create a new LoadMask
37786  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37787  * @param {Object} config The config object
37788  */
37789 Roo.LoadMask = function(el, config){
37790     this.el = Roo.get(el);
37791     Roo.apply(this, config);
37792     if(this.store){
37793         this.store.on('beforeload', this.onBeforeLoad, this);
37794         this.store.on('load', this.onLoad, this);
37795         this.store.on('loadexception', this.onLoadException, this);
37796         this.removeMask = false;
37797     }else{
37798         var um = this.el.getUpdateManager();
37799         um.showLoadIndicator = false; // disable the default indicator
37800         um.on('beforeupdate', this.onBeforeLoad, this);
37801         um.on('update', this.onLoad, this);
37802         um.on('failure', this.onLoad, this);
37803         this.removeMask = true;
37804     }
37805 };
37806
37807 Roo.LoadMask.prototype = {
37808     /**
37809      * @cfg {Boolean} removeMask
37810      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37811      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37812      */
37813     /**
37814      * @cfg {String} msg
37815      * The text to display in a centered loading message box (defaults to 'Loading...')
37816      */
37817     msg : 'Loading...',
37818     /**
37819      * @cfg {String} msgCls
37820      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37821      */
37822     msgCls : 'x-mask-loading',
37823
37824     /**
37825      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37826      * @type Boolean
37827      */
37828     disabled: false,
37829
37830     /**
37831      * Disables the mask to prevent it from being displayed
37832      */
37833     disable : function(){
37834        this.disabled = true;
37835     },
37836
37837     /**
37838      * Enables the mask so that it can be displayed
37839      */
37840     enable : function(){
37841         this.disabled = false;
37842     },
37843     
37844     onLoadException : function()
37845     {
37846         Roo.log(arguments);
37847         
37848         if (typeof(arguments[3]) != 'undefined') {
37849             Roo.MessageBox.alert("Error loading",arguments[3]);
37850         } 
37851         /*
37852         try {
37853             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37854                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37855             }   
37856         } catch(e) {
37857             
37858         }
37859         */
37860     
37861         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37862     },
37863     // private
37864     onLoad : function()
37865     {
37866         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37867     },
37868
37869     // private
37870     onBeforeLoad : function(){
37871         if(!this.disabled){
37872             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37873         }
37874     },
37875
37876     // private
37877     destroy : function(){
37878         if(this.store){
37879             this.store.un('beforeload', this.onBeforeLoad, this);
37880             this.store.un('load', this.onLoad, this);
37881             this.store.un('loadexception', this.onLoadException, this);
37882         }else{
37883             var um = this.el.getUpdateManager();
37884             um.un('beforeupdate', this.onBeforeLoad, this);
37885             um.un('update', this.onLoad, this);
37886             um.un('failure', this.onLoad, this);
37887         }
37888     }
37889 };/*
37890  * Based on:
37891  * Ext JS Library 1.1.1
37892  * Copyright(c) 2006-2007, Ext JS, LLC.
37893  *
37894  * Originally Released Under LGPL - original licence link has changed is not relivant.
37895  *
37896  * Fork - LGPL
37897  * <script type="text/javascript">
37898  */
37899
37900
37901 /**
37902  * @class Roo.XTemplate
37903  * @extends Roo.Template
37904  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37905 <pre><code>
37906 var t = new Roo.XTemplate(
37907         '&lt;select name="{name}"&gt;',
37908                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37909         '&lt;/select&gt;'
37910 );
37911  
37912 // then append, applying the master template values
37913  </code></pre>
37914  *
37915  * Supported features:
37916  *
37917  *  Tags:
37918
37919 <pre><code>
37920       {a_variable} - output encoded.
37921       {a_variable.format:("Y-m-d")} - call a method on the variable
37922       {a_variable:raw} - unencoded output
37923       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37924       {a_variable:this.method_on_template(...)} - call a method on the template object.
37925  
37926 </code></pre>
37927  *  The tpl tag:
37928 <pre><code>
37929         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37930         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37931         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37932         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37933   
37934         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37935         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37936 </code></pre>
37937  *      
37938  */
37939 Roo.XTemplate = function()
37940 {
37941     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37942     if (this.html) {
37943         this.compile();
37944     }
37945 };
37946
37947
37948 Roo.extend(Roo.XTemplate, Roo.Template, {
37949
37950     /**
37951      * The various sub templates
37952      */
37953     tpls : false,
37954     /**
37955      *
37956      * basic tag replacing syntax
37957      * WORD:WORD()
37958      *
37959      * // you can fake an object call by doing this
37960      *  x.t:(test,tesT) 
37961      * 
37962      */
37963     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37964
37965     /**
37966      * compile the template
37967      *
37968      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37969      *
37970      */
37971     compile: function()
37972     {
37973         var s = this.html;
37974      
37975         s = ['<tpl>', s, '</tpl>'].join('');
37976     
37977         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
37978             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
37979             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
37980             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
37981             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
37982             m,
37983             id     = 0,
37984             tpls   = [];
37985     
37986         while(true == !!(m = s.match(re))){
37987             var forMatch   = m[0].match(nameRe),
37988                 ifMatch   = m[0].match(ifRe),
37989                 execMatch   = m[0].match(execRe),
37990                 namedMatch   = m[0].match(namedRe),
37991                 
37992                 exp  = null, 
37993                 fn   = null,
37994                 exec = null,
37995                 name = forMatch && forMatch[1] ? forMatch[1] : '';
37996                 
37997             if (ifMatch) {
37998                 // if - puts fn into test..
37999                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38000                 if(exp){
38001                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38002                 }
38003             }
38004             
38005             if (execMatch) {
38006                 // exec - calls a function... returns empty if true is  returned.
38007                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38008                 if(exp){
38009                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38010                 }
38011             }
38012             
38013             
38014             if (name) {
38015                 // for = 
38016                 switch(name){
38017                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38018                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38019                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38020                 }
38021             }
38022             var uid = namedMatch ? namedMatch[1] : id;
38023             
38024             
38025             tpls.push({
38026                 id:     namedMatch ? namedMatch[1] : id,
38027                 target: name,
38028                 exec:   exec,
38029                 test:   fn,
38030                 body:   m[1] || ''
38031             });
38032             if (namedMatch) {
38033                 s = s.replace(m[0], '');
38034             } else { 
38035                 s = s.replace(m[0], '{xtpl'+ id + '}');
38036             }
38037             ++id;
38038         }
38039         this.tpls = [];
38040         for(var i = tpls.length-1; i >= 0; --i){
38041             this.compileTpl(tpls[i]);
38042             this.tpls[tpls[i].id] = tpls[i];
38043         }
38044         this.master = tpls[tpls.length-1];
38045         return this;
38046     },
38047     /**
38048      * same as applyTemplate, except it's done to one of the subTemplates
38049      * when using named templates, you can do:
38050      *
38051      * var str = pl.applySubTemplate('your-name', values);
38052      *
38053      * 
38054      * @param {Number} id of the template
38055      * @param {Object} values to apply to template
38056      * @param {Object} parent (normaly the instance of this object)
38057      */
38058     applySubTemplate : function(id, values, parent)
38059     {
38060         
38061         
38062         var t = this.tpls[id];
38063         
38064         
38065         try { 
38066             if(t.test && !t.test.call(this, values, parent)){
38067                 return '';
38068             }
38069         } catch(e) {
38070             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38071             Roo.log(e.toString());
38072             Roo.log(t.test);
38073             return ''
38074         }
38075         try { 
38076             
38077             if(t.exec && t.exec.call(this, values, parent)){
38078                 return '';
38079             }
38080         } catch(e) {
38081             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38082             Roo.log(e.toString());
38083             Roo.log(t.exec);
38084             return ''
38085         }
38086         try {
38087             var vs = t.target ? t.target.call(this, values, parent) : values;
38088             parent = t.target ? values : parent;
38089             if(t.target && vs instanceof Array){
38090                 var buf = [];
38091                 for(var i = 0, len = vs.length; i < len; i++){
38092                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38093                 }
38094                 return buf.join('');
38095             }
38096             return t.compiled.call(this, vs, parent);
38097         } catch (e) {
38098             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38099             Roo.log(e.toString());
38100             Roo.log(t.compiled);
38101             return '';
38102         }
38103     },
38104
38105     compileTpl : function(tpl)
38106     {
38107         var fm = Roo.util.Format;
38108         var useF = this.disableFormats !== true;
38109         var sep = Roo.isGecko ? "+" : ",";
38110         var undef = function(str) {
38111             Roo.log("Property not found :"  + str);
38112             return '';
38113         };
38114         
38115         var fn = function(m, name, format, args)
38116         {
38117             //Roo.log(arguments);
38118             args = args ? args.replace(/\\'/g,"'") : args;
38119             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38120             if (typeof(format) == 'undefined') {
38121                 format= 'htmlEncode';
38122             }
38123             if (format == 'raw' ) {
38124                 format = false;
38125             }
38126             
38127             if(name.substr(0, 4) == 'xtpl'){
38128                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38129             }
38130             
38131             // build an array of options to determine if value is undefined..
38132             
38133             // basically get 'xxxx.yyyy' then do
38134             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38135             //    (function () { Roo.log("Property not found"); return ''; })() :
38136             //    ......
38137             
38138             var udef_ar = [];
38139             var lookfor = '';
38140             Roo.each(name.split('.'), function(st) {
38141                 lookfor += (lookfor.length ? '.': '') + st;
38142                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38143             });
38144             
38145             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38146             
38147             
38148             if(format && useF){
38149                 
38150                 args = args ? ',' + args : "";
38151                  
38152                 if(format.substr(0, 5) != "this."){
38153                     format = "fm." + format + '(';
38154                 }else{
38155                     format = 'this.call("'+ format.substr(5) + '", ';
38156                     args = ", values";
38157                 }
38158                 
38159                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38160             }
38161              
38162             if (args.length) {
38163                 // called with xxyx.yuu:(test,test)
38164                 // change to ()
38165                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38166             }
38167             // raw.. - :raw modifier..
38168             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38169             
38170         };
38171         var body;
38172         // branched to use + in gecko and [].join() in others
38173         if(Roo.isGecko){
38174             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38175                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38176                     "';};};";
38177         }else{
38178             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38179             body.push(tpl.body.replace(/(\r\n|\n)/g,
38180                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38181             body.push("'].join('');};};");
38182             body = body.join('');
38183         }
38184         
38185         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38186        
38187         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38188         eval(body);
38189         
38190         return this;
38191     },
38192
38193     applyTemplate : function(values){
38194         return this.master.compiled.call(this, values, {});
38195         //var s = this.subs;
38196     },
38197
38198     apply : function(){
38199         return this.applyTemplate.apply(this, arguments);
38200     }
38201
38202  });
38203
38204 Roo.XTemplate.from = function(el){
38205     el = Roo.getDom(el);
38206     return new Roo.XTemplate(el.value || el.innerHTML);
38207 };