roojs-core.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570         if(this.pruneModifiedRecords){
571             this.modified.remove(record);
572         }
573         this.fireEvent("remove", this, record, index);
574     },
575
576     /**
577      * Remove all Records from the Store and fires the clear event.
578      */
579     removeAll : function(){
580         this.data.clear();
581         if(this.pruneModifiedRecords){
582             this.modified = [];
583         }
584         this.fireEvent("clear", this);
585     },
586
587     /**
588      * Inserts Records to the Store at the given index and fires the add event.
589      * @param {Number} index The start index at which to insert the passed Records.
590      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
591      */
592     insert : function(index, records){
593         records = [].concat(records);
594         for(var i = 0, len = records.length; i < len; i++){
595             this.data.insert(index, records[i]);
596             records[i].join(this);
597         }
598         this.fireEvent("add", this, records, index);
599     },
600
601     /**
602      * Get the index within the cache of the passed Record.
603      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
604      * @return {Number} The index of the passed Record. Returns -1 if not found.
605      */
606     indexOf : function(record){
607         return this.data.indexOf(record);
608     },
609
610     /**
611      * Get the index within the cache of the Record with the passed id.
612      * @param {String} id The id of the Record to find.
613      * @return {Number} The index of the Record. Returns -1 if not found.
614      */
615     indexOfId : function(id){
616         return this.data.indexOfKey(id);
617     },
618
619     /**
620      * Get the Record with the specified id.
621      * @param {String} id The id of the Record to find.
622      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
623      */
624     getById : function(id){
625         return this.data.key(id);
626     },
627
628     /**
629      * Get the Record at the specified index.
630      * @param {Number} index The index of the Record to find.
631      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
632      */
633     getAt : function(index){
634         return this.data.itemAt(index);
635     },
636
637     /**
638      * Returns a range of Records between specified indices.
639      * @param {Number} startIndex (optional) The starting index (defaults to 0)
640      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
641      * @return {Roo.data.Record[]} An array of Records
642      */
643     getRange : function(start, end){
644         return this.data.getRange(start, end);
645     },
646
647     // private
648     storeOptions : function(o){
649         o = Roo.apply({}, o);
650         delete o.callback;
651         delete o.scope;
652         this.lastOptions = o;
653     },
654
655     /**
656      * Loads the Record cache from the configured Proxy using the configured Reader.
657      * <p>
658      * If using remote paging, then the first load call must specify the <em>start</em>
659      * and <em>limit</em> properties in the options.params property to establish the initial
660      * position within the dataset, and the number of Records to cache on each read from the Proxy.
661      * <p>
662      * <strong>It is important to note that for remote data sources, loading is asynchronous,
663      * and this call will return before the new data has been loaded. Perform any post-processing
664      * in a callback function, or in a "load" event handler.</strong>
665      * <p>
666      * @param {Object} options An object containing properties which control loading options:<ul>
667      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
668      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
669      * passed the following arguments:<ul>
670      * <li>r : Roo.data.Record[]</li>
671      * <li>options: Options object from the load call</li>
672      * <li>success: Boolean success indicator</li></ul></li>
673      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
674      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
675      * </ul>
676      */
677     load : function(options){
678         options = options || {};
679         if(this.fireEvent("beforeload", this, options) !== false){
680             this.storeOptions(options);
681             var p = Roo.apply(options.params || {}, this.baseParams);
682             // if meta was not loaded from remote source.. try requesting it.
683             if (!this.reader.metaFromRemote) {
684                 p._requestMeta = 1;
685             }
686             if(this.sortInfo && this.remoteSort){
687                 var pn = this.paramNames;
688                 p[pn["sort"]] = this.sortInfo.field;
689                 p[pn["dir"]] = this.sortInfo.direction;
690             }
691             if (this.multiSort) {
692                 var pn = this.paramNames;
693                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
694             }
695             
696             this.proxy.load(p, this.reader, this.loadRecords, this, options);
697         }
698     },
699
700     /**
701      * Reloads the Record cache from the configured Proxy using the configured Reader and
702      * the options from the last load operation performed.
703      * @param {Object} options (optional) An object containing properties which may override the options
704      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
705      * the most recently used options are reused).
706      */
707     reload : function(options){
708         this.load(Roo.applyIf(options||{}, this.lastOptions));
709     },
710
711     // private
712     // Called as a callback by the Reader during a load operation.
713     loadRecords : function(o, options, success){
714         if(!o || success === false){
715             if(success !== false){
716                 this.fireEvent("load", this, [], options, o);
717             }
718             if(options.callback){
719                 options.callback.call(options.scope || this, [], options, false);
720             }
721             return;
722         }
723         // if data returned failure - throw an exception.
724         if (o.success === false) {
725             // show a message if no listener is registered.
726             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
727                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
728             }
729             // loadmask wil be hooked into this..
730             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
731             return;
732         }
733         var r = o.records, t = o.totalRecords || r.length;
734         
735         this.fireEvent("beforeloadadd", this, r, options, o);
736         
737         if(!options || options.add !== true){
738             if(this.pruneModifiedRecords){
739                 this.modified = [];
740             }
741             for(var i = 0, len = r.length; i < len; i++){
742                 r[i].join(this);
743             }
744             if(this.snapshot){
745                 this.data = this.snapshot;
746                 delete this.snapshot;
747             }
748             this.data.clear();
749             this.data.addAll(r);
750             this.totalLength = t;
751             this.applySort();
752             this.fireEvent("datachanged", this);
753         }else{
754             this.totalLength = Math.max(t, this.data.length+r.length);
755             this.add(r);
756         }
757         
758         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
759                 
760             var e = new Roo.data.Record({});
761
762             e.set(this.parent.displayField, this.parent.emptyTitle);
763             e.set(this.parent.valueField, '');
764
765             this.insert(0, e);
766         }
767             
768         this.fireEvent("load", this, r, options, o);
769         if(options.callback){
770             options.callback.call(options.scope || this, r, options, true);
771         }
772     },
773
774
775     /**
776      * Loads data from a passed data block. A Reader which understands the format of the data
777      * must have been configured in the constructor.
778      * @param {Object} data The data block from which to read the Records.  The format of the data expected
779      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
780      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
781      */
782     loadData : function(o, append){
783         var r = this.reader.readRecords(o);
784         this.loadRecords(r, {add: append}, true);
785     },
786
787     /**
788      * Gets the number of cached records.
789      * <p>
790      * <em>If using paging, this may not be the total size of the dataset. If the data object
791      * used by the Reader contains the dataset size, then the getTotalCount() function returns
792      * the data set size</em>
793      */
794     getCount : function(){
795         return this.data.length || 0;
796     },
797
798     /**
799      * Gets the total number of records in the dataset as returned by the server.
800      * <p>
801      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
802      * the dataset size</em>
803      */
804     getTotalCount : function(){
805         return this.totalLength || 0;
806     },
807
808     /**
809      * Returns the sort state of the Store as an object with two properties:
810      * <pre><code>
811  field {String} The name of the field by which the Records are sorted
812  direction {String} The sort order, "ASC" or "DESC"
813      * </code></pre>
814      */
815     getSortState : function(){
816         return this.sortInfo;
817     },
818
819     // private
820     applySort : function(){
821         if(this.sortInfo && !this.remoteSort){
822             var s = this.sortInfo, f = s.field;
823             var st = this.fields.get(f).sortType;
824             var fn = function(r1, r2){
825                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
826                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
827             };
828             this.data.sort(s.direction, fn);
829             if(this.snapshot && this.snapshot != this.data){
830                 this.snapshot.sort(s.direction, fn);
831             }
832         }
833     },
834
835     /**
836      * Sets the default sort column and order to be used by the next load operation.
837      * @param {String} fieldName The name of the field to sort by.
838      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
839      */
840     setDefaultSort : function(field, dir){
841         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
842     },
843
844     /**
845      * Sort the Records.
846      * If remote sorting is used, the sort is performed on the server, and the cache is
847      * reloaded. If local sorting is used, the cache is sorted internally.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     sort : function(fieldName, dir){
852         var f = this.fields.get(fieldName);
853         if(!dir){
854             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
855             
856             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
857                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
858             }else{
859                 dir = f.sortDir;
860             }
861         }
862         this.sortToggle[f.name] = dir;
863         this.sortInfo = {field: f.name, direction: dir};
864         if(!this.remoteSort){
865             this.applySort();
866             this.fireEvent("datachanged", this);
867         }else{
868             this.load(this.lastOptions);
869         }
870     },
871
872     /**
873      * Calls the specified function for each of the Records in the cache.
874      * @param {Function} fn The function to call. The Record is passed as the first parameter.
875      * Returning <em>false</em> aborts and exits the iteration.
876      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
877      */
878     each : function(fn, scope){
879         this.data.each(fn, scope);
880     },
881
882     /**
883      * Gets all records modified since the last commit.  Modified records are persisted across load operations
884      * (e.g., during paging).
885      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
886      */
887     getModifiedRecords : function(){
888         return this.modified;
889     },
890
891     // private
892     createFilterFn : function(property, value, anyMatch){
893         if(!value.exec){ // not a regex
894             value = String(value);
895             if(value.length == 0){
896                 return false;
897             }
898             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
899         }
900         return function(r){
901             return value.test(r.data[property]);
902         };
903     },
904
905     /**
906      * Sums the value of <i>property</i> for each record between start and end and returns the result.
907      * @param {String} property A field on your records
908      * @param {Number} start The record index to start at (defaults to 0)
909      * @param {Number} end The last record index to include (defaults to length - 1)
910      * @return {Number} The sum
911      */
912     sum : function(property, start, end){
913         var rs = this.data.items, v = 0;
914         start = start || 0;
915         end = (end || end === 0) ? end : rs.length-1;
916
917         for(var i = start; i <= end; i++){
918             v += (rs[i].data[property] || 0);
919         }
920         return v;
921     },
922
923     /**
924      * Filter the records by a specified property.
925      * @param {String} field A field on your records
926      * @param {String/RegExp} value Either a string that the field
927      * should start with or a RegExp to test against the field
928      * @param {Boolean} anyMatch True to match any part not just the beginning
929      */
930     filter : function(property, value, anyMatch){
931         var fn = this.createFilterFn(property, value, anyMatch);
932         return fn ? this.filterBy(fn) : this.clearFilter();
933     },
934
935     /**
936      * Filter by a function. The specified function will be called with each
937      * record in this data source. If the function returns true the record is included,
938      * otherwise it is filtered.
939      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
940      * @param {Object} scope (optional) The scope of the function (defaults to this)
941      */
942     filterBy : function(fn, scope){
943         this.snapshot = this.snapshot || this.data;
944         this.data = this.queryBy(fn, scope||this);
945         this.fireEvent("datachanged", this);
946     },
947
948     /**
949      * Query the records by a specified property.
950      * @param {String} field A field on your records
951      * @param {String/RegExp} value Either a string that the field
952      * should start with or a RegExp to test against the field
953      * @param {Boolean} anyMatch True to match any part not just the beginning
954      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
955      */
956     query : function(property, value, anyMatch){
957         var fn = this.createFilterFn(property, value, anyMatch);
958         return fn ? this.queryBy(fn) : this.data.clone();
959     },
960
961     /**
962      * Query by a function. The specified function will be called with each
963      * record in this data source. If the function returns true the record is included
964      * in the results.
965      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
966      * @param {Object} scope (optional) The scope of the function (defaults to this)
967       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
968      **/
969     queryBy : function(fn, scope){
970         var data = this.snapshot || this.data;
971         return data.filterBy(fn, scope||this);
972     },
973
974     /**
975      * Collects unique values for a particular dataIndex from this store.
976      * @param {String} dataIndex The property to collect
977      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
978      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
979      * @return {Array} An array of the unique values
980      **/
981     collect : function(dataIndex, allowNull, bypassFilter){
982         var d = (bypassFilter === true && this.snapshot) ?
983                 this.snapshot.items : this.data.items;
984         var v, sv, r = [], l = {};
985         for(var i = 0, len = d.length; i < len; i++){
986             v = d[i].data[dataIndex];
987             sv = String(v);
988             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
989                 l[sv] = true;
990                 r[r.length] = v;
991             }
992         }
993         return r;
994     },
995
996     /**
997      * Revert to a view of the Record cache with no filtering applied.
998      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
999      */
1000     clearFilter : function(suppressEvent){
1001         if(this.snapshot && this.snapshot != this.data){
1002             this.data = this.snapshot;
1003             delete this.snapshot;
1004             if(suppressEvent !== true){
1005                 this.fireEvent("datachanged", this);
1006             }
1007         }
1008     },
1009
1010     // private
1011     afterEdit : function(record){
1012         if(this.modified.indexOf(record) == -1){
1013             this.modified.push(record);
1014         }
1015         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1016     },
1017     
1018     // private
1019     afterReject : function(record){
1020         this.modified.remove(record);
1021         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1022     },
1023
1024     // private
1025     afterCommit : function(record){
1026         this.modified.remove(record);
1027         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1028     },
1029
1030     /**
1031      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1032      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1033      */
1034     commitChanges : function(){
1035         var m = this.modified.slice(0);
1036         this.modified = [];
1037         for(var i = 0, len = m.length; i < len; i++){
1038             m[i].commit();
1039         }
1040     },
1041
1042     /**
1043      * Cancel outstanding changes on all changed records.
1044      */
1045     rejectChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].reject();
1050         }
1051     },
1052
1053     onMetaChange : function(meta, rtype, o){
1054         this.recordType = rtype;
1055         this.fields = rtype.prototype.fields;
1056         delete this.snapshot;
1057         this.sortInfo = meta.sortInfo || this.sortInfo;
1058         this.modified = [];
1059         this.fireEvent('metachange', this, this.reader.meta);
1060     },
1061     
1062     moveIndex : function(data, type)
1063     {
1064         var index = this.indexOf(data);
1065         
1066         var newIndex = index + type;
1067         
1068         this.remove(data);
1069         
1070         this.insert(newIndex, data);
1071         
1072     }
1073 });/*
1074  * Based on:
1075  * Ext JS Library 1.1.1
1076  * Copyright(c) 2006-2007, Ext JS, LLC.
1077  *
1078  * Originally Released Under LGPL - original licence link has changed is not relivant.
1079  *
1080  * Fork - LGPL
1081  * <script type="text/javascript">
1082  */
1083
1084 /**
1085  * @class Roo.data.SimpleStore
1086  * @extends Roo.data.Store
1087  * Small helper class to make creating Stores from Array data easier.
1088  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1089  * @cfg {Array} fields An array of field definition objects, or field name strings.
1090  * @cfg {Array} data The multi-dimensional array of data
1091  * @constructor
1092  * @param {Object} config
1093  */
1094 Roo.data.SimpleStore = function(config){
1095     Roo.data.SimpleStore.superclass.constructor.call(this, {
1096         isLocal : true,
1097         reader: new Roo.data.ArrayReader({
1098                 id: config.id
1099             },
1100             Roo.data.Record.create(config.fields)
1101         ),
1102         proxy : new Roo.data.MemoryProxy(config.data)
1103     });
1104     this.load();
1105 };
1106 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1107  * Based on:
1108  * Ext JS Library 1.1.1
1109  * Copyright(c) 2006-2007, Ext JS, LLC.
1110  *
1111  * Originally Released Under LGPL - original licence link has changed is not relivant.
1112  *
1113  * Fork - LGPL
1114  * <script type="text/javascript">
1115  */
1116
1117 /**
1118 /**
1119  * @extends Roo.data.Store
1120  * @class Roo.data.JsonStore
1121  * Small helper class to make creating Stores for JSON data easier. <br/>
1122 <pre><code>
1123 var store = new Roo.data.JsonStore({
1124     url: 'get-images.php',
1125     root: 'images',
1126     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1127 });
1128 </code></pre>
1129  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1130  * JsonReader and HttpProxy (unless inline data is provided).</b>
1131  * @cfg {Array} fields An array of field definition objects, or field name strings.
1132  * @constructor
1133  * @param {Object} config
1134  */
1135 Roo.data.JsonStore = function(c){
1136     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1137         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1138         reader: new Roo.data.JsonReader(c, c.fields)
1139     }));
1140 };
1141 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1142  * Based on:
1143  * Ext JS Library 1.1.1
1144  * Copyright(c) 2006-2007, Ext JS, LLC.
1145  *
1146  * Originally Released Under LGPL - original licence link has changed is not relivant.
1147  *
1148  * Fork - LGPL
1149  * <script type="text/javascript">
1150  */
1151
1152  
1153 Roo.data.Field = function(config){
1154     if(typeof config == "string"){
1155         config = {name: config};
1156     }
1157     Roo.apply(this, config);
1158     
1159     if(!this.type){
1160         this.type = "auto";
1161     }
1162     
1163     var st = Roo.data.SortTypes;
1164     // named sortTypes are supported, here we look them up
1165     if(typeof this.sortType == "string"){
1166         this.sortType = st[this.sortType];
1167     }
1168     
1169     // set default sortType for strings and dates
1170     if(!this.sortType){
1171         switch(this.type){
1172             case "string":
1173                 this.sortType = st.asUCString;
1174                 break;
1175             case "date":
1176                 this.sortType = st.asDate;
1177                 break;
1178             default:
1179                 this.sortType = st.none;
1180         }
1181     }
1182
1183     // define once
1184     var stripRe = /[\$,%]/g;
1185
1186     // prebuilt conversion function for this field, instead of
1187     // switching every time we're reading a value
1188     if(!this.convert){
1189         var cv, dateFormat = this.dateFormat;
1190         switch(this.type){
1191             case "":
1192             case "auto":
1193             case undefined:
1194                 cv = function(v){ return v; };
1195                 break;
1196             case "string":
1197                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1198                 break;
1199             case "int":
1200                 cv = function(v){
1201                     return v !== undefined && v !== null && v !== '' ?
1202                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1203                     };
1204                 break;
1205             case "float":
1206                 cv = function(v){
1207                     return v !== undefined && v !== null && v !== '' ?
1208                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1209                     };
1210                 break;
1211             case "bool":
1212             case "boolean":
1213                 cv = function(v){ return v === true || v === "true" || v == 1; };
1214                 break;
1215             case "date":
1216                 cv = function(v){
1217                     if(!v){
1218                         return '';
1219                     }
1220                     if(v instanceof Date){
1221                         return v;
1222                     }
1223                     if(dateFormat){
1224                         if(dateFormat == "timestamp"){
1225                             return new Date(v*1000);
1226                         }
1227                         return Date.parseDate(v, dateFormat);
1228                     }
1229                     var parsed = Date.parse(v);
1230                     return parsed ? new Date(parsed) : null;
1231                 };
1232              break;
1233             
1234         }
1235         this.convert = cv;
1236     }
1237 };
1238
1239 Roo.data.Field.prototype = {
1240     dateFormat: null,
1241     defaultValue: "",
1242     mapping: null,
1243     sortType : null,
1244     sortDir : "ASC"
1245 };/*
1246  * Based on:
1247  * Ext JS Library 1.1.1
1248  * Copyright(c) 2006-2007, Ext JS, LLC.
1249  *
1250  * Originally Released Under LGPL - original licence link has changed is not relivant.
1251  *
1252  * Fork - LGPL
1253  * <script type="text/javascript">
1254  */
1255  
1256 // Base class for reading structured data from a data source.  This class is intended to be
1257 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1258
1259 /**
1260  * @class Roo.data.DataReader
1261  * Base class for reading structured data from a data source.  This class is intended to be
1262  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1263  */
1264
1265 Roo.data.DataReader = function(meta, recordType){
1266     
1267     this.meta = meta;
1268     
1269     this.recordType = recordType instanceof Array ? 
1270         Roo.data.Record.create(recordType) : recordType;
1271 };
1272
1273 Roo.data.DataReader.prototype = {
1274      /**
1275      * Create an empty record
1276      * @param {Object} data (optional) - overlay some values
1277      * @return {Roo.data.Record} record created.
1278      */
1279     newRow :  function(d) {
1280         var da =  {};
1281         this.recordType.prototype.fields.each(function(c) {
1282             switch( c.type) {
1283                 case 'int' : da[c.name] = 0; break;
1284                 case 'date' : da[c.name] = new Date(); break;
1285                 case 'float' : da[c.name] = 0.0; break;
1286                 case 'boolean' : da[c.name] = false; break;
1287                 default : da[c.name] = ""; break;
1288             }
1289             
1290         });
1291         return new this.recordType(Roo.apply(da, d));
1292     }
1293     
1294 };/*
1295  * Based on:
1296  * Ext JS Library 1.1.1
1297  * Copyright(c) 2006-2007, Ext JS, LLC.
1298  *
1299  * Originally Released Under LGPL - original licence link has changed is not relivant.
1300  *
1301  * Fork - LGPL
1302  * <script type="text/javascript">
1303  */
1304
1305 /**
1306  * @class Roo.data.DataProxy
1307  * @extends Roo.data.Observable
1308  * This class is an abstract base class for implementations which provide retrieval of
1309  * unformatted data objects.<br>
1310  * <p>
1311  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1312  * (of the appropriate type which knows how to parse the data object) to provide a block of
1313  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1314  * <p>
1315  * Custom implementations must implement the load method as described in
1316  * {@link Roo.data.HttpProxy#load}.
1317  */
1318 Roo.data.DataProxy = function(){
1319     this.addEvents({
1320         /**
1321          * @event beforeload
1322          * Fires before a network request is made to retrieve a data object.
1323          * @param {Object} This DataProxy object.
1324          * @param {Object} params The params parameter to the load function.
1325          */
1326         beforeload : true,
1327         /**
1328          * @event load
1329          * Fires before the load method's callback is called.
1330          * @param {Object} This DataProxy object.
1331          * @param {Object} o The data object.
1332          * @param {Object} arg The callback argument object passed to the load function.
1333          */
1334         load : true,
1335         /**
1336          * @event loadexception
1337          * Fires if an Exception occurs during data retrieval.
1338          * @param {Object} This DataProxy object.
1339          * @param {Object} o The data object.
1340          * @param {Object} arg The callback argument object passed to the load function.
1341          * @param {Object} e The Exception.
1342          */
1343         loadexception : true
1344     });
1345     Roo.data.DataProxy.superclass.constructor.call(this);
1346 };
1347
1348 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1349
1350     /**
1351      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1352      */
1353 /*
1354  * Based on:
1355  * Ext JS Library 1.1.1
1356  * Copyright(c) 2006-2007, Ext JS, LLC.
1357  *
1358  * Originally Released Under LGPL - original licence link has changed is not relivant.
1359  *
1360  * Fork - LGPL
1361  * <script type="text/javascript">
1362  */
1363 /**
1364  * @class Roo.data.MemoryProxy
1365  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1366  * to the Reader when its load method is called.
1367  * @constructor
1368  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1369  */
1370 Roo.data.MemoryProxy = function(data){
1371     if (data.data) {
1372         data = data.data;
1373     }
1374     Roo.data.MemoryProxy.superclass.constructor.call(this);
1375     this.data = data;
1376 };
1377
1378 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1379     
1380     /**
1381      * Load data from the requested source (in this case an in-memory
1382      * data object passed to the constructor), read the data object into
1383      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1384      * process that block using the passed callback.
1385      * @param {Object} params This parameter is not used by the MemoryProxy class.
1386      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1387      * object into a block of Roo.data.Records.
1388      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1389      * The function must be passed <ul>
1390      * <li>The Record block object</li>
1391      * <li>The "arg" argument from the load function</li>
1392      * <li>A boolean success indicator</li>
1393      * </ul>
1394      * @param {Object} scope The scope in which to call the callback
1395      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1396      */
1397     load : function(params, reader, callback, scope, arg){
1398         params = params || {};
1399         var result;
1400         try {
1401             result = reader.readRecords(this.data);
1402         }catch(e){
1403             this.fireEvent("loadexception", this, arg, null, e);
1404             callback.call(scope, null, arg, false);
1405             return;
1406         }
1407         callback.call(scope, result, arg, true);
1408     },
1409     
1410     // private
1411     update : function(params, records){
1412         
1413     }
1414 });/*
1415  * Based on:
1416  * Ext JS Library 1.1.1
1417  * Copyright(c) 2006-2007, Ext JS, LLC.
1418  *
1419  * Originally Released Under LGPL - original licence link has changed is not relivant.
1420  *
1421  * Fork - LGPL
1422  * <script type="text/javascript">
1423  */
1424 /**
1425  * @class Roo.data.HttpProxy
1426  * @extends Roo.data.DataProxy
1427  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1428  * configured to reference a certain URL.<br><br>
1429  * <p>
1430  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1431  * from which the running page was served.<br><br>
1432  * <p>
1433  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1434  * <p>
1435  * Be aware that to enable the browser to parse an XML document, the server must set
1436  * the Content-Type header in the HTTP response to "text/xml".
1437  * @constructor
1438  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1439  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1440  * will be used to make the request.
1441  */
1442 Roo.data.HttpProxy = function(conn){
1443     Roo.data.HttpProxy.superclass.constructor.call(this);
1444     // is conn a conn config or a real conn?
1445     this.conn = conn;
1446     this.useAjax = !conn || !conn.events;
1447   
1448 };
1449
1450 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1451     // thse are take from connection...
1452     
1453     /**
1454      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1455      */
1456     /**
1457      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1458      * extra parameters to each request made by this object. (defaults to undefined)
1459      */
1460     /**
1461      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1462      *  to each request made by this object. (defaults to undefined)
1463      */
1464     /**
1465      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1466      */
1467     /**
1468      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1469      */
1470      /**
1471      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1472      * @type Boolean
1473      */
1474   
1475
1476     /**
1477      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1478      * @type Boolean
1479      */
1480     /**
1481      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1482      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1483      * a finer-grained basis than the DataProxy events.
1484      */
1485     getConnection : function(){
1486         return this.useAjax ? Roo.Ajax : this.conn;
1487     },
1488
1489     /**
1490      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1491      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1492      * process that block using the passed callback.
1493      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1494      * for the request to the remote server.
1495      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1496      * object into a block of Roo.data.Records.
1497      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1498      * The function must be passed <ul>
1499      * <li>The Record block object</li>
1500      * <li>The "arg" argument from the load function</li>
1501      * <li>A boolean success indicator</li>
1502      * </ul>
1503      * @param {Object} scope The scope in which to call the callback
1504      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1505      */
1506     load : function(params, reader, callback, scope, arg){
1507         if(this.fireEvent("beforeload", this, params) !== false){
1508             var  o = {
1509                 params : params || {},
1510                 request: {
1511                     callback : callback,
1512                     scope : scope,
1513                     arg : arg
1514                 },
1515                 reader: reader,
1516                 callback : this.loadResponse,
1517                 scope: this
1518             };
1519             if(this.useAjax){
1520                 Roo.applyIf(o, this.conn);
1521                 if(this.activeRequest){
1522                     Roo.Ajax.abort(this.activeRequest);
1523                 }
1524                 this.activeRequest = Roo.Ajax.request(o);
1525             }else{
1526                 this.conn.request(o);
1527             }
1528         }else{
1529             callback.call(scope||this, null, arg, false);
1530         }
1531     },
1532
1533     // private
1534     loadResponse : function(o, success, response){
1535         delete this.activeRequest;
1536         if(!success){
1537             this.fireEvent("loadexception", this, o, response);
1538             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1539             return;
1540         }
1541         var result;
1542         try {
1543             result = o.reader.read(response);
1544         }catch(e){
1545             this.fireEvent("loadexception", this, o, response, e);
1546             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1547             return;
1548         }
1549         
1550         this.fireEvent("load", this, o, o.request.arg);
1551         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1552     },
1553
1554     // private
1555     update : function(dataSet){
1556
1557     },
1558
1559     // private
1560     updateResponse : function(dataSet){
1561
1562     }
1563 });/*
1564  * Based on:
1565  * Ext JS Library 1.1.1
1566  * Copyright(c) 2006-2007, Ext JS, LLC.
1567  *
1568  * Originally Released Under LGPL - original licence link has changed is not relivant.
1569  *
1570  * Fork - LGPL
1571  * <script type="text/javascript">
1572  */
1573
1574 /**
1575  * @class Roo.data.ScriptTagProxy
1576  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1577  * other than the originating domain of the running page.<br><br>
1578  * <p>
1579  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1580  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1581  * <p>
1582  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1583  * source code that is used as the source inside a &lt;script> tag.<br><br>
1584  * <p>
1585  * In order for the browser to process the returned data, the server must wrap the data object
1586  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1587  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1588  * depending on whether the callback name was passed:
1589  * <p>
1590  * <pre><code>
1591 boolean scriptTag = false;
1592 String cb = request.getParameter("callback");
1593 if (cb != null) {
1594     scriptTag = true;
1595     response.setContentType("text/javascript");
1596 } else {
1597     response.setContentType("application/x-json");
1598 }
1599 Writer out = response.getWriter();
1600 if (scriptTag) {
1601     out.write(cb + "(");
1602 }
1603 out.print(dataBlock.toJsonString());
1604 if (scriptTag) {
1605     out.write(");");
1606 }
1607 </pre></code>
1608  *
1609  * @constructor
1610  * @param {Object} config A configuration object.
1611  */
1612 Roo.data.ScriptTagProxy = function(config){
1613     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1614     Roo.apply(this, config);
1615     this.head = document.getElementsByTagName("head")[0];
1616 };
1617
1618 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1619
1620 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1621     /**
1622      * @cfg {String} url The URL from which to request the data object.
1623      */
1624     /**
1625      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1626      */
1627     timeout : 30000,
1628     /**
1629      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1630      * the server the name of the callback function set up by the load call to process the returned data object.
1631      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1632      * javascript output which calls this named function passing the data object as its only parameter.
1633      */
1634     callbackParam : "callback",
1635     /**
1636      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1637      * name to the request.
1638      */
1639     nocache : true,
1640
1641     /**
1642      * Load data from the configured URL, read the data object into
1643      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1644      * process that block using the passed callback.
1645      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1646      * for the request to the remote server.
1647      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1648      * object into a block of Roo.data.Records.
1649      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1650      * The function must be passed <ul>
1651      * <li>The Record block object</li>
1652      * <li>The "arg" argument from the load function</li>
1653      * <li>A boolean success indicator</li>
1654      * </ul>
1655      * @param {Object} scope The scope in which to call the callback
1656      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1657      */
1658     load : function(params, reader, callback, scope, arg){
1659         if(this.fireEvent("beforeload", this, params) !== false){
1660
1661             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1662
1663             var url = this.url;
1664             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1665             if(this.nocache){
1666                 url += "&_dc=" + (new Date().getTime());
1667             }
1668             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1669             var trans = {
1670                 id : transId,
1671                 cb : "stcCallback"+transId,
1672                 scriptId : "stcScript"+transId,
1673                 params : params,
1674                 arg : arg,
1675                 url : url,
1676                 callback : callback,
1677                 scope : scope,
1678                 reader : reader
1679             };
1680             var conn = this;
1681
1682             window[trans.cb] = function(o){
1683                 conn.handleResponse(o, trans);
1684             };
1685
1686             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1687
1688             if(this.autoAbort !== false){
1689                 this.abort();
1690             }
1691
1692             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1693
1694             var script = document.createElement("script");
1695             script.setAttribute("src", url);
1696             script.setAttribute("type", "text/javascript");
1697             script.setAttribute("id", trans.scriptId);
1698             this.head.appendChild(script);
1699
1700             this.trans = trans;
1701         }else{
1702             callback.call(scope||this, null, arg, false);
1703         }
1704     },
1705
1706     // private
1707     isLoading : function(){
1708         return this.trans ? true : false;
1709     },
1710
1711     /**
1712      * Abort the current server request.
1713      */
1714     abort : function(){
1715         if(this.isLoading()){
1716             this.destroyTrans(this.trans);
1717         }
1718     },
1719
1720     // private
1721     destroyTrans : function(trans, isLoaded){
1722         this.head.removeChild(document.getElementById(trans.scriptId));
1723         clearTimeout(trans.timeoutId);
1724         if(isLoaded){
1725             window[trans.cb] = undefined;
1726             try{
1727                 delete window[trans.cb];
1728             }catch(e){}
1729         }else{
1730             // if hasn't been loaded, wait for load to remove it to prevent script error
1731             window[trans.cb] = function(){
1732                 window[trans.cb] = undefined;
1733                 try{
1734                     delete window[trans.cb];
1735                 }catch(e){}
1736             };
1737         }
1738     },
1739
1740     // private
1741     handleResponse : function(o, trans){
1742         this.trans = false;
1743         this.destroyTrans(trans, true);
1744         var result;
1745         try {
1746             result = trans.reader.readRecords(o);
1747         }catch(e){
1748             this.fireEvent("loadexception", this, o, trans.arg, e);
1749             trans.callback.call(trans.scope||window, null, trans.arg, false);
1750             return;
1751         }
1752         this.fireEvent("load", this, o, trans.arg);
1753         trans.callback.call(trans.scope||window, result, trans.arg, true);
1754     },
1755
1756     // private
1757     handleFailure : function(trans){
1758         this.trans = false;
1759         this.destroyTrans(trans, false);
1760         this.fireEvent("loadexception", this, null, trans.arg);
1761         trans.callback.call(trans.scope||window, null, trans.arg, false);
1762     }
1763 });/*
1764  * Based on:
1765  * Ext JS Library 1.1.1
1766  * Copyright(c) 2006-2007, Ext JS, LLC.
1767  *
1768  * Originally Released Under LGPL - original licence link has changed is not relivant.
1769  *
1770  * Fork - LGPL
1771  * <script type="text/javascript">
1772  */
1773
1774 /**
1775  * @class Roo.data.JsonReader
1776  * @extends Roo.data.DataReader
1777  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1778  * based on mappings in a provided Roo.data.Record constructor.
1779  * 
1780  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1781  * in the reply previously. 
1782  * 
1783  * <p>
1784  * Example code:
1785  * <pre><code>
1786 var RecordDef = Roo.data.Record.create([
1787     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1788     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1789 ]);
1790 var myReader = new Roo.data.JsonReader({
1791     totalProperty: "results",    // The property which contains the total dataset size (optional)
1792     root: "rows",                // The property which contains an Array of row objects
1793     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1794 }, RecordDef);
1795 </code></pre>
1796  * <p>
1797  * This would consume a JSON file like this:
1798  * <pre><code>
1799 { 'results': 2, 'rows': [
1800     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1801     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1802 }
1803 </code></pre>
1804  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1805  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1806  * paged from the remote server.
1807  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1808  * @cfg {String} root name of the property which contains the Array of row objects.
1809  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1810  * @cfg {Array} fields Array of field definition objects
1811  * @constructor
1812  * Create a new JsonReader
1813  * @param {Object} meta Metadata configuration options
1814  * @param {Object} recordType Either an Array of field definition objects,
1815  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1816  */
1817 Roo.data.JsonReader = function(meta, recordType){
1818     
1819     meta = meta || {};
1820     // set some defaults:
1821     Roo.applyIf(meta, {
1822         totalProperty: 'total',
1823         successProperty : 'success',
1824         root : 'data',
1825         id : 'id'
1826     });
1827     
1828     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1829 };
1830 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1831     
1832     /**
1833      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1834      * Used by Store query builder to append _requestMeta to params.
1835      * 
1836      */
1837     metaFromRemote : false,
1838     /**
1839      * This method is only used by a DataProxy which has retrieved data from a remote server.
1840      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1841      * @return {Object} data A data block which is used by an Roo.data.Store object as
1842      * a cache of Roo.data.Records.
1843      */
1844     read : function(response){
1845         var json = response.responseText;
1846        
1847         var o = /* eval:var:o */ eval("("+json+")");
1848         if(!o) {
1849             throw {message: "JsonReader.read: Json object not found"};
1850         }
1851         
1852         if(o.metaData){
1853             
1854             delete this.ef;
1855             this.metaFromRemote = true;
1856             this.meta = o.metaData;
1857             this.recordType = Roo.data.Record.create(o.metaData.fields);
1858             this.onMetaChange(this.meta, this.recordType, o);
1859         }
1860         return this.readRecords(o);
1861     },
1862
1863     // private function a store will implement
1864     onMetaChange : function(meta, recordType, o){
1865
1866     },
1867
1868     /**
1869          * @ignore
1870          */
1871     simpleAccess: function(obj, subsc) {
1872         return obj[subsc];
1873     },
1874
1875         /**
1876          * @ignore
1877          */
1878     getJsonAccessor: function(){
1879         var re = /[\[\.]/;
1880         return function(expr) {
1881             try {
1882                 return(re.test(expr))
1883                     ? new Function("obj", "return obj." + expr)
1884                     : function(obj){
1885                         return obj[expr];
1886                     };
1887             } catch(e){}
1888             return Roo.emptyFn;
1889         };
1890     }(),
1891
1892     /**
1893      * Create a data block containing Roo.data.Records from an XML document.
1894      * @param {Object} o An object which contains an Array of row objects in the property specified
1895      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1896      * which contains the total size of the dataset.
1897      * @return {Object} data A data block which is used by an Roo.data.Store object as
1898      * a cache of Roo.data.Records.
1899      */
1900     readRecords : function(o){
1901         /**
1902          * After any data loads, the raw JSON data is available for further custom processing.
1903          * @type Object
1904          */
1905         this.o = o;
1906         var s = this.meta, Record = this.recordType,
1907             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1908
1909 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1910         if (!this.ef) {
1911             if(s.totalProperty) {
1912                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1913                 }
1914                 if(s.successProperty) {
1915                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1916                 }
1917                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1918                 if (s.id) {
1919                         var g = this.getJsonAccessor(s.id);
1920                         this.getId = function(rec) {
1921                                 var r = g(rec);  
1922                                 return (r === undefined || r === "") ? null : r;
1923                         };
1924                 } else {
1925                         this.getId = function(){return null;};
1926                 }
1927             this.ef = [];
1928             for(var jj = 0; jj < fl; jj++){
1929                 f = fi[jj];
1930                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1931                 this.ef[jj] = this.getJsonAccessor(map);
1932             }
1933         }
1934
1935         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1936         if(s.totalProperty){
1937             var vt = parseInt(this.getTotal(o), 10);
1938             if(!isNaN(vt)){
1939                 totalRecords = vt;
1940             }
1941         }
1942         if(s.successProperty){
1943             var vs = this.getSuccess(o);
1944             if(vs === false || vs === 'false'){
1945                 success = false;
1946             }
1947         }
1948         var records = [];
1949         for(var i = 0; i < c; i++){
1950                 var n = root[i];
1951             var values = {};
1952             var id = this.getId(n);
1953             for(var j = 0; j < fl; j++){
1954                 f = fi[j];
1955             var v = this.ef[j](n);
1956             if (!f.convert) {
1957                 Roo.log('missing convert for ' + f.name);
1958                 Roo.log(f);
1959                 continue;
1960             }
1961             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1962             }
1963             var record = new Record(values, id);
1964             record.json = n;
1965             records[i] = record;
1966         }
1967         return {
1968             raw : o,
1969             success : success,
1970             records : records,
1971             totalRecords : totalRecords
1972         };
1973     }
1974 });/*
1975  * Based on:
1976  * Ext JS Library 1.1.1
1977  * Copyright(c) 2006-2007, Ext JS, LLC.
1978  *
1979  * Originally Released Under LGPL - original licence link has changed is not relivant.
1980  *
1981  * Fork - LGPL
1982  * <script type="text/javascript">
1983  */
1984
1985 /**
1986  * @class Roo.data.XmlReader
1987  * @extends Roo.data.DataReader
1988  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1989  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1990  * <p>
1991  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1992  * header in the HTTP response must be set to "text/xml".</em>
1993  * <p>
1994  * Example code:
1995  * <pre><code>
1996 var RecordDef = Roo.data.Record.create([
1997    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1998    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1999 ]);
2000 var myReader = new Roo.data.XmlReader({
2001    totalRecords: "results", // The element which contains the total dataset size (optional)
2002    record: "row",           // The repeated element which contains row information
2003    id: "id"                 // The element within the row that provides an ID for the record (optional)
2004 }, RecordDef);
2005 </code></pre>
2006  * <p>
2007  * This would consume an XML file like this:
2008  * <pre><code>
2009 &lt;?xml?>
2010 &lt;dataset>
2011  &lt;results>2&lt;/results>
2012  &lt;row>
2013    &lt;id>1&lt;/id>
2014    &lt;name>Bill&lt;/name>
2015    &lt;occupation>Gardener&lt;/occupation>
2016  &lt;/row>
2017  &lt;row>
2018    &lt;id>2&lt;/id>
2019    &lt;name>Ben&lt;/name>
2020    &lt;occupation>Horticulturalist&lt;/occupation>
2021  &lt;/row>
2022 &lt;/dataset>
2023 </code></pre>
2024  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2025  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2026  * paged from the remote server.
2027  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2028  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2029  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2030  * a record identifier value.
2031  * @constructor
2032  * Create a new XmlReader
2033  * @param {Object} meta Metadata configuration options
2034  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2035  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2036  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2037  */
2038 Roo.data.XmlReader = function(meta, recordType){
2039     meta = meta || {};
2040     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2041 };
2042 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2043     /**
2044      * This method is only used by a DataProxy which has retrieved data from a remote server.
2045          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2046          * to contain a method called 'responseXML' that returns an XML document object.
2047      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2048      * a cache of Roo.data.Records.
2049      */
2050     read : function(response){
2051         var doc = response.responseXML;
2052         if(!doc) {
2053             throw {message: "XmlReader.read: XML Document not available"};
2054         }
2055         return this.readRecords(doc);
2056     },
2057
2058     /**
2059      * Create a data block containing Roo.data.Records from an XML document.
2060          * @param {Object} doc A parsed XML document.
2061      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2062      * a cache of Roo.data.Records.
2063      */
2064     readRecords : function(doc){
2065         /**
2066          * After any data loads/reads, the raw XML Document is available for further custom processing.
2067          * @type XMLDocument
2068          */
2069         this.xmlData = doc;
2070         var root = doc.documentElement || doc;
2071         var q = Roo.DomQuery;
2072         var recordType = this.recordType, fields = recordType.prototype.fields;
2073         var sid = this.meta.id;
2074         var totalRecords = 0, success = true;
2075         if(this.meta.totalRecords){
2076             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2077         }
2078         
2079         if(this.meta.success){
2080             var sv = q.selectValue(this.meta.success, root, true);
2081             success = sv !== false && sv !== 'false';
2082         }
2083         var records = [];
2084         var ns = q.select(this.meta.record, root);
2085         for(var i = 0, len = ns.length; i < len; i++) {
2086                 var n = ns[i];
2087                 var values = {};
2088                 var id = sid ? q.selectValue(sid, n) : undefined;
2089                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2090                     var f = fields.items[j];
2091                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2092                     v = f.convert(v);
2093                     values[f.name] = v;
2094                 }
2095                 var record = new recordType(values, id);
2096                 record.node = n;
2097                 records[records.length] = record;
2098             }
2099
2100             return {
2101                 success : success,
2102                 records : records,
2103                 totalRecords : totalRecords || records.length
2104             };
2105     }
2106 });/*
2107  * Based on:
2108  * Ext JS Library 1.1.1
2109  * Copyright(c) 2006-2007, Ext JS, LLC.
2110  *
2111  * Originally Released Under LGPL - original licence link has changed is not relivant.
2112  *
2113  * Fork - LGPL
2114  * <script type="text/javascript">
2115  */
2116
2117 /**
2118  * @class Roo.data.ArrayReader
2119  * @extends Roo.data.DataReader
2120  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2121  * Each element of that Array represents a row of data fields. The
2122  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2123  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2124  * <p>
2125  * Example code:.
2126  * <pre><code>
2127 var RecordDef = Roo.data.Record.create([
2128     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2129     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2130 ]);
2131 var myReader = new Roo.data.ArrayReader({
2132     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2133 }, RecordDef);
2134 </code></pre>
2135  * <p>
2136  * This would consume an Array like this:
2137  * <pre><code>
2138 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2139   </code></pre>
2140  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
2141  * @constructor
2142  * Create a new JsonReader
2143  * @param {Object} meta Metadata configuration options.
2144  * @param {Object} recordType Either an Array of field definition objects
2145  * as specified to {@link Roo.data.Record#create},
2146  * or an {@link Roo.data.Record} object
2147  * created using {@link Roo.data.Record#create}.
2148  */
2149 Roo.data.ArrayReader = function(meta, recordType){
2150     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
2151 };
2152
2153 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2154     /**
2155      * Create a data block containing Roo.data.Records from an XML document.
2156      * @param {Object} o An Array of row objects which represents the dataset.
2157      * @return {Object} data A data block which is used by an Roo.data.Store object as
2158      * a cache of Roo.data.Records.
2159      */
2160     readRecords : function(o){
2161         var sid = this.meta ? this.meta.id : null;
2162         var recordType = this.recordType, fields = recordType.prototype.fields;
2163         var records = [];
2164         var root = o;
2165             for(var i = 0; i < root.length; i++){
2166                     var n = root[i];
2167                 var values = {};
2168                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2169                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2170                 var f = fields.items[j];
2171                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2172                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2173                 v = f.convert(v);
2174                 values[f.name] = v;
2175             }
2176                 var record = new recordType(values, id);
2177                 record.json = n;
2178                 records[records.length] = record;
2179             }
2180             return {
2181                 records : records,
2182                 totalRecords : records.length
2183             };
2184     }
2185 });/*
2186  * Based on:
2187  * Ext JS Library 1.1.1
2188  * Copyright(c) 2006-2007, Ext JS, LLC.
2189  *
2190  * Originally Released Under LGPL - original licence link has changed is not relivant.
2191  *
2192  * Fork - LGPL
2193  * <script type="text/javascript">
2194  */
2195
2196
2197 /**
2198  * @class Roo.data.Tree
2199  * @extends Roo.util.Observable
2200  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2201  * in the tree have most standard DOM functionality.
2202  * @constructor
2203  * @param {Node} root (optional) The root node
2204  */
2205 Roo.data.Tree = function(root){
2206    this.nodeHash = {};
2207    /**
2208     * The root node for this tree
2209     * @type Node
2210     */
2211    this.root = null;
2212    if(root){
2213        this.setRootNode(root);
2214    }
2215    this.addEvents({
2216        /**
2217         * @event append
2218         * Fires when a new child node is appended to a node in this tree.
2219         * @param {Tree} tree The owner tree
2220         * @param {Node} parent The parent node
2221         * @param {Node} node The newly appended node
2222         * @param {Number} index The index of the newly appended node
2223         */
2224        "append" : true,
2225        /**
2226         * @event remove
2227         * Fires when a child node is removed from a node in this tree.
2228         * @param {Tree} tree The owner tree
2229         * @param {Node} parent The parent node
2230         * @param {Node} node The child node removed
2231         */
2232        "remove" : true,
2233        /**
2234         * @event move
2235         * Fires when a node is moved to a new location in the tree
2236         * @param {Tree} tree The owner tree
2237         * @param {Node} node The node moved
2238         * @param {Node} oldParent The old parent of this node
2239         * @param {Node} newParent The new parent of this node
2240         * @param {Number} index The index it was moved to
2241         */
2242        "move" : true,
2243        /**
2244         * @event insert
2245         * Fires when a new child node is inserted in a node in this tree.
2246         * @param {Tree} tree The owner tree
2247         * @param {Node} parent The parent node
2248         * @param {Node} node The child node inserted
2249         * @param {Node} refNode The child node the node was inserted before
2250         */
2251        "insert" : true,
2252        /**
2253         * @event beforeappend
2254         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2255         * @param {Tree} tree The owner tree
2256         * @param {Node} parent The parent node
2257         * @param {Node} node The child node to be appended
2258         */
2259        "beforeappend" : true,
2260        /**
2261         * @event beforeremove
2262         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2263         * @param {Tree} tree The owner tree
2264         * @param {Node} parent The parent node
2265         * @param {Node} node The child node to be removed
2266         */
2267        "beforeremove" : true,
2268        /**
2269         * @event beforemove
2270         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2271         * @param {Tree} tree The owner tree
2272         * @param {Node} node The node being moved
2273         * @param {Node} oldParent The parent of the node
2274         * @param {Node} newParent The new parent the node is moving to
2275         * @param {Number} index The index it is being moved to
2276         */
2277        "beforemove" : true,
2278        /**
2279         * @event beforeinsert
2280         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2281         * @param {Tree} tree The owner tree
2282         * @param {Node} parent The parent node
2283         * @param {Node} node The child node to be inserted
2284         * @param {Node} refNode The child node the node is being inserted before
2285         */
2286        "beforeinsert" : true
2287    });
2288
2289     Roo.data.Tree.superclass.constructor.call(this);
2290 };
2291
2292 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2293     pathSeparator: "/",
2294
2295     proxyNodeEvent : function(){
2296         return this.fireEvent.apply(this, arguments);
2297     },
2298
2299     /**
2300      * Returns the root node for this tree.
2301      * @return {Node}
2302      */
2303     getRootNode : function(){
2304         return this.root;
2305     },
2306
2307     /**
2308      * Sets the root node for this tree.
2309      * @param {Node} node
2310      * @return {Node}
2311      */
2312     setRootNode : function(node){
2313         this.root = node;
2314         node.ownerTree = this;
2315         node.isRoot = true;
2316         this.registerNode(node);
2317         return node;
2318     },
2319
2320     /**
2321      * Gets a node in this tree by its id.
2322      * @param {String} id
2323      * @return {Node}
2324      */
2325     getNodeById : function(id){
2326         return this.nodeHash[id];
2327     },
2328
2329     registerNode : function(node){
2330         this.nodeHash[node.id] = node;
2331     },
2332
2333     unregisterNode : function(node){
2334         delete this.nodeHash[node.id];
2335     },
2336
2337     toString : function(){
2338         return "[Tree"+(this.id?" "+this.id:"")+"]";
2339     }
2340 });
2341
2342 /**
2343  * @class Roo.data.Node
2344  * @extends Roo.util.Observable
2345  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2346  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2347  * @constructor
2348  * @param {Object} attributes The attributes/config for the node
2349  */
2350 Roo.data.Node = function(attributes){
2351     /**
2352      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2353      * @type {Object}
2354      */
2355     this.attributes = attributes || {};
2356     this.leaf = this.attributes.leaf;
2357     /**
2358      * The node id. @type String
2359      */
2360     this.id = this.attributes.id;
2361     if(!this.id){
2362         this.id = Roo.id(null, "ynode-");
2363         this.attributes.id = this.id;
2364     }
2365      
2366     
2367     /**
2368      * All child nodes of this node. @type Array
2369      */
2370     this.childNodes = [];
2371     if(!this.childNodes.indexOf){ // indexOf is a must
2372         this.childNodes.indexOf = function(o){
2373             for(var i = 0, len = this.length; i < len; i++){
2374                 if(this[i] == o) {
2375                     return i;
2376                 }
2377             }
2378             return -1;
2379         };
2380     }
2381     /**
2382      * The parent node for this node. @type Node
2383      */
2384     this.parentNode = null;
2385     /**
2386      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2387      */
2388     this.firstChild = null;
2389     /**
2390      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2391      */
2392     this.lastChild = null;
2393     /**
2394      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2395      */
2396     this.previousSibling = null;
2397     /**
2398      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2399      */
2400     this.nextSibling = null;
2401
2402     this.addEvents({
2403        /**
2404         * @event append
2405         * Fires when a new child node is appended
2406         * @param {Tree} tree The owner tree
2407         * @param {Node} this This node
2408         * @param {Node} node The newly appended node
2409         * @param {Number} index The index of the newly appended node
2410         */
2411        "append" : true,
2412        /**
2413         * @event remove
2414         * Fires when a child node is removed
2415         * @param {Tree} tree The owner tree
2416         * @param {Node} this This node
2417         * @param {Node} node The removed node
2418         */
2419        "remove" : true,
2420        /**
2421         * @event move
2422         * Fires when this node is moved to a new location in the tree
2423         * @param {Tree} tree The owner tree
2424         * @param {Node} this This node
2425         * @param {Node} oldParent The old parent of this node
2426         * @param {Node} newParent The new parent of this node
2427         * @param {Number} index The index it was moved to
2428         */
2429        "move" : true,
2430        /**
2431         * @event insert
2432         * Fires when a new child node is inserted.
2433         * @param {Tree} tree The owner tree
2434         * @param {Node} this This node
2435         * @param {Node} node The child node inserted
2436         * @param {Node} refNode The child node the node was inserted before
2437         */
2438        "insert" : true,
2439        /**
2440         * @event beforeappend
2441         * Fires before a new child is appended, return false to cancel the append.
2442         * @param {Tree} tree The owner tree
2443         * @param {Node} this This node
2444         * @param {Node} node The child node to be appended
2445         */
2446        "beforeappend" : true,
2447        /**
2448         * @event beforeremove
2449         * Fires before a child is removed, return false to cancel the remove.
2450         * @param {Tree} tree The owner tree
2451         * @param {Node} this This node
2452         * @param {Node} node The child node to be removed
2453         */
2454        "beforeremove" : true,
2455        /**
2456         * @event beforemove
2457         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2458         * @param {Tree} tree The owner tree
2459         * @param {Node} this This node
2460         * @param {Node} oldParent The parent of this node
2461         * @param {Node} newParent The new parent this node is moving to
2462         * @param {Number} index The index it is being moved to
2463         */
2464        "beforemove" : true,
2465        /**
2466         * @event beforeinsert
2467         * Fires before a new child is inserted, return false to cancel the insert.
2468         * @param {Tree} tree The owner tree
2469         * @param {Node} this This node
2470         * @param {Node} node The child node to be inserted
2471         * @param {Node} refNode The child node the node is being inserted before
2472         */
2473        "beforeinsert" : true
2474    });
2475     this.listeners = this.attributes.listeners;
2476     Roo.data.Node.superclass.constructor.call(this);
2477 };
2478
2479 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2480     fireEvent : function(evtName){
2481         // first do standard event for this node
2482         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2483             return false;
2484         }
2485         // then bubble it up to the tree if the event wasn't cancelled
2486         var ot = this.getOwnerTree();
2487         if(ot){
2488             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2489                 return false;
2490             }
2491         }
2492         return true;
2493     },
2494
2495     /**
2496      * Returns true if this node is a leaf
2497      * @return {Boolean}
2498      */
2499     isLeaf : function(){
2500         return this.leaf === true;
2501     },
2502
2503     // private
2504     setFirstChild : function(node){
2505         this.firstChild = node;
2506     },
2507
2508     //private
2509     setLastChild : function(node){
2510         this.lastChild = node;
2511     },
2512
2513
2514     /**
2515      * Returns true if this node is the last child of its parent
2516      * @return {Boolean}
2517      */
2518     isLast : function(){
2519        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2520     },
2521
2522     /**
2523      * Returns true if this node is the first child of its parent
2524      * @return {Boolean}
2525      */
2526     isFirst : function(){
2527        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2528     },
2529
2530     hasChildNodes : function(){
2531         return !this.isLeaf() && this.childNodes.length > 0;
2532     },
2533
2534     /**
2535      * Insert node(s) as the last child node of this node.
2536      * @param {Node/Array} node The node or Array of nodes to append
2537      * @return {Node} The appended node if single append, or null if an array was passed
2538      */
2539     appendChild : function(node){
2540         var multi = false;
2541         if(node instanceof Array){
2542             multi = node;
2543         }else if(arguments.length > 1){
2544             multi = arguments;
2545         }
2546         // if passed an array or multiple args do them one by one
2547         if(multi){
2548             for(var i = 0, len = multi.length; i < len; i++) {
2549                 this.appendChild(multi[i]);
2550             }
2551         }else{
2552             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2553                 return false;
2554             }
2555             var index = this.childNodes.length;
2556             var oldParent = node.parentNode;
2557             // it's a move, make sure we move it cleanly
2558             if(oldParent){
2559                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2560                     return false;
2561                 }
2562                 oldParent.removeChild(node);
2563             }
2564             index = this.childNodes.length;
2565             if(index == 0){
2566                 this.setFirstChild(node);
2567             }
2568             this.childNodes.push(node);
2569             node.parentNode = this;
2570             var ps = this.childNodes[index-1];
2571             if(ps){
2572                 node.previousSibling = ps;
2573                 ps.nextSibling = node;
2574             }else{
2575                 node.previousSibling = null;
2576             }
2577             node.nextSibling = null;
2578             this.setLastChild(node);
2579             node.setOwnerTree(this.getOwnerTree());
2580             this.fireEvent("append", this.ownerTree, this, node, index);
2581             if(oldParent){
2582                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2583             }
2584             return node;
2585         }
2586     },
2587
2588     /**
2589      * Removes a child node from this node.
2590      * @param {Node} node The node to remove
2591      * @return {Node} The removed node
2592      */
2593     removeChild : function(node){
2594         var index = this.childNodes.indexOf(node);
2595         if(index == -1){
2596             return false;
2597         }
2598         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2599             return false;
2600         }
2601
2602         // remove it from childNodes collection
2603         this.childNodes.splice(index, 1);
2604
2605         // update siblings
2606         if(node.previousSibling){
2607             node.previousSibling.nextSibling = node.nextSibling;
2608         }
2609         if(node.nextSibling){
2610             node.nextSibling.previousSibling = node.previousSibling;
2611         }
2612
2613         // update child refs
2614         if(this.firstChild == node){
2615             this.setFirstChild(node.nextSibling);
2616         }
2617         if(this.lastChild == node){
2618             this.setLastChild(node.previousSibling);
2619         }
2620
2621         node.setOwnerTree(null);
2622         // clear any references from the node
2623         node.parentNode = null;
2624         node.previousSibling = null;
2625         node.nextSibling = null;
2626         this.fireEvent("remove", this.ownerTree, this, node);
2627         return node;
2628     },
2629
2630     /**
2631      * Inserts the first node before the second node in this nodes childNodes collection.
2632      * @param {Node} node The node to insert
2633      * @param {Node} refNode The node to insert before (if null the node is appended)
2634      * @return {Node} The inserted node
2635      */
2636     insertBefore : function(node, refNode){
2637         if(!refNode){ // like standard Dom, refNode can be null for append
2638             return this.appendChild(node);
2639         }
2640         // nothing to do
2641         if(node == refNode){
2642             return false;
2643         }
2644
2645         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2646             return false;
2647         }
2648         var index = this.childNodes.indexOf(refNode);
2649         var oldParent = node.parentNode;
2650         var refIndex = index;
2651
2652         // when moving internally, indexes will change after remove
2653         if(oldParent == this && this.childNodes.indexOf(node) < index){
2654             refIndex--;
2655         }
2656
2657         // it's a move, make sure we move it cleanly
2658         if(oldParent){
2659             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2660                 return false;
2661             }
2662             oldParent.removeChild(node);
2663         }
2664         if(refIndex == 0){
2665             this.setFirstChild(node);
2666         }
2667         this.childNodes.splice(refIndex, 0, node);
2668         node.parentNode = this;
2669         var ps = this.childNodes[refIndex-1];
2670         if(ps){
2671             node.previousSibling = ps;
2672             ps.nextSibling = node;
2673         }else{
2674             node.previousSibling = null;
2675         }
2676         node.nextSibling = refNode;
2677         refNode.previousSibling = node;
2678         node.setOwnerTree(this.getOwnerTree());
2679         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2680         if(oldParent){
2681             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2682         }
2683         return node;
2684     },
2685
2686     /**
2687      * Returns the child node at the specified index.
2688      * @param {Number} index
2689      * @return {Node}
2690      */
2691     item : function(index){
2692         return this.childNodes[index];
2693     },
2694
2695     /**
2696      * Replaces one child node in this node with another.
2697      * @param {Node} newChild The replacement node
2698      * @param {Node} oldChild The node to replace
2699      * @return {Node} The replaced node
2700      */
2701     replaceChild : function(newChild, oldChild){
2702         this.insertBefore(newChild, oldChild);
2703         this.removeChild(oldChild);
2704         return oldChild;
2705     },
2706
2707     /**
2708      * Returns the index of a child node
2709      * @param {Node} node
2710      * @return {Number} The index of the node or -1 if it was not found
2711      */
2712     indexOf : function(child){
2713         return this.childNodes.indexOf(child);
2714     },
2715
2716     /**
2717      * Returns the tree this node is in.
2718      * @return {Tree}
2719      */
2720     getOwnerTree : function(){
2721         // if it doesn't have one, look for one
2722         if(!this.ownerTree){
2723             var p = this;
2724             while(p){
2725                 if(p.ownerTree){
2726                     this.ownerTree = p.ownerTree;
2727                     break;
2728                 }
2729                 p = p.parentNode;
2730             }
2731         }
2732         return this.ownerTree;
2733     },
2734
2735     /**
2736      * Returns depth of this node (the root node has a depth of 0)
2737      * @return {Number}
2738      */
2739     getDepth : function(){
2740         var depth = 0;
2741         var p = this;
2742         while(p.parentNode){
2743             ++depth;
2744             p = p.parentNode;
2745         }
2746         return depth;
2747     },
2748
2749     // private
2750     setOwnerTree : function(tree){
2751         // if it's move, we need to update everyone
2752         if(tree != this.ownerTree){
2753             if(this.ownerTree){
2754                 this.ownerTree.unregisterNode(this);
2755             }
2756             this.ownerTree = tree;
2757             var cs = this.childNodes;
2758             for(var i = 0, len = cs.length; i < len; i++) {
2759                 cs[i].setOwnerTree(tree);
2760             }
2761             if(tree){
2762                 tree.registerNode(this);
2763             }
2764         }
2765     },
2766
2767     /**
2768      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2769      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2770      * @return {String} The path
2771      */
2772     getPath : function(attr){
2773         attr = attr || "id";
2774         var p = this.parentNode;
2775         var b = [this.attributes[attr]];
2776         while(p){
2777             b.unshift(p.attributes[attr]);
2778             p = p.parentNode;
2779         }
2780         var sep = this.getOwnerTree().pathSeparator;
2781         return sep + b.join(sep);
2782     },
2783
2784     /**
2785      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2786      * function call will be the scope provided or the current node. The arguments to the function
2787      * will be the args provided or the current node. If the function returns false at any point,
2788      * the bubble is stopped.
2789      * @param {Function} fn The function to call
2790      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2791      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2792      */
2793     bubble : function(fn, scope, args){
2794         var p = this;
2795         while(p){
2796             if(fn.call(scope || p, args || p) === false){
2797                 break;
2798             }
2799             p = p.parentNode;
2800         }
2801     },
2802
2803     /**
2804      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2805      * function call will be the scope provided or the current node. The arguments to the function
2806      * will be the args provided or the current node. If the function returns false at any point,
2807      * the cascade is stopped on that branch.
2808      * @param {Function} fn The function to call
2809      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2810      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2811      */
2812     cascade : function(fn, scope, args){
2813         if(fn.call(scope || this, args || this) !== false){
2814             var cs = this.childNodes;
2815             for(var i = 0, len = cs.length; i < len; i++) {
2816                 cs[i].cascade(fn, scope, args);
2817             }
2818         }
2819     },
2820
2821     /**
2822      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2823      * function call will be the scope provided or the current node. The arguments to the function
2824      * will be the args provided or the current node. If the function returns false at any point,
2825      * the iteration stops.
2826      * @param {Function} fn The function to call
2827      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2828      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2829      */
2830     eachChild : function(fn, scope, args){
2831         var cs = this.childNodes;
2832         for(var i = 0, len = cs.length; i < len; i++) {
2833                 if(fn.call(scope || this, args || cs[i]) === false){
2834                     break;
2835                 }
2836         }
2837     },
2838
2839     /**
2840      * Finds the first child that has the attribute with the specified value.
2841      * @param {String} attribute The attribute name
2842      * @param {Mixed} value The value to search for
2843      * @return {Node} The found child or null if none was found
2844      */
2845     findChild : function(attribute, value){
2846         var cs = this.childNodes;
2847         for(var i = 0, len = cs.length; i < len; i++) {
2848                 if(cs[i].attributes[attribute] == value){
2849                     return cs[i];
2850                 }
2851         }
2852         return null;
2853     },
2854
2855     /**
2856      * Finds the first child by a custom function. The child matches if the function passed
2857      * returns true.
2858      * @param {Function} fn
2859      * @param {Object} scope (optional)
2860      * @return {Node} The found child or null if none was found
2861      */
2862     findChildBy : function(fn, scope){
2863         var cs = this.childNodes;
2864         for(var i = 0, len = cs.length; i < len; i++) {
2865                 if(fn.call(scope||cs[i], cs[i]) === true){
2866                     return cs[i];
2867                 }
2868         }
2869         return null;
2870     },
2871
2872     /**
2873      * Sorts this nodes children using the supplied sort function
2874      * @param {Function} fn
2875      * @param {Object} scope (optional)
2876      */
2877     sort : function(fn, scope){
2878         var cs = this.childNodes;
2879         var len = cs.length;
2880         if(len > 0){
2881             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2882             cs.sort(sortFn);
2883             for(var i = 0; i < len; i++){
2884                 var n = cs[i];
2885                 n.previousSibling = cs[i-1];
2886                 n.nextSibling = cs[i+1];
2887                 if(i == 0){
2888                     this.setFirstChild(n);
2889                 }
2890                 if(i == len-1){
2891                     this.setLastChild(n);
2892                 }
2893             }
2894         }
2895     },
2896
2897     /**
2898      * Returns true if this node is an ancestor (at any point) of the passed node.
2899      * @param {Node} node
2900      * @return {Boolean}
2901      */
2902     contains : function(node){
2903         return node.isAncestor(this);
2904     },
2905
2906     /**
2907      * Returns true if the passed node is an ancestor (at any point) of this node.
2908      * @param {Node} node
2909      * @return {Boolean}
2910      */
2911     isAncestor : function(node){
2912         var p = this.parentNode;
2913         while(p){
2914             if(p == node){
2915                 return true;
2916             }
2917             p = p.parentNode;
2918         }
2919         return false;
2920     },
2921
2922     toString : function(){
2923         return "[Node"+(this.id?" "+this.id:"")+"]";
2924     }
2925 });/*
2926  * Based on:
2927  * Ext JS Library 1.1.1
2928  * Copyright(c) 2006-2007, Ext JS, LLC.
2929  *
2930  * Originally Released Under LGPL - original licence link has changed is not relivant.
2931  *
2932  * Fork - LGPL
2933  * <script type="text/javascript">
2934  */
2935  (function(){ 
2936 /**
2937  * @class Roo.Layer
2938  * @extends Roo.Element
2939  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2940  * automatic maintaining of shadow/shim positions.
2941  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2942  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2943  * you can pass a string with a CSS class name. False turns off the shadow.
2944  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2945  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2946  * @cfg {String} cls CSS class to add to the element
2947  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2948  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2949  * @constructor
2950  * @param {Object} config An object with config options.
2951  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2952  */
2953
2954 Roo.Layer = function(config, existingEl){
2955     config = config || {};
2956     var dh = Roo.DomHelper;
2957     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2958     if(existingEl){
2959         this.dom = Roo.getDom(existingEl);
2960     }
2961     if(!this.dom){
2962         var o = config.dh || {tag: "div", cls: "x-layer"};
2963         this.dom = dh.append(pel, o);
2964     }
2965     if(config.cls){
2966         this.addClass(config.cls);
2967     }
2968     this.constrain = config.constrain !== false;
2969     this.visibilityMode = Roo.Element.VISIBILITY;
2970     if(config.id){
2971         this.id = this.dom.id = config.id;
2972     }else{
2973         this.id = Roo.id(this.dom);
2974     }
2975     this.zindex = config.zindex || this.getZIndex();
2976     this.position("absolute", this.zindex);
2977     if(config.shadow){
2978         this.shadowOffset = config.shadowOffset || 4;
2979         this.shadow = new Roo.Shadow({
2980             offset : this.shadowOffset,
2981             mode : config.shadow
2982         });
2983     }else{
2984         this.shadowOffset = 0;
2985     }
2986     this.useShim = config.shim !== false && Roo.useShims;
2987     this.useDisplay = config.useDisplay;
2988     this.hide();
2989 };
2990
2991 var supr = Roo.Element.prototype;
2992
2993 // shims are shared among layer to keep from having 100 iframes
2994 var shims = [];
2995
2996 Roo.extend(Roo.Layer, Roo.Element, {
2997
2998     getZIndex : function(){
2999         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3000     },
3001
3002     getShim : function(){
3003         if(!this.useShim){
3004             return null;
3005         }
3006         if(this.shim){
3007             return this.shim;
3008         }
3009         var shim = shims.shift();
3010         if(!shim){
3011             shim = this.createShim();
3012             shim.enableDisplayMode('block');
3013             shim.dom.style.display = 'none';
3014             shim.dom.style.visibility = 'visible';
3015         }
3016         var pn = this.dom.parentNode;
3017         if(shim.dom.parentNode != pn){
3018             pn.insertBefore(shim.dom, this.dom);
3019         }
3020         shim.setStyle('z-index', this.getZIndex()-2);
3021         this.shim = shim;
3022         return shim;
3023     },
3024
3025     hideShim : function(){
3026         if(this.shim){
3027             this.shim.setDisplayed(false);
3028             shims.push(this.shim);
3029             delete this.shim;
3030         }
3031     },
3032
3033     disableShadow : function(){
3034         if(this.shadow){
3035             this.shadowDisabled = true;
3036             this.shadow.hide();
3037             this.lastShadowOffset = this.shadowOffset;
3038             this.shadowOffset = 0;
3039         }
3040     },
3041
3042     enableShadow : function(show){
3043         if(this.shadow){
3044             this.shadowDisabled = false;
3045             this.shadowOffset = this.lastShadowOffset;
3046             delete this.lastShadowOffset;
3047             if(show){
3048                 this.sync(true);
3049             }
3050         }
3051     },
3052
3053     // private
3054     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3055     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3056     sync : function(doShow){
3057         var sw = this.shadow;
3058         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3059             var sh = this.getShim();
3060
3061             var w = this.getWidth(),
3062                 h = this.getHeight();
3063
3064             var l = this.getLeft(true),
3065                 t = this.getTop(true);
3066
3067             if(sw && !this.shadowDisabled){
3068                 if(doShow && !sw.isVisible()){
3069                     sw.show(this);
3070                 }else{
3071                     sw.realign(l, t, w, h);
3072                 }
3073                 if(sh){
3074                     if(doShow){
3075                        sh.show();
3076                     }
3077                     // fit the shim behind the shadow, so it is shimmed too
3078                     var a = sw.adjusts, s = sh.dom.style;
3079                     s.left = (Math.min(l, l+a.l))+"px";
3080                     s.top = (Math.min(t, t+a.t))+"px";
3081                     s.width = (w+a.w)+"px";
3082                     s.height = (h+a.h)+"px";
3083                 }
3084             }else if(sh){
3085                 if(doShow){
3086                    sh.show();
3087                 }
3088                 sh.setSize(w, h);
3089                 sh.setLeftTop(l, t);
3090             }
3091             
3092         }
3093     },
3094
3095     // private
3096     destroy : function(){
3097         this.hideShim();
3098         if(this.shadow){
3099             this.shadow.hide();
3100         }
3101         this.removeAllListeners();
3102         var pn = this.dom.parentNode;
3103         if(pn){
3104             pn.removeChild(this.dom);
3105         }
3106         Roo.Element.uncache(this.id);
3107     },
3108
3109     remove : function(){
3110         this.destroy();
3111     },
3112
3113     // private
3114     beginUpdate : function(){
3115         this.updating = true;
3116     },
3117
3118     // private
3119     endUpdate : function(){
3120         this.updating = false;
3121         this.sync(true);
3122     },
3123
3124     // private
3125     hideUnders : function(negOffset){
3126         if(this.shadow){
3127             this.shadow.hide();
3128         }
3129         this.hideShim();
3130     },
3131
3132     // private
3133     constrainXY : function(){
3134         if(this.constrain){
3135             var vw = Roo.lib.Dom.getViewWidth(),
3136                 vh = Roo.lib.Dom.getViewHeight();
3137             var s = Roo.get(document).getScroll();
3138
3139             var xy = this.getXY();
3140             var x = xy[0], y = xy[1];   
3141             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3142             // only move it if it needs it
3143             var moved = false;
3144             // first validate right/bottom
3145             if((x + w) > vw+s.left){
3146                 x = vw - w - this.shadowOffset;
3147                 moved = true;
3148             }
3149             if((y + h) > vh+s.top){
3150                 y = vh - h - this.shadowOffset;
3151                 moved = true;
3152             }
3153             // then make sure top/left isn't negative
3154             if(x < s.left){
3155                 x = s.left;
3156                 moved = true;
3157             }
3158             if(y < s.top){
3159                 y = s.top;
3160                 moved = true;
3161             }
3162             if(moved){
3163                 if(this.avoidY){
3164                     var ay = this.avoidY;
3165                     if(y <= ay && (y+h) >= ay){
3166                         y = ay-h-5;   
3167                     }
3168                 }
3169                 xy = [x, y];
3170                 this.storeXY(xy);
3171                 supr.setXY.call(this, xy);
3172                 this.sync();
3173             }
3174         }
3175     },
3176
3177     isVisible : function(){
3178         return this.visible;    
3179     },
3180
3181     // private
3182     showAction : function(){
3183         this.visible = true; // track visibility to prevent getStyle calls
3184         if(this.useDisplay === true){
3185             this.setDisplayed("");
3186         }else if(this.lastXY){
3187             supr.setXY.call(this, this.lastXY);
3188         }else if(this.lastLT){
3189             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3190         }
3191     },
3192
3193     // private
3194     hideAction : function(){
3195         this.visible = false;
3196         if(this.useDisplay === true){
3197             this.setDisplayed(false);
3198         }else{
3199             this.setLeftTop(-10000,-10000);
3200         }
3201     },
3202
3203     // overridden Element method
3204     setVisible : function(v, a, d, c, e){
3205         if(v){
3206             this.showAction();
3207         }
3208         if(a && v){
3209             var cb = function(){
3210                 this.sync(true);
3211                 if(c){
3212                     c();
3213                 }
3214             }.createDelegate(this);
3215             supr.setVisible.call(this, true, true, d, cb, e);
3216         }else{
3217             if(!v){
3218                 this.hideUnders(true);
3219             }
3220             var cb = c;
3221             if(a){
3222                 cb = function(){
3223                     this.hideAction();
3224                     if(c){
3225                         c();
3226                     }
3227                 }.createDelegate(this);
3228             }
3229             supr.setVisible.call(this, v, a, d, cb, e);
3230             if(v){
3231                 this.sync(true);
3232             }else if(!a){
3233                 this.hideAction();
3234             }
3235         }
3236     },
3237
3238     storeXY : function(xy){
3239         delete this.lastLT;
3240         this.lastXY = xy;
3241     },
3242
3243     storeLeftTop : function(left, top){
3244         delete this.lastXY;
3245         this.lastLT = [left, top];
3246     },
3247
3248     // private
3249     beforeFx : function(){
3250         this.beforeAction();
3251         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3252     },
3253
3254     // private
3255     afterFx : function(){
3256         Roo.Layer.superclass.afterFx.apply(this, arguments);
3257         this.sync(this.isVisible());
3258     },
3259
3260     // private
3261     beforeAction : function(){
3262         if(!this.updating && this.shadow){
3263             this.shadow.hide();
3264         }
3265     },
3266
3267     // overridden Element method
3268     setLeft : function(left){
3269         this.storeLeftTop(left, this.getTop(true));
3270         supr.setLeft.apply(this, arguments);
3271         this.sync();
3272     },
3273
3274     setTop : function(top){
3275         this.storeLeftTop(this.getLeft(true), top);
3276         supr.setTop.apply(this, arguments);
3277         this.sync();
3278     },
3279
3280     setLeftTop : function(left, top){
3281         this.storeLeftTop(left, top);
3282         supr.setLeftTop.apply(this, arguments);
3283         this.sync();
3284     },
3285
3286     setXY : function(xy, a, d, c, e){
3287         this.fixDisplay();
3288         this.beforeAction();
3289         this.storeXY(xy);
3290         var cb = this.createCB(c);
3291         supr.setXY.call(this, xy, a, d, cb, e);
3292         if(!a){
3293             cb();
3294         }
3295     },
3296
3297     // private
3298     createCB : function(c){
3299         var el = this;
3300         return function(){
3301             el.constrainXY();
3302             el.sync(true);
3303             if(c){
3304                 c();
3305             }
3306         };
3307     },
3308
3309     // overridden Element method
3310     setX : function(x, a, d, c, e){
3311         this.setXY([x, this.getY()], a, d, c, e);
3312     },
3313
3314     // overridden Element method
3315     setY : function(y, a, d, c, e){
3316         this.setXY([this.getX(), y], a, d, c, e);
3317     },
3318
3319     // overridden Element method
3320     setSize : function(w, h, a, d, c, e){
3321         this.beforeAction();
3322         var cb = this.createCB(c);
3323         supr.setSize.call(this, w, h, a, d, cb, e);
3324         if(!a){
3325             cb();
3326         }
3327     },
3328
3329     // overridden Element method
3330     setWidth : function(w, a, d, c, e){
3331         this.beforeAction();
3332         var cb = this.createCB(c);
3333         supr.setWidth.call(this, w, a, d, cb, e);
3334         if(!a){
3335             cb();
3336         }
3337     },
3338
3339     // overridden Element method
3340     setHeight : function(h, a, d, c, e){
3341         this.beforeAction();
3342         var cb = this.createCB(c);
3343         supr.setHeight.call(this, h, a, d, cb, e);
3344         if(!a){
3345             cb();
3346         }
3347     },
3348
3349     // overridden Element method
3350     setBounds : function(x, y, w, h, a, d, c, e){
3351         this.beforeAction();
3352         var cb = this.createCB(c);
3353         if(!a){
3354             this.storeXY([x, y]);
3355             supr.setXY.call(this, [x, y]);
3356             supr.setSize.call(this, w, h, a, d, cb, e);
3357             cb();
3358         }else{
3359             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3360         }
3361         return this;
3362     },
3363     
3364     /**
3365      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3366      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3367      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3368      * @param {Number} zindex The new z-index to set
3369      * @return {this} The Layer
3370      */
3371     setZIndex : function(zindex){
3372         this.zindex = zindex;
3373         this.setStyle("z-index", zindex + 2);
3374         if(this.shadow){
3375             this.shadow.setZIndex(zindex + 1);
3376         }
3377         if(this.shim){
3378             this.shim.setStyle("z-index", zindex);
3379         }
3380     }
3381 });
3382 })();/*
3383  * Based on:
3384  * Ext JS Library 1.1.1
3385  * Copyright(c) 2006-2007, Ext JS, LLC.
3386  *
3387  * Originally Released Under LGPL - original licence link has changed is not relivant.
3388  *
3389  * Fork - LGPL
3390  * <script type="text/javascript">
3391  */
3392
3393
3394 /**
3395  * @class Roo.Shadow
3396  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3397  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3398  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3399  * @constructor
3400  * Create a new Shadow
3401  * @param {Object} config The config object
3402  */
3403 Roo.Shadow = function(config){
3404     Roo.apply(this, config);
3405     if(typeof this.mode != "string"){
3406         this.mode = this.defaultMode;
3407     }
3408     var o = this.offset, a = {h: 0};
3409     var rad = Math.floor(this.offset/2);
3410     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3411         case "drop":
3412             a.w = 0;
3413             a.l = a.t = o;
3414             a.t -= 1;
3415             if(Roo.isIE){
3416                 a.l -= this.offset + rad;
3417                 a.t -= this.offset + rad;
3418                 a.w -= rad;
3419                 a.h -= rad;
3420                 a.t += 1;
3421             }
3422         break;
3423         case "sides":
3424             a.w = (o*2);
3425             a.l = -o;
3426             a.t = o-1;
3427             if(Roo.isIE){
3428                 a.l -= (this.offset - rad);
3429                 a.t -= this.offset + rad;
3430                 a.l += 1;
3431                 a.w -= (this.offset - rad)*2;
3432                 a.w -= rad + 1;
3433                 a.h -= 1;
3434             }
3435         break;
3436         case "frame":
3437             a.w = a.h = (o*2);
3438             a.l = a.t = -o;
3439             a.t += 1;
3440             a.h -= 2;
3441             if(Roo.isIE){
3442                 a.l -= (this.offset - rad);
3443                 a.t -= (this.offset - rad);
3444                 a.l += 1;
3445                 a.w -= (this.offset + rad + 1);
3446                 a.h -= (this.offset + rad);
3447                 a.h += 1;
3448             }
3449         break;
3450     };
3451
3452     this.adjusts = a;
3453 };
3454
3455 Roo.Shadow.prototype = {
3456     /**
3457      * @cfg {String} mode
3458      * The shadow display mode.  Supports the following options:<br />
3459      * sides: Shadow displays on both sides and bottom only<br />
3460      * frame: Shadow displays equally on all four sides<br />
3461      * drop: Traditional bottom-right drop shadow (default)
3462      */
3463     /**
3464      * @cfg {String} offset
3465      * The number of pixels to offset the shadow from the element (defaults to 4)
3466      */
3467     offset: 4,
3468
3469     // private
3470     defaultMode: "drop",
3471
3472     /**
3473      * Displays the shadow under the target element
3474      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3475      */
3476     show : function(target){
3477         target = Roo.get(target);
3478         if(!this.el){
3479             this.el = Roo.Shadow.Pool.pull();
3480             if(this.el.dom.nextSibling != target.dom){
3481                 this.el.insertBefore(target);
3482             }
3483         }
3484         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3485         if(Roo.isIE){
3486             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3487         }
3488         this.realign(
3489             target.getLeft(true),
3490             target.getTop(true),
3491             target.getWidth(),
3492             target.getHeight()
3493         );
3494         this.el.dom.style.display = "block";
3495     },
3496
3497     /**
3498      * Returns true if the shadow is visible, else false
3499      */
3500     isVisible : function(){
3501         return this.el ? true : false;  
3502     },
3503
3504     /**
3505      * Direct alignment when values are already available. Show must be called at least once before
3506      * calling this method to ensure it is initialized.
3507      * @param {Number} left The target element left position
3508      * @param {Number} top The target element top position
3509      * @param {Number} width The target element width
3510      * @param {Number} height The target element height
3511      */
3512     realign : function(l, t, w, h){
3513         if(!this.el){
3514             return;
3515         }
3516         var a = this.adjusts, d = this.el.dom, s = d.style;
3517         var iea = 0;
3518         s.left = (l+a.l)+"px";
3519         s.top = (t+a.t)+"px";
3520         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3521  
3522         if(s.width != sws || s.height != shs){
3523             s.width = sws;
3524             s.height = shs;
3525             if(!Roo.isIE){
3526                 var cn = d.childNodes;
3527                 var sww = Math.max(0, (sw-12))+"px";
3528                 cn[0].childNodes[1].style.width = sww;
3529                 cn[1].childNodes[1].style.width = sww;
3530                 cn[2].childNodes[1].style.width = sww;
3531                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3532             }
3533         }
3534     },
3535
3536     /**
3537      * Hides this shadow
3538      */
3539     hide : function(){
3540         if(this.el){
3541             this.el.dom.style.display = "none";
3542             Roo.Shadow.Pool.push(this.el);
3543             delete this.el;
3544         }
3545     },
3546
3547     /**
3548      * Adjust the z-index of this shadow
3549      * @param {Number} zindex The new z-index
3550      */
3551     setZIndex : function(z){
3552         this.zIndex = z;
3553         if(this.el){
3554             this.el.setStyle("z-index", z);
3555         }
3556     }
3557 };
3558
3559 // Private utility class that manages the internal Shadow cache
3560 Roo.Shadow.Pool = function(){
3561     var p = [];
3562     var markup = Roo.isIE ?
3563                  '<div class="x-ie-shadow"></div>' :
3564                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3565     return {
3566         pull : function(){
3567             var sh = p.shift();
3568             if(!sh){
3569                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3570                 sh.autoBoxAdjust = false;
3571             }
3572             return sh;
3573         },
3574
3575         push : function(sh){
3576             p.push(sh);
3577         }
3578     };
3579 }();/*
3580  * Based on:
3581  * Ext JS Library 1.1.1
3582  * Copyright(c) 2006-2007, Ext JS, LLC.
3583  *
3584  * Originally Released Under LGPL - original licence link has changed is not relivant.
3585  *
3586  * Fork - LGPL
3587  * <script type="text/javascript">
3588  */
3589
3590
3591 /**
3592  * @class Roo.SplitBar
3593  * @extends Roo.util.Observable
3594  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3595  * <br><br>
3596  * Usage:
3597  * <pre><code>
3598 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3599                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3600 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3601 split.minSize = 100;
3602 split.maxSize = 600;
3603 split.animate = true;
3604 split.on('moved', splitterMoved);
3605 </code></pre>
3606  * @constructor
3607  * Create a new SplitBar
3608  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3609  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3610  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3611  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3612                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3613                         position of the SplitBar).
3614  */
3615 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3616     
3617     /** @private */
3618     this.el = Roo.get(dragElement, true);
3619     this.el.dom.unselectable = "on";
3620     /** @private */
3621     this.resizingEl = Roo.get(resizingElement, true);
3622
3623     /**
3624      * @private
3625      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3626      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3627      * @type Number
3628      */
3629     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3630     
3631     /**
3632      * The minimum size of the resizing element. (Defaults to 0)
3633      * @type Number
3634      */
3635     this.minSize = 0;
3636     
3637     /**
3638      * The maximum size of the resizing element. (Defaults to 2000)
3639      * @type Number
3640      */
3641     this.maxSize = 2000;
3642     
3643     /**
3644      * Whether to animate the transition to the new size
3645      * @type Boolean
3646      */
3647     this.animate = false;
3648     
3649     /**
3650      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3651      * @type Boolean
3652      */
3653     this.useShim = false;
3654     
3655     /** @private */
3656     this.shim = null;
3657     
3658     if(!existingProxy){
3659         /** @private */
3660         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3661     }else{
3662         this.proxy = Roo.get(existingProxy).dom;
3663     }
3664     /** @private */
3665     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3666     
3667     /** @private */
3668     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3669     
3670     /** @private */
3671     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3672     
3673     /** @private */
3674     this.dragSpecs = {};
3675     
3676     /**
3677      * @private The adapter to use to positon and resize elements
3678      */
3679     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3680     this.adapter.init(this);
3681     
3682     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3683         /** @private */
3684         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3685         this.el.addClass("x-splitbar-h");
3686     }else{
3687         /** @private */
3688         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3689         this.el.addClass("x-splitbar-v");
3690     }
3691     
3692     this.addEvents({
3693         /**
3694          * @event resize
3695          * Fires when the splitter is moved (alias for {@link #event-moved})
3696          * @param {Roo.SplitBar} this
3697          * @param {Number} newSize the new width or height
3698          */
3699         "resize" : true,
3700         /**
3701          * @event moved
3702          * Fires when the splitter is moved
3703          * @param {Roo.SplitBar} this
3704          * @param {Number} newSize the new width or height
3705          */
3706         "moved" : true,
3707         /**
3708          * @event beforeresize
3709          * Fires before the splitter is dragged
3710          * @param {Roo.SplitBar} this
3711          */
3712         "beforeresize" : true,
3713
3714         "beforeapply" : true
3715     });
3716
3717     Roo.util.Observable.call(this);
3718 };
3719
3720 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3721     onStartProxyDrag : function(x, y){
3722         this.fireEvent("beforeresize", this);
3723         if(!this.overlay){
3724             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3725             o.unselectable();
3726             o.enableDisplayMode("block");
3727             // all splitbars share the same overlay
3728             Roo.SplitBar.prototype.overlay = o;
3729         }
3730         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3731         this.overlay.show();
3732         Roo.get(this.proxy).setDisplayed("block");
3733         var size = this.adapter.getElementSize(this);
3734         this.activeMinSize = this.getMinimumSize();;
3735         this.activeMaxSize = this.getMaximumSize();;
3736         var c1 = size - this.activeMinSize;
3737         var c2 = Math.max(this.activeMaxSize - size, 0);
3738         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3739             this.dd.resetConstraints();
3740             this.dd.setXConstraint(
3741                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3742                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3743             );
3744             this.dd.setYConstraint(0, 0);
3745         }else{
3746             this.dd.resetConstraints();
3747             this.dd.setXConstraint(0, 0);
3748             this.dd.setYConstraint(
3749                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3750                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3751             );
3752          }
3753         this.dragSpecs.startSize = size;
3754         this.dragSpecs.startPoint = [x, y];
3755         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3756     },
3757     
3758     /** 
3759      * @private Called after the drag operation by the DDProxy
3760      */
3761     onEndProxyDrag : function(e){
3762         Roo.get(this.proxy).setDisplayed(false);
3763         var endPoint = Roo.lib.Event.getXY(e);
3764         if(this.overlay){
3765             this.overlay.hide();
3766         }
3767         var newSize;
3768         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3769             newSize = this.dragSpecs.startSize + 
3770                 (this.placement == Roo.SplitBar.LEFT ?
3771                     endPoint[0] - this.dragSpecs.startPoint[0] :
3772                     this.dragSpecs.startPoint[0] - endPoint[0]
3773                 );
3774         }else{
3775             newSize = this.dragSpecs.startSize + 
3776                 (this.placement == Roo.SplitBar.TOP ?
3777                     endPoint[1] - this.dragSpecs.startPoint[1] :
3778                     this.dragSpecs.startPoint[1] - endPoint[1]
3779                 );
3780         }
3781         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3782         if(newSize != this.dragSpecs.startSize){
3783             if(this.fireEvent('beforeapply', this, newSize) !== false){
3784                 this.adapter.setElementSize(this, newSize);
3785                 this.fireEvent("moved", this, newSize);
3786                 this.fireEvent("resize", this, newSize);
3787             }
3788         }
3789     },
3790     
3791     /**
3792      * Get the adapter this SplitBar uses
3793      * @return The adapter object
3794      */
3795     getAdapter : function(){
3796         return this.adapter;
3797     },
3798     
3799     /**
3800      * Set the adapter this SplitBar uses
3801      * @param {Object} adapter A SplitBar adapter object
3802      */
3803     setAdapter : function(adapter){
3804         this.adapter = adapter;
3805         this.adapter.init(this);
3806     },
3807     
3808     /**
3809      * Gets the minimum size for the resizing element
3810      * @return {Number} The minimum size
3811      */
3812     getMinimumSize : function(){
3813         return this.minSize;
3814     },
3815     
3816     /**
3817      * Sets the minimum size for the resizing element
3818      * @param {Number} minSize The minimum size
3819      */
3820     setMinimumSize : function(minSize){
3821         this.minSize = minSize;
3822     },
3823     
3824     /**
3825      * Gets the maximum size for the resizing element
3826      * @return {Number} The maximum size
3827      */
3828     getMaximumSize : function(){
3829         return this.maxSize;
3830     },
3831     
3832     /**
3833      * Sets the maximum size for the resizing element
3834      * @param {Number} maxSize The maximum size
3835      */
3836     setMaximumSize : function(maxSize){
3837         this.maxSize = maxSize;
3838     },
3839     
3840     /**
3841      * Sets the initialize size for the resizing element
3842      * @param {Number} size The initial size
3843      */
3844     setCurrentSize : function(size){
3845         var oldAnimate = this.animate;
3846         this.animate = false;
3847         this.adapter.setElementSize(this, size);
3848         this.animate = oldAnimate;
3849     },
3850     
3851     /**
3852      * Destroy this splitbar. 
3853      * @param {Boolean} removeEl True to remove the element
3854      */
3855     destroy : function(removeEl){
3856         if(this.shim){
3857             this.shim.remove();
3858         }
3859         this.dd.unreg();
3860         this.proxy.parentNode.removeChild(this.proxy);
3861         if(removeEl){
3862             this.el.remove();
3863         }
3864     }
3865 });
3866
3867 /**
3868  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3869  */
3870 Roo.SplitBar.createProxy = function(dir){
3871     var proxy = new Roo.Element(document.createElement("div"));
3872     proxy.unselectable();
3873     var cls = 'x-splitbar-proxy';
3874     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3875     document.body.appendChild(proxy.dom);
3876     return proxy.dom;
3877 };
3878
3879 /** 
3880  * @class Roo.SplitBar.BasicLayoutAdapter
3881  * Default Adapter. It assumes the splitter and resizing element are not positioned
3882  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3883  */
3884 Roo.SplitBar.BasicLayoutAdapter = function(){
3885 };
3886
3887 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3888     // do nothing for now
3889     init : function(s){
3890     
3891     },
3892     /**
3893      * Called before drag operations to get the current size of the resizing element. 
3894      * @param {Roo.SplitBar} s The SplitBar using this adapter
3895      */
3896      getElementSize : function(s){
3897         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3898             return s.resizingEl.getWidth();
3899         }else{
3900             return s.resizingEl.getHeight();
3901         }
3902     },
3903     
3904     /**
3905      * Called after drag operations to set the size of the resizing element.
3906      * @param {Roo.SplitBar} s The SplitBar using this adapter
3907      * @param {Number} newSize The new size to set
3908      * @param {Function} onComplete A function to be invoked when resizing is complete
3909      */
3910     setElementSize : function(s, newSize, onComplete){
3911         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3912             if(!s.animate){
3913                 s.resizingEl.setWidth(newSize);
3914                 if(onComplete){
3915                     onComplete(s, newSize);
3916                 }
3917             }else{
3918                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3919             }
3920         }else{
3921             
3922             if(!s.animate){
3923                 s.resizingEl.setHeight(newSize);
3924                 if(onComplete){
3925                     onComplete(s, newSize);
3926                 }
3927             }else{
3928                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3929             }
3930         }
3931     }
3932 };
3933
3934 /** 
3935  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3936  * @extends Roo.SplitBar.BasicLayoutAdapter
3937  * Adapter that  moves the splitter element to align with the resized sizing element. 
3938  * Used with an absolute positioned SplitBar.
3939  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3940  * document.body, make sure you assign an id to the body element.
3941  */
3942 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3943     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3944     this.container = Roo.get(container);
3945 };
3946
3947 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3948     init : function(s){
3949         this.basic.init(s);
3950     },
3951     
3952     getElementSize : function(s){
3953         return this.basic.getElementSize(s);
3954     },
3955     
3956     setElementSize : function(s, newSize, onComplete){
3957         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3958     },
3959     
3960     moveSplitter : function(s){
3961         var yes = Roo.SplitBar;
3962         switch(s.placement){
3963             case yes.LEFT:
3964                 s.el.setX(s.resizingEl.getRight());
3965                 break;
3966             case yes.RIGHT:
3967                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3968                 break;
3969             case yes.TOP:
3970                 s.el.setY(s.resizingEl.getBottom());
3971                 break;
3972             case yes.BOTTOM:
3973                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3974                 break;
3975         }
3976     }
3977 };
3978
3979 /**
3980  * Orientation constant - Create a vertical SplitBar
3981  * @static
3982  * @type Number
3983  */
3984 Roo.SplitBar.VERTICAL = 1;
3985
3986 /**
3987  * Orientation constant - Create a horizontal SplitBar
3988  * @static
3989  * @type Number
3990  */
3991 Roo.SplitBar.HORIZONTAL = 2;
3992
3993 /**
3994  * Placement constant - The resizing element is to the left of the splitter element
3995  * @static
3996  * @type Number
3997  */
3998 Roo.SplitBar.LEFT = 1;
3999
4000 /**
4001  * Placement constant - The resizing element is to the right of the splitter element
4002  * @static
4003  * @type Number
4004  */
4005 Roo.SplitBar.RIGHT = 2;
4006
4007 /**
4008  * Placement constant - The resizing element is positioned above the splitter element
4009  * @static
4010  * @type Number
4011  */
4012 Roo.SplitBar.TOP = 3;
4013
4014 /**
4015  * Placement constant - The resizing element is positioned under splitter element
4016  * @static
4017  * @type Number
4018  */
4019 Roo.SplitBar.BOTTOM = 4;
4020 /*
4021  * Based on:
4022  * Ext JS Library 1.1.1
4023  * Copyright(c) 2006-2007, Ext JS, LLC.
4024  *
4025  * Originally Released Under LGPL - original licence link has changed is not relivant.
4026  *
4027  * Fork - LGPL
4028  * <script type="text/javascript">
4029  */
4030
4031 /**
4032  * @class Roo.View
4033  * @extends Roo.util.Observable
4034  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4035  * This class also supports single and multi selection modes. <br>
4036  * Create a data model bound view:
4037  <pre><code>
4038  var store = new Roo.data.Store(...);
4039
4040  var view = new Roo.View({
4041     el : "my-element",
4042     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4043  
4044     singleSelect: true,
4045     selectedClass: "ydataview-selected",
4046     store: store
4047  });
4048
4049  // listen for node click?
4050  view.on("click", function(vw, index, node, e){
4051  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4052  });
4053
4054  // load XML data
4055  dataModel.load("foobar.xml");
4056  </code></pre>
4057  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4058  * <br><br>
4059  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4060  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4061  * 
4062  * Note: old style constructor is still suported (container, template, config)
4063  * 
4064  * @constructor
4065  * Create a new View
4066  * @param {Object} config The config object
4067  * 
4068  */
4069 Roo.View = function(config, depreciated_tpl, depreciated_config){
4070     
4071     this.parent = false;
4072     
4073     if (typeof(depreciated_tpl) == 'undefined') {
4074         // new way.. - universal constructor.
4075         Roo.apply(this, config);
4076         this.el  = Roo.get(this.el);
4077     } else {
4078         // old format..
4079         this.el  = Roo.get(config);
4080         this.tpl = depreciated_tpl;
4081         Roo.apply(this, depreciated_config);
4082     }
4083     this.wrapEl  = this.el.wrap().wrap();
4084     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4085     
4086     
4087     if(typeof(this.tpl) == "string"){
4088         this.tpl = new Roo.Template(this.tpl);
4089     } else {
4090         // support xtype ctors..
4091         this.tpl = new Roo.factory(this.tpl, Roo);
4092     }
4093     
4094     
4095     this.tpl.compile();
4096     
4097     /** @private */
4098     this.addEvents({
4099         /**
4100          * @event beforeclick
4101          * Fires before a click is processed. Returns false to cancel the default action.
4102          * @param {Roo.View} this
4103          * @param {Number} index The index of the target node
4104          * @param {HTMLElement} node The target node
4105          * @param {Roo.EventObject} e The raw event object
4106          */
4107             "beforeclick" : true,
4108         /**
4109          * @event click
4110          * Fires when a template node is clicked.
4111          * @param {Roo.View} this
4112          * @param {Number} index The index of the target node
4113          * @param {HTMLElement} node The target node
4114          * @param {Roo.EventObject} e The raw event object
4115          */
4116             "click" : true,
4117         /**
4118          * @event dblclick
4119          * Fires when a template node is double clicked.
4120          * @param {Roo.View} this
4121          * @param {Number} index The index of the target node
4122          * @param {HTMLElement} node The target node
4123          * @param {Roo.EventObject} e The raw event object
4124          */
4125             "dblclick" : true,
4126         /**
4127          * @event contextmenu
4128          * Fires when a template node is right clicked.
4129          * @param {Roo.View} this
4130          * @param {Number} index The index of the target node
4131          * @param {HTMLElement} node The target node
4132          * @param {Roo.EventObject} e The raw event object
4133          */
4134             "contextmenu" : true,
4135         /**
4136          * @event selectionchange
4137          * Fires when the selected nodes change.
4138          * @param {Roo.View} this
4139          * @param {Array} selections Array of the selected nodes
4140          */
4141             "selectionchange" : true,
4142     
4143         /**
4144          * @event beforeselect
4145          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4146          * @param {Roo.View} this
4147          * @param {HTMLElement} node The node to be selected
4148          * @param {Array} selections Array of currently selected nodes
4149          */
4150             "beforeselect" : true,
4151         /**
4152          * @event preparedata
4153          * Fires on every row to render, to allow you to change the data.
4154          * @param {Roo.View} this
4155          * @param {Object} data to be rendered (change this)
4156          */
4157           "preparedata" : true
4158           
4159           
4160         });
4161
4162
4163
4164     this.el.on({
4165         "click": this.onClick,
4166         "dblclick": this.onDblClick,
4167         "contextmenu": this.onContextMenu,
4168         scope:this
4169     });
4170
4171     this.selections = [];
4172     this.nodes = [];
4173     this.cmp = new Roo.CompositeElementLite([]);
4174     if(this.store){
4175         this.store = Roo.factory(this.store, Roo.data);
4176         this.setStore(this.store, true);
4177     }
4178     
4179     if ( this.footer && this.footer.xtype) {
4180            
4181          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4182         
4183         this.footer.dataSource = this.store;
4184         this.footer.container = fctr;
4185         this.footer = Roo.factory(this.footer, Roo);
4186         fctr.insertFirst(this.el);
4187         
4188         // this is a bit insane - as the paging toolbar seems to detach the el..
4189 //        dom.parentNode.parentNode.parentNode
4190          // they get detached?
4191     }
4192     
4193     
4194     Roo.View.superclass.constructor.call(this);
4195     
4196     
4197 };
4198
4199 Roo.extend(Roo.View, Roo.util.Observable, {
4200     
4201      /**
4202      * @cfg {Roo.data.Store} store Data store to load data from.
4203      */
4204     store : false,
4205     
4206     /**
4207      * @cfg {String|Roo.Element} el The container element.
4208      */
4209     el : '',
4210     
4211     /**
4212      * @cfg {String|Roo.Template} tpl The template used by this View 
4213      */
4214     tpl : false,
4215     /**
4216      * @cfg {String} dataName the named area of the template to use as the data area
4217      *                          Works with domtemplates roo-name="name"
4218      */
4219     dataName: false,
4220     /**
4221      * @cfg {String} selectedClass The css class to add to selected nodes
4222      */
4223     selectedClass : "x-view-selected",
4224      /**
4225      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4226      */
4227     emptyText : "",
4228     
4229     /**
4230      * @cfg {String} text to display on mask (default Loading)
4231      */
4232     mask : false,
4233     /**
4234      * @cfg {Boolean} multiSelect Allow multiple selection
4235      */
4236     multiSelect : false,
4237     /**
4238      * @cfg {Boolean} singleSelect Allow single selection
4239      */
4240     singleSelect:  false,
4241     
4242     /**
4243      * @cfg {Boolean} toggleSelect - selecting 
4244      */
4245     toggleSelect : false,
4246     
4247     /**
4248      * @cfg {Boolean} tickable - selecting 
4249      */
4250     tickable : false,
4251     
4252     /**
4253      * Returns the element this view is bound to.
4254      * @return {Roo.Element}
4255      */
4256     getEl : function(){
4257         return this.wrapEl;
4258     },
4259     
4260     
4261
4262     /**
4263      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4264      */
4265     refresh : function(){
4266         //Roo.log('refresh');
4267         var t = this.tpl;
4268         
4269         // if we are using something like 'domtemplate', then
4270         // the what gets used is:
4271         // t.applySubtemplate(NAME, data, wrapping data..)
4272         // the outer template then get' applied with
4273         //     the store 'extra data'
4274         // and the body get's added to the
4275         //      roo-name="data" node?
4276         //      <span class='roo-tpl-{name}'></span> ?????
4277         
4278         
4279         
4280         this.clearSelections();
4281         this.el.update("");
4282         var html = [];
4283         var records = this.store.getRange();
4284         if(records.length < 1) {
4285             
4286             // is this valid??  = should it render a template??
4287             
4288             this.el.update(this.emptyText);
4289             return;
4290         }
4291         var el = this.el;
4292         if (this.dataName) {
4293             this.el.update(t.apply(this.store.meta)); //????
4294             el = this.el.child('.roo-tpl-' + this.dataName);
4295         }
4296         
4297         for(var i = 0, len = records.length; i < len; i++){
4298             var data = this.prepareData(records[i].data, i, records[i]);
4299             this.fireEvent("preparedata", this, data, i, records[i]);
4300             
4301             var d = Roo.apply({}, data);
4302             
4303             if(this.tickable){
4304                 Roo.apply(d, {'roo-id' : Roo.id()});
4305                 
4306                 var _this = this;
4307             
4308                 Roo.each(this.parent.item, function(item){
4309                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4310                         return;
4311                     }
4312                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4313                 });
4314             }
4315             
4316             html[html.length] = Roo.util.Format.trim(
4317                 this.dataName ?
4318                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4319                     t.apply(d)
4320             );
4321         }
4322         
4323         
4324         
4325         el.update(html.join(""));
4326         this.nodes = el.dom.childNodes;
4327         this.updateIndexes(0);
4328     },
4329     
4330
4331     /**
4332      * Function to override to reformat the data that is sent to
4333      * the template for each node.
4334      * DEPRICATED - use the preparedata event handler.
4335      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4336      * a JSON object for an UpdateManager bound view).
4337      */
4338     prepareData : function(data, index, record)
4339     {
4340         this.fireEvent("preparedata", this, data, index, record);
4341         return data;
4342     },
4343
4344     onUpdate : function(ds, record){
4345         // Roo.log('on update');   
4346         this.clearSelections();
4347         var index = this.store.indexOf(record);
4348         var n = this.nodes[index];
4349         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4350         n.parentNode.removeChild(n);
4351         this.updateIndexes(index, index);
4352     },
4353
4354     
4355     
4356 // --------- FIXME     
4357     onAdd : function(ds, records, index)
4358     {
4359         //Roo.log(['on Add', ds, records, index] );        
4360         this.clearSelections();
4361         if(this.nodes.length == 0){
4362             this.refresh();
4363             return;
4364         }
4365         var n = this.nodes[index];
4366         for(var i = 0, len = records.length; i < len; i++){
4367             var d = this.prepareData(records[i].data, i, records[i]);
4368             if(n){
4369                 this.tpl.insertBefore(n, d);
4370             }else{
4371                 
4372                 this.tpl.append(this.el, d);
4373             }
4374         }
4375         this.updateIndexes(index);
4376     },
4377
4378     onRemove : function(ds, record, index){
4379        // Roo.log('onRemove');
4380         this.clearSelections();
4381         var el = this.dataName  ?
4382             this.el.child('.roo-tpl-' + this.dataName) :
4383             this.el; 
4384         
4385         el.dom.removeChild(this.nodes[index]);
4386         this.updateIndexes(index);
4387     },
4388
4389     /**
4390      * Refresh an individual node.
4391      * @param {Number} index
4392      */
4393     refreshNode : function(index){
4394         this.onUpdate(this.store, this.store.getAt(index));
4395     },
4396
4397     updateIndexes : function(startIndex, endIndex){
4398         var ns = this.nodes;
4399         startIndex = startIndex || 0;
4400         endIndex = endIndex || ns.length - 1;
4401         for(var i = startIndex; i <= endIndex; i++){
4402             ns[i].nodeIndex = i;
4403         }
4404     },
4405
4406     /**
4407      * Changes the data store this view uses and refresh the view.
4408      * @param {Store} store
4409      */
4410     setStore : function(store, initial){
4411         if(!initial && this.store){
4412             this.store.un("datachanged", this.refresh);
4413             this.store.un("add", this.onAdd);
4414             this.store.un("remove", this.onRemove);
4415             this.store.un("update", this.onUpdate);
4416             this.store.un("clear", this.refresh);
4417             this.store.un("beforeload", this.onBeforeLoad);
4418             this.store.un("load", this.onLoad);
4419             this.store.un("loadexception", this.onLoad);
4420         }
4421         if(store){
4422           
4423             store.on("datachanged", this.refresh, this);
4424             store.on("add", this.onAdd, this);
4425             store.on("remove", this.onRemove, this);
4426             store.on("update", this.onUpdate, this);
4427             store.on("clear", this.refresh, this);
4428             store.on("beforeload", this.onBeforeLoad, this);
4429             store.on("load", this.onLoad, this);
4430             store.on("loadexception", this.onLoad, this);
4431         }
4432         
4433         if(store){
4434             this.refresh();
4435         }
4436     },
4437     /**
4438      * onbeforeLoad - masks the loading area.
4439      *
4440      */
4441     onBeforeLoad : function(store,opts)
4442     {
4443          //Roo.log('onBeforeLoad');   
4444         if (!opts.add) {
4445             this.el.update("");
4446         }
4447         this.el.mask(this.mask ? this.mask : "Loading" ); 
4448     },
4449     onLoad : function ()
4450     {
4451         this.el.unmask();
4452     },
4453     
4454
4455     /**
4456      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4457      * @param {HTMLElement} node
4458      * @return {HTMLElement} The template node
4459      */
4460     findItemFromChild : function(node){
4461         var el = this.dataName  ?
4462             this.el.child('.roo-tpl-' + this.dataName,true) :
4463             this.el.dom; 
4464         
4465         if(!node || node.parentNode == el){
4466                     return node;
4467             }
4468             var p = node.parentNode;
4469             while(p && p != el){
4470             if(p.parentNode == el){
4471                 return p;
4472             }
4473             p = p.parentNode;
4474         }
4475             return null;
4476     },
4477
4478     /** @ignore */
4479     onClick : function(e){
4480         var item = this.findItemFromChild(e.getTarget());
4481         if(item){
4482             var index = this.indexOf(item);
4483             if(this.onItemClick(item, index, e) !== false){
4484                 this.fireEvent("click", this, index, item, e);
4485             }
4486         }else{
4487             this.clearSelections();
4488         }
4489     },
4490
4491     /** @ignore */
4492     onContextMenu : function(e){
4493         var item = this.findItemFromChild(e.getTarget());
4494         if(item){
4495             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4496         }
4497     },
4498
4499     /** @ignore */
4500     onDblClick : function(e){
4501         var item = this.findItemFromChild(e.getTarget());
4502         if(item){
4503             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4504         }
4505     },
4506
4507     onItemClick : function(item, index, e)
4508     {
4509         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4510             return false;
4511         }
4512         if (this.toggleSelect) {
4513             var m = this.isSelected(item) ? 'unselect' : 'select';
4514             //Roo.log(m);
4515             var _t = this;
4516             _t[m](item, true, false);
4517             return true;
4518         }
4519         if(this.multiSelect || this.singleSelect){
4520             if(this.multiSelect && e.shiftKey && this.lastSelection){
4521                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4522             }else{
4523                 this.select(item, this.multiSelect && e.ctrlKey);
4524                 this.lastSelection = item;
4525             }
4526             
4527             if(!this.tickable){
4528                 e.preventDefault();
4529             }
4530             
4531         }
4532         return true;
4533     },
4534
4535     /**
4536      * Get the number of selected nodes.
4537      * @return {Number}
4538      */
4539     getSelectionCount : function(){
4540         return this.selections.length;
4541     },
4542
4543     /**
4544      * Get the currently selected nodes.
4545      * @return {Array} An array of HTMLElements
4546      */
4547     getSelectedNodes : function(){
4548         return this.selections;
4549     },
4550
4551     /**
4552      * Get the indexes of the selected nodes.
4553      * @return {Array}
4554      */
4555     getSelectedIndexes : function(){
4556         var indexes = [], s = this.selections;
4557         for(var i = 0, len = s.length; i < len; i++){
4558             indexes.push(s[i].nodeIndex);
4559         }
4560         return indexes;
4561     },
4562
4563     /**
4564      * Clear all selections
4565      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4566      */
4567     clearSelections : function(suppressEvent){
4568         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4569             this.cmp.elements = this.selections;
4570             this.cmp.removeClass(this.selectedClass);
4571             this.selections = [];
4572             if(!suppressEvent){
4573                 this.fireEvent("selectionchange", this, this.selections);
4574             }
4575         }
4576     },
4577
4578     /**
4579      * Returns true if the passed node is selected
4580      * @param {HTMLElement/Number} node The node or node index
4581      * @return {Boolean}
4582      */
4583     isSelected : function(node){
4584         var s = this.selections;
4585         if(s.length < 1){
4586             return false;
4587         }
4588         node = this.getNode(node);
4589         return s.indexOf(node) !== -1;
4590     },
4591
4592     /**
4593      * Selects nodes.
4594      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4595      * @param {Boolean} keepExisting (optional) true to keep existing selections
4596      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4597      */
4598     select : function(nodeInfo, keepExisting, suppressEvent){
4599         if(nodeInfo instanceof Array){
4600             if(!keepExisting){
4601                 this.clearSelections(true);
4602             }
4603             for(var i = 0, len = nodeInfo.length; i < len; i++){
4604                 this.select(nodeInfo[i], true, true);
4605             }
4606             return;
4607         } 
4608         var node = this.getNode(nodeInfo);
4609         if(!node || this.isSelected(node)){
4610             return; // already selected.
4611         }
4612         if(!keepExisting){
4613             this.clearSelections(true);
4614         }
4615         
4616         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4617             Roo.fly(node).addClass(this.selectedClass);
4618             this.selections.push(node);
4619             if(!suppressEvent){
4620                 this.fireEvent("selectionchange", this, this.selections);
4621             }
4622         }
4623         
4624         
4625     },
4626       /**
4627      * Unselects nodes.
4628      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4629      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4630      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4631      */
4632     unselect : function(nodeInfo, keepExisting, suppressEvent)
4633     {
4634         if(nodeInfo instanceof Array){
4635             Roo.each(this.selections, function(s) {
4636                 this.unselect(s, nodeInfo);
4637             }, this);
4638             return;
4639         }
4640         var node = this.getNode(nodeInfo);
4641         if(!node || !this.isSelected(node)){
4642             //Roo.log("not selected");
4643             return; // not selected.
4644         }
4645         // fireevent???
4646         var ns = [];
4647         Roo.each(this.selections, function(s) {
4648             if (s == node ) {
4649                 Roo.fly(node).removeClass(this.selectedClass);
4650
4651                 return;
4652             }
4653             ns.push(s);
4654         },this);
4655         
4656         this.selections= ns;
4657         this.fireEvent("selectionchange", this, this.selections);
4658     },
4659
4660     /**
4661      * Gets a template node.
4662      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4663      * @return {HTMLElement} The node or null if it wasn't found
4664      */
4665     getNode : function(nodeInfo){
4666         if(typeof nodeInfo == "string"){
4667             return document.getElementById(nodeInfo);
4668         }else if(typeof nodeInfo == "number"){
4669             return this.nodes[nodeInfo];
4670         }
4671         return nodeInfo;
4672     },
4673
4674     /**
4675      * Gets a range template nodes.
4676      * @param {Number} startIndex
4677      * @param {Number} endIndex
4678      * @return {Array} An array of nodes
4679      */
4680     getNodes : function(start, end){
4681         var ns = this.nodes;
4682         start = start || 0;
4683         end = typeof end == "undefined" ? ns.length - 1 : end;
4684         var nodes = [];
4685         if(start <= end){
4686             for(var i = start; i <= end; i++){
4687                 nodes.push(ns[i]);
4688             }
4689         } else{
4690             for(var i = start; i >= end; i--){
4691                 nodes.push(ns[i]);
4692             }
4693         }
4694         return nodes;
4695     },
4696
4697     /**
4698      * Finds the index of the passed node
4699      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4700      * @return {Number} The index of the node or -1
4701      */
4702     indexOf : function(node){
4703         node = this.getNode(node);
4704         if(typeof node.nodeIndex == "number"){
4705             return node.nodeIndex;
4706         }
4707         var ns = this.nodes;
4708         for(var i = 0, len = ns.length; i < len; i++){
4709             if(ns[i] == node){
4710                 return i;
4711             }
4712         }
4713         return -1;
4714     }
4715 });
4716 /*
4717  * Based on:
4718  * Ext JS Library 1.1.1
4719  * Copyright(c) 2006-2007, Ext JS, LLC.
4720  *
4721  * Originally Released Under LGPL - original licence link has changed is not relivant.
4722  *
4723  * Fork - LGPL
4724  * <script type="text/javascript">
4725  */
4726
4727 /**
4728  * @class Roo.JsonView
4729  * @extends Roo.View
4730  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4731 <pre><code>
4732 var view = new Roo.JsonView({
4733     container: "my-element",
4734     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4735     multiSelect: true, 
4736     jsonRoot: "data" 
4737 });
4738
4739 // listen for node click?
4740 view.on("click", function(vw, index, node, e){
4741     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4742 });
4743
4744 // direct load of JSON data
4745 view.load("foobar.php");
4746
4747 // Example from my blog list
4748 var tpl = new Roo.Template(
4749     '&lt;div class="entry"&gt;' +
4750     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4751     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4752     "&lt;/div&gt;&lt;hr /&gt;"
4753 );
4754
4755 var moreView = new Roo.JsonView({
4756     container :  "entry-list", 
4757     template : tpl,
4758     jsonRoot: "posts"
4759 });
4760 moreView.on("beforerender", this.sortEntries, this);
4761 moreView.load({
4762     url: "/blog/get-posts.php",
4763     params: "allposts=true",
4764     text: "Loading Blog Entries..."
4765 });
4766 </code></pre>
4767
4768 * Note: old code is supported with arguments : (container, template, config)
4769
4770
4771  * @constructor
4772  * Create a new JsonView
4773  * 
4774  * @param {Object} config The config object
4775  * 
4776  */
4777 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4778     
4779     
4780     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4781
4782     var um = this.el.getUpdateManager();
4783     um.setRenderer(this);
4784     um.on("update", this.onLoad, this);
4785     um.on("failure", this.onLoadException, this);
4786
4787     /**
4788      * @event beforerender
4789      * Fires before rendering of the downloaded JSON data.
4790      * @param {Roo.JsonView} this
4791      * @param {Object} data The JSON data loaded
4792      */
4793     /**
4794      * @event load
4795      * Fires when data is loaded.
4796      * @param {Roo.JsonView} this
4797      * @param {Object} data The JSON data loaded
4798      * @param {Object} response The raw Connect response object
4799      */
4800     /**
4801      * @event loadexception
4802      * Fires when loading fails.
4803      * @param {Roo.JsonView} this
4804      * @param {Object} response The raw Connect response object
4805      */
4806     this.addEvents({
4807         'beforerender' : true,
4808         'load' : true,
4809         'loadexception' : true
4810     });
4811 };
4812 Roo.extend(Roo.JsonView, Roo.View, {
4813     /**
4814      * @type {String} The root property in the loaded JSON object that contains the data
4815      */
4816     jsonRoot : "",
4817
4818     /**
4819      * Refreshes the view.
4820      */
4821     refresh : function(){
4822         this.clearSelections();
4823         this.el.update("");
4824         var html = [];
4825         var o = this.jsonData;
4826         if(o && o.length > 0){
4827             for(var i = 0, len = o.length; i < len; i++){
4828                 var data = this.prepareData(o[i], i, o);
4829                 html[html.length] = this.tpl.apply(data);
4830             }
4831         }else{
4832             html.push(this.emptyText);
4833         }
4834         this.el.update(html.join(""));
4835         this.nodes = this.el.dom.childNodes;
4836         this.updateIndexes(0);
4837     },
4838
4839     /**
4840      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4841      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4842      <pre><code>
4843      view.load({
4844          url: "your-url.php",
4845          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4846          callback: yourFunction,
4847          scope: yourObject, //(optional scope)
4848          discardUrl: false,
4849          nocache: false,
4850          text: "Loading...",
4851          timeout: 30,
4852          scripts: false
4853      });
4854      </code></pre>
4855      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4856      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4857      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4858      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4859      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4860      */
4861     load : function(){
4862         var um = this.el.getUpdateManager();
4863         um.update.apply(um, arguments);
4864     },
4865
4866     // note - render is a standard framework call...
4867     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4868     render : function(el, response){
4869         
4870         this.clearSelections();
4871         this.el.update("");
4872         var o;
4873         try{
4874             if (response != '') {
4875                 o = Roo.util.JSON.decode(response.responseText);
4876                 if(this.jsonRoot){
4877                     
4878                     o = o[this.jsonRoot];
4879                 }
4880             }
4881         } catch(e){
4882         }
4883         /**
4884          * The current JSON data or null
4885          */
4886         this.jsonData = o;
4887         this.beforeRender();
4888         this.refresh();
4889     },
4890
4891 /**
4892  * Get the number of records in the current JSON dataset
4893  * @return {Number}
4894  */
4895     getCount : function(){
4896         return this.jsonData ? this.jsonData.length : 0;
4897     },
4898
4899 /**
4900  * Returns the JSON object for the specified node(s)
4901  * @param {HTMLElement/Array} node The node or an array of nodes
4902  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4903  * you get the JSON object for the node
4904  */
4905     getNodeData : function(node){
4906         if(node instanceof Array){
4907             var data = [];
4908             for(var i = 0, len = node.length; i < len; i++){
4909                 data.push(this.getNodeData(node[i]));
4910             }
4911             return data;
4912         }
4913         return this.jsonData[this.indexOf(node)] || null;
4914     },
4915
4916     beforeRender : function(){
4917         this.snapshot = this.jsonData;
4918         if(this.sortInfo){
4919             this.sort.apply(this, this.sortInfo);
4920         }
4921         this.fireEvent("beforerender", this, this.jsonData);
4922     },
4923
4924     onLoad : function(el, o){
4925         this.fireEvent("load", this, this.jsonData, o);
4926     },
4927
4928     onLoadException : function(el, o){
4929         this.fireEvent("loadexception", this, o);
4930     },
4931
4932 /**
4933  * Filter the data by a specific property.
4934  * @param {String} property A property on your JSON objects
4935  * @param {String/RegExp} value Either string that the property values
4936  * should start with, or a RegExp to test against the property
4937  */
4938     filter : function(property, value){
4939         if(this.jsonData){
4940             var data = [];
4941             var ss = this.snapshot;
4942             if(typeof value == "string"){
4943                 var vlen = value.length;
4944                 if(vlen == 0){
4945                     this.clearFilter();
4946                     return;
4947                 }
4948                 value = value.toLowerCase();
4949                 for(var i = 0, len = ss.length; i < len; i++){
4950                     var o = ss[i];
4951                     if(o[property].substr(0, vlen).toLowerCase() == value){
4952                         data.push(o);
4953                     }
4954                 }
4955             } else if(value.exec){ // regex?
4956                 for(var i = 0, len = ss.length; i < len; i++){
4957                     var o = ss[i];
4958                     if(value.test(o[property])){
4959                         data.push(o);
4960                     }
4961                 }
4962             } else{
4963                 return;
4964             }
4965             this.jsonData = data;
4966             this.refresh();
4967         }
4968     },
4969
4970 /**
4971  * Filter by a function. The passed function will be called with each
4972  * object in the current dataset. If the function returns true the value is kept,
4973  * otherwise it is filtered.
4974  * @param {Function} fn
4975  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4976  */
4977     filterBy : function(fn, scope){
4978         if(this.jsonData){
4979             var data = [];
4980             var ss = this.snapshot;
4981             for(var i = 0, len = ss.length; i < len; i++){
4982                 var o = ss[i];
4983                 if(fn.call(scope || this, o)){
4984                     data.push(o);
4985                 }
4986             }
4987             this.jsonData = data;
4988             this.refresh();
4989         }
4990     },
4991
4992 /**
4993  * Clears the current filter.
4994  */
4995     clearFilter : function(){
4996         if(this.snapshot && this.jsonData != this.snapshot){
4997             this.jsonData = this.snapshot;
4998             this.refresh();
4999         }
5000     },
5001
5002
5003 /**
5004  * Sorts the data for this view and refreshes it.
5005  * @param {String} property A property on your JSON objects to sort on
5006  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5007  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5008  */
5009     sort : function(property, dir, sortType){
5010         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5011         if(this.jsonData){
5012             var p = property;
5013             var dsc = dir && dir.toLowerCase() == "desc";
5014             var f = function(o1, o2){
5015                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5016                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5017                 ;
5018                 if(v1 < v2){
5019                     return dsc ? +1 : -1;
5020                 } else if(v1 > v2){
5021                     return dsc ? -1 : +1;
5022                 } else{
5023                     return 0;
5024                 }
5025             };
5026             this.jsonData.sort(f);
5027             this.refresh();
5028             if(this.jsonData != this.snapshot){
5029                 this.snapshot.sort(f);
5030             }
5031         }
5032     }
5033 });/*
5034  * Based on:
5035  * Ext JS Library 1.1.1
5036  * Copyright(c) 2006-2007, Ext JS, LLC.
5037  *
5038  * Originally Released Under LGPL - original licence link has changed is not relivant.
5039  *
5040  * Fork - LGPL
5041  * <script type="text/javascript">
5042  */
5043  
5044
5045 /**
5046  * @class Roo.ColorPalette
5047  * @extends Roo.Component
5048  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5049  * Here's an example of typical usage:
5050  * <pre><code>
5051 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5052 cp.render('my-div');
5053
5054 cp.on('select', function(palette, selColor){
5055     // do something with selColor
5056 });
5057 </code></pre>
5058  * @constructor
5059  * Create a new ColorPalette
5060  * @param {Object} config The config object
5061  */
5062 Roo.ColorPalette = function(config){
5063     Roo.ColorPalette.superclass.constructor.call(this, config);
5064     this.addEvents({
5065         /**
5066              * @event select
5067              * Fires when a color is selected
5068              * @param {ColorPalette} this
5069              * @param {String} color The 6-digit color hex code (without the # symbol)
5070              */
5071         select: true
5072     });
5073
5074     if(this.handler){
5075         this.on("select", this.handler, this.scope, true);
5076     }
5077 };
5078 Roo.extend(Roo.ColorPalette, Roo.Component, {
5079     /**
5080      * @cfg {String} itemCls
5081      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5082      */
5083     itemCls : "x-color-palette",
5084     /**
5085      * @cfg {String} value
5086      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5087      * the hex codes are case-sensitive.
5088      */
5089     value : null,
5090     clickEvent:'click',
5091     // private
5092     ctype: "Roo.ColorPalette",
5093
5094     /**
5095      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5096      */
5097     allowReselect : false,
5098
5099     /**
5100      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5101      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5102      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5103      * of colors with the width setting until the box is symmetrical.</p>
5104      * <p>You can override individual colors if needed:</p>
5105      * <pre><code>
5106 var cp = new Roo.ColorPalette();
5107 cp.colors[0] = "FF0000";  // change the first box to red
5108 </code></pre>
5109
5110 Or you can provide a custom array of your own for complete control:
5111 <pre><code>
5112 var cp = new Roo.ColorPalette();
5113 cp.colors = ["000000", "993300", "333300"];
5114 </code></pre>
5115      * @type Array
5116      */
5117     colors : [
5118         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5119         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5120         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5121         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5122         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5123     ],
5124
5125     // private
5126     onRender : function(container, position){
5127         var t = new Roo.MasterTemplate(
5128             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5129         );
5130         var c = this.colors;
5131         for(var i = 0, len = c.length; i < len; i++){
5132             t.add([c[i]]);
5133         }
5134         var el = document.createElement("div");
5135         el.className = this.itemCls;
5136         t.overwrite(el);
5137         container.dom.insertBefore(el, position);
5138         this.el = Roo.get(el);
5139         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5140         if(this.clickEvent != 'click'){
5141             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5142         }
5143     },
5144
5145     // private
5146     afterRender : function(){
5147         Roo.ColorPalette.superclass.afterRender.call(this);
5148         if(this.value){
5149             var s = this.value;
5150             this.value = null;
5151             this.select(s);
5152         }
5153     },
5154
5155     // private
5156     handleClick : function(e, t){
5157         e.preventDefault();
5158         if(!this.disabled){
5159             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5160             this.select(c.toUpperCase());
5161         }
5162     },
5163
5164     /**
5165      * Selects the specified color in the palette (fires the select event)
5166      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5167      */
5168     select : function(color){
5169         color = color.replace("#", "");
5170         if(color != this.value || this.allowReselect){
5171             var el = this.el;
5172             if(this.value){
5173                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5174             }
5175             el.child("a.color-"+color).addClass("x-color-palette-sel");
5176             this.value = color;
5177             this.fireEvent("select", this, color);
5178         }
5179     }
5180 });/*
5181  * Based on:
5182  * Ext JS Library 1.1.1
5183  * Copyright(c) 2006-2007, Ext JS, LLC.
5184  *
5185  * Originally Released Under LGPL - original licence link has changed is not relivant.
5186  *
5187  * Fork - LGPL
5188  * <script type="text/javascript">
5189  */
5190  
5191 /**
5192  * @class Roo.DatePicker
5193  * @extends Roo.Component
5194  * Simple date picker class.
5195  * @constructor
5196  * Create a new DatePicker
5197  * @param {Object} config The config object
5198  */
5199 Roo.DatePicker = function(config){
5200     Roo.DatePicker.superclass.constructor.call(this, config);
5201
5202     this.value = config && config.value ?
5203                  config.value.clearTime() : new Date().clearTime();
5204
5205     this.addEvents({
5206         /**
5207              * @event select
5208              * Fires when a date is selected
5209              * @param {DatePicker} this
5210              * @param {Date} date The selected date
5211              */
5212         'select': true,
5213         /**
5214              * @event monthchange
5215              * Fires when the displayed month changes 
5216              * @param {DatePicker} this
5217              * @param {Date} date The selected month
5218              */
5219         'monthchange': true
5220     });
5221
5222     if(this.handler){
5223         this.on("select", this.handler,  this.scope || this);
5224     }
5225     // build the disabledDatesRE
5226     if(!this.disabledDatesRE && this.disabledDates){
5227         var dd = this.disabledDates;
5228         var re = "(?:";
5229         for(var i = 0; i < dd.length; i++){
5230             re += dd[i];
5231             if(i != dd.length-1) {
5232                 re += "|";
5233             }
5234         }
5235         this.disabledDatesRE = new RegExp(re + ")");
5236     }
5237 };
5238
5239 Roo.extend(Roo.DatePicker, Roo.Component, {
5240     /**
5241      * @cfg {String} todayText
5242      * The text to display on the button that selects the current date (defaults to "Today")
5243      */
5244     todayText : "Today",
5245     /**
5246      * @cfg {String} okText
5247      * The text to display on the ok button
5248      */
5249     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5250     /**
5251      * @cfg {String} cancelText
5252      * The text to display on the cancel button
5253      */
5254     cancelText : "Cancel",
5255     /**
5256      * @cfg {String} todayTip
5257      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5258      */
5259     todayTip : "{0} (Spacebar)",
5260     /**
5261      * @cfg {Date} minDate
5262      * Minimum allowable date (JavaScript date object, defaults to null)
5263      */
5264     minDate : null,
5265     /**
5266      * @cfg {Date} maxDate
5267      * Maximum allowable date (JavaScript date object, defaults to null)
5268      */
5269     maxDate : null,
5270     /**
5271      * @cfg {String} minText
5272      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5273      */
5274     minText : "This date is before the minimum date",
5275     /**
5276      * @cfg {String} maxText
5277      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5278      */
5279     maxText : "This date is after the maximum date",
5280     /**
5281      * @cfg {String} format
5282      * The default date format string which can be overriden for localization support.  The format must be
5283      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5284      */
5285     format : "m/d/y",
5286     /**
5287      * @cfg {Array} disabledDays
5288      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5289      */
5290     disabledDays : null,
5291     /**
5292      * @cfg {String} disabledDaysText
5293      * The tooltip to display when the date falls on a disabled day (defaults to "")
5294      */
5295     disabledDaysText : "",
5296     /**
5297      * @cfg {RegExp} disabledDatesRE
5298      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5299      */
5300     disabledDatesRE : null,
5301     /**
5302      * @cfg {String} disabledDatesText
5303      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5304      */
5305     disabledDatesText : "",
5306     /**
5307      * @cfg {Boolean} constrainToViewport
5308      * True to constrain the date picker to the viewport (defaults to true)
5309      */
5310     constrainToViewport : true,
5311     /**
5312      * @cfg {Array} monthNames
5313      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5314      */
5315     monthNames : Date.monthNames,
5316     /**
5317      * @cfg {Array} dayNames
5318      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5319      */
5320     dayNames : Date.dayNames,
5321     /**
5322      * @cfg {String} nextText
5323      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5324      */
5325     nextText: 'Next Month (Control+Right)',
5326     /**
5327      * @cfg {String} prevText
5328      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5329      */
5330     prevText: 'Previous Month (Control+Left)',
5331     /**
5332      * @cfg {String} monthYearText
5333      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5334      */
5335     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5336     /**
5337      * @cfg {Number} startDay
5338      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5339      */
5340     startDay : 0,
5341     /**
5342      * @cfg {Bool} showClear
5343      * Show a clear button (usefull for date form elements that can be blank.)
5344      */
5345     
5346     showClear: false,
5347     
5348     /**
5349      * Sets the value of the date field
5350      * @param {Date} value The date to set
5351      */
5352     setValue : function(value){
5353         var old = this.value;
5354         
5355         if (typeof(value) == 'string') {
5356          
5357             value = Date.parseDate(value, this.format);
5358         }
5359         if (!value) {
5360             value = new Date();
5361         }
5362         
5363         this.value = value.clearTime(true);
5364         if(this.el){
5365             this.update(this.value);
5366         }
5367     },
5368
5369     /**
5370      * Gets the current selected value of the date field
5371      * @return {Date} The selected date
5372      */
5373     getValue : function(){
5374         return this.value;
5375     },
5376
5377     // private
5378     focus : function(){
5379         if(this.el){
5380             this.update(this.activeDate);
5381         }
5382     },
5383
5384     // privateval
5385     onRender : function(container, position){
5386         
5387         var m = [
5388              '<table cellspacing="0">',
5389                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
5390                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5391         var dn = this.dayNames;
5392         for(var i = 0; i < 7; i++){
5393             var d = this.startDay+i;
5394             if(d > 6){
5395                 d = d-7;
5396             }
5397             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5398         }
5399         m[m.length] = "</tr></thead><tbody><tr>";
5400         for(var i = 0; i < 42; i++) {
5401             if(i % 7 == 0 && i != 0){
5402                 m[m.length] = "</tr><tr>";
5403             }
5404             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5405         }
5406         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5407             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5408
5409         var el = document.createElement("div");
5410         el.className = "x-date-picker";
5411         el.innerHTML = m.join("");
5412
5413         container.dom.insertBefore(el, position);
5414
5415         this.el = Roo.get(el);
5416         this.eventEl = Roo.get(el.firstChild);
5417
5418         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5419             handler: this.showPrevMonth,
5420             scope: this,
5421             preventDefault:true,
5422             stopDefault:true
5423         });
5424
5425         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5426             handler: this.showNextMonth,
5427             scope: this,
5428             preventDefault:true,
5429             stopDefault:true
5430         });
5431
5432         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5433
5434         this.monthPicker = this.el.down('div.x-date-mp');
5435         this.monthPicker.enableDisplayMode('block');
5436         
5437         var kn = new Roo.KeyNav(this.eventEl, {
5438             "left" : function(e){
5439                 e.ctrlKey ?
5440                     this.showPrevMonth() :
5441                     this.update(this.activeDate.add("d", -1));
5442             },
5443
5444             "right" : function(e){
5445                 e.ctrlKey ?
5446                     this.showNextMonth() :
5447                     this.update(this.activeDate.add("d", 1));
5448             },
5449
5450             "up" : function(e){
5451                 e.ctrlKey ?
5452                     this.showNextYear() :
5453                     this.update(this.activeDate.add("d", -7));
5454             },
5455
5456             "down" : function(e){
5457                 e.ctrlKey ?
5458                     this.showPrevYear() :
5459                     this.update(this.activeDate.add("d", 7));
5460             },
5461
5462             "pageUp" : function(e){
5463                 this.showNextMonth();
5464             },
5465
5466             "pageDown" : function(e){
5467                 this.showPrevMonth();
5468             },
5469
5470             "enter" : function(e){
5471                 e.stopPropagation();
5472                 return true;
5473             },
5474
5475             scope : this
5476         });
5477
5478         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5479
5480         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5481
5482         this.el.unselectable();
5483         
5484         this.cells = this.el.select("table.x-date-inner tbody td");
5485         this.textNodes = this.el.query("table.x-date-inner tbody span");
5486
5487         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5488             text: "&#160;",
5489             tooltip: this.monthYearText
5490         });
5491
5492         this.mbtn.on('click', this.showMonthPicker, this);
5493         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5494
5495
5496         var today = (new Date()).dateFormat(this.format);
5497         
5498         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5499         if (this.showClear) {
5500             baseTb.add( new Roo.Toolbar.Fill());
5501         }
5502         baseTb.add({
5503             text: String.format(this.todayText, today),
5504             tooltip: String.format(this.todayTip, today),
5505             handler: this.selectToday,
5506             scope: this
5507         });
5508         
5509         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5510             
5511         //});
5512         if (this.showClear) {
5513             
5514             baseTb.add( new Roo.Toolbar.Fill());
5515             baseTb.add({
5516                 text: '&#160;',
5517                 cls: 'x-btn-icon x-btn-clear',
5518                 handler: function() {
5519                     //this.value = '';
5520                     this.fireEvent("select", this, '');
5521                 },
5522                 scope: this
5523             });
5524         }
5525         
5526         
5527         if(Roo.isIE){
5528             this.el.repaint();
5529         }
5530         this.update(this.value);
5531     },
5532
5533     createMonthPicker : function(){
5534         if(!this.monthPicker.dom.firstChild){
5535             var buf = ['<table border="0" cellspacing="0">'];
5536             for(var i = 0; i < 6; i++){
5537                 buf.push(
5538                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5539                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5540                     i == 0 ?
5541                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5542                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5543                 );
5544             }
5545             buf.push(
5546                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5547                     this.okText,
5548                     '</button><button type="button" class="x-date-mp-cancel">',
5549                     this.cancelText,
5550                     '</button></td></tr>',
5551                 '</table>'
5552             );
5553             this.monthPicker.update(buf.join(''));
5554             this.monthPicker.on('click', this.onMonthClick, this);
5555             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5556
5557             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5558             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5559
5560             this.mpMonths.each(function(m, a, i){
5561                 i += 1;
5562                 if((i%2) == 0){
5563                     m.dom.xmonth = 5 + Math.round(i * .5);
5564                 }else{
5565                     m.dom.xmonth = Math.round((i-1) * .5);
5566                 }
5567             });
5568         }
5569     },
5570
5571     showMonthPicker : function(){
5572         this.createMonthPicker();
5573         var size = this.el.getSize();
5574         this.monthPicker.setSize(size);
5575         this.monthPicker.child('table').setSize(size);
5576
5577         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5578         this.updateMPMonth(this.mpSelMonth);
5579         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5580         this.updateMPYear(this.mpSelYear);
5581
5582         this.monthPicker.slideIn('t', {duration:.2});
5583     },
5584
5585     updateMPYear : function(y){
5586         this.mpyear = y;
5587         var ys = this.mpYears.elements;
5588         for(var i = 1; i <= 10; i++){
5589             var td = ys[i-1], y2;
5590             if((i%2) == 0){
5591                 y2 = y + Math.round(i * .5);
5592                 td.firstChild.innerHTML = y2;
5593                 td.xyear = y2;
5594             }else{
5595                 y2 = y - (5-Math.round(i * .5));
5596                 td.firstChild.innerHTML = y2;
5597                 td.xyear = y2;
5598             }
5599             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5600         }
5601     },
5602
5603     updateMPMonth : function(sm){
5604         this.mpMonths.each(function(m, a, i){
5605             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5606         });
5607     },
5608
5609     selectMPMonth: function(m){
5610         
5611     },
5612
5613     onMonthClick : function(e, t){
5614         e.stopEvent();
5615         var el = new Roo.Element(t), pn;
5616         if(el.is('button.x-date-mp-cancel')){
5617             this.hideMonthPicker();
5618         }
5619         else if(el.is('button.x-date-mp-ok')){
5620             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5621             this.hideMonthPicker();
5622         }
5623         else if(pn = el.up('td.x-date-mp-month', 2)){
5624             this.mpMonths.removeClass('x-date-mp-sel');
5625             pn.addClass('x-date-mp-sel');
5626             this.mpSelMonth = pn.dom.xmonth;
5627         }
5628         else if(pn = el.up('td.x-date-mp-year', 2)){
5629             this.mpYears.removeClass('x-date-mp-sel');
5630             pn.addClass('x-date-mp-sel');
5631             this.mpSelYear = pn.dom.xyear;
5632         }
5633         else if(el.is('a.x-date-mp-prev')){
5634             this.updateMPYear(this.mpyear-10);
5635         }
5636         else if(el.is('a.x-date-mp-next')){
5637             this.updateMPYear(this.mpyear+10);
5638         }
5639     },
5640
5641     onMonthDblClick : function(e, t){
5642         e.stopEvent();
5643         var el = new Roo.Element(t), pn;
5644         if(pn = el.up('td.x-date-mp-month', 2)){
5645             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5646             this.hideMonthPicker();
5647         }
5648         else if(pn = el.up('td.x-date-mp-year', 2)){
5649             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5650             this.hideMonthPicker();
5651         }
5652     },
5653
5654     hideMonthPicker : function(disableAnim){
5655         if(this.monthPicker){
5656             if(disableAnim === true){
5657                 this.monthPicker.hide();
5658             }else{
5659                 this.monthPicker.slideOut('t', {duration:.2});
5660             }
5661         }
5662     },
5663
5664     // private
5665     showPrevMonth : function(e){
5666         this.update(this.activeDate.add("mo", -1));
5667     },
5668
5669     // private
5670     showNextMonth : function(e){
5671         this.update(this.activeDate.add("mo", 1));
5672     },
5673
5674     // private
5675     showPrevYear : function(){
5676         this.update(this.activeDate.add("y", -1));
5677     },
5678
5679     // private
5680     showNextYear : function(){
5681         this.update(this.activeDate.add("y", 1));
5682     },
5683
5684     // private
5685     handleMouseWheel : function(e){
5686         var delta = e.getWheelDelta();
5687         if(delta > 0){
5688             this.showPrevMonth();
5689             e.stopEvent();
5690         } else if(delta < 0){
5691             this.showNextMonth();
5692             e.stopEvent();
5693         }
5694     },
5695
5696     // private
5697     handleDateClick : function(e, t){
5698         e.stopEvent();
5699         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5700             this.setValue(new Date(t.dateValue));
5701             this.fireEvent("select", this, this.value);
5702         }
5703     },
5704
5705     // private
5706     selectToday : function(){
5707         this.setValue(new Date().clearTime());
5708         this.fireEvent("select", this, this.value);
5709     },
5710
5711     // private
5712     update : function(date)
5713     {
5714         var vd = this.activeDate;
5715         this.activeDate = date;
5716         if(vd && this.el){
5717             var t = date.getTime();
5718             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5719                 this.cells.removeClass("x-date-selected");
5720                 this.cells.each(function(c){
5721                    if(c.dom.firstChild.dateValue == t){
5722                        c.addClass("x-date-selected");
5723                        setTimeout(function(){
5724                             try{c.dom.firstChild.focus();}catch(e){}
5725                        }, 50);
5726                        return false;
5727                    }
5728                 });
5729                 return;
5730             }
5731         }
5732         
5733         var days = date.getDaysInMonth();
5734         var firstOfMonth = date.getFirstDateOfMonth();
5735         var startingPos = firstOfMonth.getDay()-this.startDay;
5736
5737         if(startingPos <= this.startDay){
5738             startingPos += 7;
5739         }
5740
5741         var pm = date.add("mo", -1);
5742         var prevStart = pm.getDaysInMonth()-startingPos;
5743
5744         var cells = this.cells.elements;
5745         var textEls = this.textNodes;
5746         days += startingPos;
5747
5748         // convert everything to numbers so it's fast
5749         var day = 86400000;
5750         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5751         var today = new Date().clearTime().getTime();
5752         var sel = date.clearTime().getTime();
5753         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5754         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5755         var ddMatch = this.disabledDatesRE;
5756         var ddText = this.disabledDatesText;
5757         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5758         var ddaysText = this.disabledDaysText;
5759         var format = this.format;
5760
5761         var setCellClass = function(cal, cell){
5762             cell.title = "";
5763             var t = d.getTime();
5764             cell.firstChild.dateValue = t;
5765             if(t == today){
5766                 cell.className += " x-date-today";
5767                 cell.title = cal.todayText;
5768             }
5769             if(t == sel){
5770                 cell.className += " x-date-selected";
5771                 setTimeout(function(){
5772                     try{cell.firstChild.focus();}catch(e){}
5773                 }, 50);
5774             }
5775             // disabling
5776             if(t < min) {
5777                 cell.className = " x-date-disabled";
5778                 cell.title = cal.minText;
5779                 return;
5780             }
5781             if(t > max) {
5782                 cell.className = " x-date-disabled";
5783                 cell.title = cal.maxText;
5784                 return;
5785             }
5786             if(ddays){
5787                 if(ddays.indexOf(d.getDay()) != -1){
5788                     cell.title = ddaysText;
5789                     cell.className = " x-date-disabled";
5790                 }
5791             }
5792             if(ddMatch && format){
5793                 var fvalue = d.dateFormat(format);
5794                 if(ddMatch.test(fvalue)){
5795                     cell.title = ddText.replace("%0", fvalue);
5796                     cell.className = " x-date-disabled";
5797                 }
5798             }
5799         };
5800
5801         var i = 0;
5802         for(; i < startingPos; i++) {
5803             textEls[i].innerHTML = (++prevStart);
5804             d.setDate(d.getDate()+1);
5805             cells[i].className = "x-date-prevday";
5806             setCellClass(this, cells[i]);
5807         }
5808         for(; i < days; i++){
5809             intDay = i - startingPos + 1;
5810             textEls[i].innerHTML = (intDay);
5811             d.setDate(d.getDate()+1);
5812             cells[i].className = "x-date-active";
5813             setCellClass(this, cells[i]);
5814         }
5815         var extraDays = 0;
5816         for(; i < 42; i++) {
5817              textEls[i].innerHTML = (++extraDays);
5818              d.setDate(d.getDate()+1);
5819              cells[i].className = "x-date-nextday";
5820              setCellClass(this, cells[i]);
5821         }
5822
5823         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5824         this.fireEvent('monthchange', this, date);
5825         
5826         if(!this.internalRender){
5827             var main = this.el.dom.firstChild;
5828             var w = main.offsetWidth;
5829             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5830             Roo.fly(main).setWidth(w);
5831             this.internalRender = true;
5832             // opera does not respect the auto grow header center column
5833             // then, after it gets a width opera refuses to recalculate
5834             // without a second pass
5835             if(Roo.isOpera && !this.secondPass){
5836                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5837                 this.secondPass = true;
5838                 this.update.defer(10, this, [date]);
5839             }
5840         }
5841         
5842         
5843     }
5844 });        /*
5845  * Based on:
5846  * Ext JS Library 1.1.1
5847  * Copyright(c) 2006-2007, Ext JS, LLC.
5848  *
5849  * Originally Released Under LGPL - original licence link has changed is not relivant.
5850  *
5851  * Fork - LGPL
5852  * <script type="text/javascript">
5853  */
5854 /**
5855  * @class Roo.TabPanel
5856  * @extends Roo.util.Observable
5857  * A lightweight tab container.
5858  * <br><br>
5859  * Usage:
5860  * <pre><code>
5861 // basic tabs 1, built from existing content
5862 var tabs = new Roo.TabPanel("tabs1");
5863 tabs.addTab("script", "View Script");
5864 tabs.addTab("markup", "View Markup");
5865 tabs.activate("script");
5866
5867 // more advanced tabs, built from javascript
5868 var jtabs = new Roo.TabPanel("jtabs");
5869 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5870
5871 // set up the UpdateManager
5872 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5873 var updater = tab2.getUpdateManager();
5874 updater.setDefaultUrl("ajax1.htm");
5875 tab2.on('activate', updater.refresh, updater, true);
5876
5877 // Use setUrl for Ajax loading
5878 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5879 tab3.setUrl("ajax2.htm", null, true);
5880
5881 // Disabled tab
5882 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5883 tab4.disable();
5884
5885 jtabs.activate("jtabs-1");
5886  * </code></pre>
5887  * @constructor
5888  * Create a new TabPanel.
5889  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5890  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5891  */
5892 Roo.TabPanel = function(container, config){
5893     /**
5894     * The container element for this TabPanel.
5895     * @type Roo.Element
5896     */
5897     this.el = Roo.get(container, true);
5898     if(config){
5899         if(typeof config == "boolean"){
5900             this.tabPosition = config ? "bottom" : "top";
5901         }else{
5902             Roo.apply(this, config);
5903         }
5904     }
5905     if(this.tabPosition == "bottom"){
5906         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5907         this.el.addClass("x-tabs-bottom");
5908     }
5909     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5910     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5911     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5912     if(Roo.isIE){
5913         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5914     }
5915     if(this.tabPosition != "bottom"){
5916         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5917          * @type Roo.Element
5918          */
5919         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5920         this.el.addClass("x-tabs-top");
5921     }
5922     this.items = [];
5923
5924     this.bodyEl.setStyle("position", "relative");
5925
5926     this.active = null;
5927     this.activateDelegate = this.activate.createDelegate(this);
5928
5929     this.addEvents({
5930         /**
5931          * @event tabchange
5932          * Fires when the active tab changes
5933          * @param {Roo.TabPanel} this
5934          * @param {Roo.TabPanelItem} activePanel The new active tab
5935          */
5936         "tabchange": true,
5937         /**
5938          * @event beforetabchange
5939          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5940          * @param {Roo.TabPanel} this
5941          * @param {Object} e Set cancel to true on this object to cancel the tab change
5942          * @param {Roo.TabPanelItem} tab The tab being changed to
5943          */
5944         "beforetabchange" : true
5945     });
5946
5947     Roo.EventManager.onWindowResize(this.onResize, this);
5948     this.cpad = this.el.getPadding("lr");
5949     this.hiddenCount = 0;
5950
5951
5952     // toolbar on the tabbar support...
5953     if (this.toolbar) {
5954         var tcfg = this.toolbar;
5955         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5956         this.toolbar = new Roo.Toolbar(tcfg);
5957         if (Roo.isSafari) {
5958             var tbl = tcfg.container.child('table', true);
5959             tbl.setAttribute('width', '100%');
5960         }
5961         
5962     }
5963    
5964
5965
5966     Roo.TabPanel.superclass.constructor.call(this);
5967 };
5968
5969 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5970     /*
5971      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5972      */
5973     tabPosition : "top",
5974     /*
5975      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5976      */
5977     currentTabWidth : 0,
5978     /*
5979      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5980      */
5981     minTabWidth : 40,
5982     /*
5983      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5984      */
5985     maxTabWidth : 250,
5986     /*
5987      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5988      */
5989     preferredTabWidth : 175,
5990     /*
5991      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5992      */
5993     resizeTabs : false,
5994     /*
5995      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5996      */
5997     monitorResize : true,
5998     /*
5999      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6000      */
6001     toolbar : false,
6002
6003     /**
6004      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6005      * @param {String} id The id of the div to use <b>or create</b>
6006      * @param {String} text The text for the tab
6007      * @param {String} content (optional) Content to put in the TabPanelItem body
6008      * @param {Boolean} closable (optional) True to create a close icon on the tab
6009      * @return {Roo.TabPanelItem} The created TabPanelItem
6010      */
6011     addTab : function(id, text, content, closable){
6012         var item = new Roo.TabPanelItem(this, id, text, closable);
6013         this.addTabItem(item);
6014         if(content){
6015             item.setContent(content);
6016         }
6017         return item;
6018     },
6019
6020     /**
6021      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6022      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6023      * @return {Roo.TabPanelItem}
6024      */
6025     getTab : function(id){
6026         return this.items[id];
6027     },
6028
6029     /**
6030      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6031      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6032      */
6033     hideTab : function(id){
6034         var t = this.items[id];
6035         if(!t.isHidden()){
6036            t.setHidden(true);
6037            this.hiddenCount++;
6038            this.autoSizeTabs();
6039         }
6040     },
6041
6042     /**
6043      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6044      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6045      */
6046     unhideTab : function(id){
6047         var t = this.items[id];
6048         if(t.isHidden()){
6049            t.setHidden(false);
6050            this.hiddenCount--;
6051            this.autoSizeTabs();
6052         }
6053     },
6054
6055     /**
6056      * Adds an existing {@link Roo.TabPanelItem}.
6057      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6058      */
6059     addTabItem : function(item){
6060         this.items[item.id] = item;
6061         this.items.push(item);
6062         if(this.resizeTabs){
6063            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6064            this.autoSizeTabs();
6065         }else{
6066             item.autoSize();
6067         }
6068     },
6069
6070     /**
6071      * Removes a {@link Roo.TabPanelItem}.
6072      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6073      */
6074     removeTab : function(id){
6075         var items = this.items;
6076         var tab = items[id];
6077         if(!tab) { return; }
6078         var index = items.indexOf(tab);
6079         if(this.active == tab && items.length > 1){
6080             var newTab = this.getNextAvailable(index);
6081             if(newTab) {
6082                 newTab.activate();
6083             }
6084         }
6085         this.stripEl.dom.removeChild(tab.pnode.dom);
6086         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6087             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6088         }
6089         items.splice(index, 1);
6090         delete this.items[tab.id];
6091         tab.fireEvent("close", tab);
6092         tab.purgeListeners();
6093         this.autoSizeTabs();
6094     },
6095
6096     getNextAvailable : function(start){
6097         var items = this.items;
6098         var index = start;
6099         // look for a next tab that will slide over to
6100         // replace the one being removed
6101         while(index < items.length){
6102             var item = items[++index];
6103             if(item && !item.isHidden()){
6104                 return item;
6105             }
6106         }
6107         // if one isn't found select the previous tab (on the left)
6108         index = start;
6109         while(index >= 0){
6110             var item = items[--index];
6111             if(item && !item.isHidden()){
6112                 return item;
6113             }
6114         }
6115         return null;
6116     },
6117
6118     /**
6119      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6120      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6121      */
6122     disableTab : function(id){
6123         var tab = this.items[id];
6124         if(tab && this.active != tab){
6125             tab.disable();
6126         }
6127     },
6128
6129     /**
6130      * Enables a {@link Roo.TabPanelItem} that is disabled.
6131      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6132      */
6133     enableTab : function(id){
6134         var tab = this.items[id];
6135         tab.enable();
6136     },
6137
6138     /**
6139      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6140      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6141      * @return {Roo.TabPanelItem} The TabPanelItem.
6142      */
6143     activate : function(id){
6144         var tab = this.items[id];
6145         if(!tab){
6146             return null;
6147         }
6148         if(tab == this.active || tab.disabled){
6149             return tab;
6150         }
6151         var e = {};
6152         this.fireEvent("beforetabchange", this, e, tab);
6153         if(e.cancel !== true && !tab.disabled){
6154             if(this.active){
6155                 this.active.hide();
6156             }
6157             this.active = this.items[id];
6158             this.active.show();
6159             this.fireEvent("tabchange", this, this.active);
6160         }
6161         return tab;
6162     },
6163
6164     /**
6165      * Gets the active {@link Roo.TabPanelItem}.
6166      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6167      */
6168     getActiveTab : function(){
6169         return this.active;
6170     },
6171
6172     /**
6173      * Updates the tab body element to fit the height of the container element
6174      * for overflow scrolling
6175      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6176      */
6177     syncHeight : function(targetHeight){
6178         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6179         var bm = this.bodyEl.getMargins();
6180         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6181         this.bodyEl.setHeight(newHeight);
6182         return newHeight;
6183     },
6184
6185     onResize : function(){
6186         if(this.monitorResize){
6187             this.autoSizeTabs();
6188         }
6189     },
6190
6191     /**
6192      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6193      */
6194     beginUpdate : function(){
6195         this.updating = true;
6196     },
6197
6198     /**
6199      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6200      */
6201     endUpdate : function(){
6202         this.updating = false;
6203         this.autoSizeTabs();
6204     },
6205
6206     /**
6207      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6208      */
6209     autoSizeTabs : function(){
6210         var count = this.items.length;
6211         var vcount = count - this.hiddenCount;
6212         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6213             return;
6214         }
6215         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6216         var availWidth = Math.floor(w / vcount);
6217         var b = this.stripBody;
6218         if(b.getWidth() > w){
6219             var tabs = this.items;
6220             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6221             if(availWidth < this.minTabWidth){
6222                 /*if(!this.sleft){    // incomplete scrolling code
6223                     this.createScrollButtons();
6224                 }
6225                 this.showScroll();
6226                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6227             }
6228         }else{
6229             if(this.currentTabWidth < this.preferredTabWidth){
6230                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6231             }
6232         }
6233     },
6234
6235     /**
6236      * Returns the number of tabs in this TabPanel.
6237      * @return {Number}
6238      */
6239      getCount : function(){
6240          return this.items.length;
6241      },
6242
6243     /**
6244      * Resizes all the tabs to the passed width
6245      * @param {Number} The new width
6246      */
6247     setTabWidth : function(width){
6248         this.currentTabWidth = width;
6249         for(var i = 0, len = this.items.length; i < len; i++) {
6250                 if(!this.items[i].isHidden()) {
6251                 this.items[i].setWidth(width);
6252             }
6253         }
6254     },
6255
6256     /**
6257      * Destroys this TabPanel
6258      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6259      */
6260     destroy : function(removeEl){
6261         Roo.EventManager.removeResizeListener(this.onResize, this);
6262         for(var i = 0, len = this.items.length; i < len; i++){
6263             this.items[i].purgeListeners();
6264         }
6265         if(removeEl === true){
6266             this.el.update("");
6267             this.el.remove();
6268         }
6269     }
6270 });
6271
6272 /**
6273  * @class Roo.TabPanelItem
6274  * @extends Roo.util.Observable
6275  * Represents an individual item (tab plus body) in a TabPanel.
6276  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6277  * @param {String} id The id of this TabPanelItem
6278  * @param {String} text The text for the tab of this TabPanelItem
6279  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6280  */
6281 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6282     /**
6283      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6284      * @type Roo.TabPanel
6285      */
6286     this.tabPanel = tabPanel;
6287     /**
6288      * The id for this TabPanelItem
6289      * @type String
6290      */
6291     this.id = id;
6292     /** @private */
6293     this.disabled = false;
6294     /** @private */
6295     this.text = text;
6296     /** @private */
6297     this.loaded = false;
6298     this.closable = closable;
6299
6300     /**
6301      * The body element for this TabPanelItem.
6302      * @type Roo.Element
6303      */
6304     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6305     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6306     this.bodyEl.setStyle("display", "block");
6307     this.bodyEl.setStyle("zoom", "1");
6308     this.hideAction();
6309
6310     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6311     /** @private */
6312     this.el = Roo.get(els.el, true);
6313     this.inner = Roo.get(els.inner, true);
6314     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6315     this.pnode = Roo.get(els.el.parentNode, true);
6316     this.el.on("mousedown", this.onTabMouseDown, this);
6317     this.el.on("click", this.onTabClick, this);
6318     /** @private */
6319     if(closable){
6320         var c = Roo.get(els.close, true);
6321         c.dom.title = this.closeText;
6322         c.addClassOnOver("close-over");
6323         c.on("click", this.closeClick, this);
6324      }
6325
6326     this.addEvents({
6327          /**
6328          * @event activate
6329          * Fires when this tab becomes the active tab.
6330          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6331          * @param {Roo.TabPanelItem} this
6332          */
6333         "activate": true,
6334         /**
6335          * @event beforeclose
6336          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6337          * @param {Roo.TabPanelItem} this
6338          * @param {Object} e Set cancel to true on this object to cancel the close.
6339          */
6340         "beforeclose": true,
6341         /**
6342          * @event close
6343          * Fires when this tab is closed.
6344          * @param {Roo.TabPanelItem} this
6345          */
6346          "close": true,
6347         /**
6348          * @event deactivate
6349          * Fires when this tab is no longer the active tab.
6350          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6351          * @param {Roo.TabPanelItem} this
6352          */
6353          "deactivate" : true
6354     });
6355     this.hidden = false;
6356
6357     Roo.TabPanelItem.superclass.constructor.call(this);
6358 };
6359
6360 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6361     purgeListeners : function(){
6362        Roo.util.Observable.prototype.purgeListeners.call(this);
6363        this.el.removeAllListeners();
6364     },
6365     /**
6366      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6367      */
6368     show : function(){
6369         this.pnode.addClass("on");
6370         this.showAction();
6371         if(Roo.isOpera){
6372             this.tabPanel.stripWrap.repaint();
6373         }
6374         this.fireEvent("activate", this.tabPanel, this);
6375     },
6376
6377     /**
6378      * Returns true if this tab is the active tab.
6379      * @return {Boolean}
6380      */
6381     isActive : function(){
6382         return this.tabPanel.getActiveTab() == this;
6383     },
6384
6385     /**
6386      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6387      */
6388     hide : function(){
6389         this.pnode.removeClass("on");
6390         this.hideAction();
6391         this.fireEvent("deactivate", this.tabPanel, this);
6392     },
6393
6394     hideAction : function(){
6395         this.bodyEl.hide();
6396         this.bodyEl.setStyle("position", "absolute");
6397         this.bodyEl.setLeft("-20000px");
6398         this.bodyEl.setTop("-20000px");
6399     },
6400
6401     showAction : function(){
6402         this.bodyEl.setStyle("position", "relative");
6403         this.bodyEl.setTop("");
6404         this.bodyEl.setLeft("");
6405         this.bodyEl.show();
6406     },
6407
6408     /**
6409      * Set the tooltip for the tab.
6410      * @param {String} tooltip The tab's tooltip
6411      */
6412     setTooltip : function(text){
6413         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6414             this.textEl.dom.qtip = text;
6415             this.textEl.dom.removeAttribute('title');
6416         }else{
6417             this.textEl.dom.title = text;
6418         }
6419     },
6420
6421     onTabClick : function(e){
6422         e.preventDefault();
6423         this.tabPanel.activate(this.id);
6424     },
6425
6426     onTabMouseDown : function(e){
6427         e.preventDefault();
6428         this.tabPanel.activate(this.id);
6429     },
6430
6431     getWidth : function(){
6432         return this.inner.getWidth();
6433     },
6434
6435     setWidth : function(width){
6436         var iwidth = width - this.pnode.getPadding("lr");
6437         this.inner.setWidth(iwidth);
6438         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6439         this.pnode.setWidth(width);
6440     },
6441
6442     /**
6443      * Show or hide the tab
6444      * @param {Boolean} hidden True to hide or false to show.
6445      */
6446     setHidden : function(hidden){
6447         this.hidden = hidden;
6448         this.pnode.setStyle("display", hidden ? "none" : "");
6449     },
6450
6451     /**
6452      * Returns true if this tab is "hidden"
6453      * @return {Boolean}
6454      */
6455     isHidden : function(){
6456         return this.hidden;
6457     },
6458
6459     /**
6460      * Returns the text for this tab
6461      * @return {String}
6462      */
6463     getText : function(){
6464         return this.text;
6465     },
6466
6467     autoSize : function(){
6468         //this.el.beginMeasure();
6469         this.textEl.setWidth(1);
6470         /*
6471          *  #2804 [new] Tabs in Roojs
6472          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6473          */
6474         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6475         //this.el.endMeasure();
6476     },
6477
6478     /**
6479      * Sets the text for the tab (Note: this also sets the tooltip text)
6480      * @param {String} text The tab's text and tooltip
6481      */
6482     setText : function(text){
6483         this.text = text;
6484         this.textEl.update(text);
6485         this.setTooltip(text);
6486         if(!this.tabPanel.resizeTabs){
6487             this.autoSize();
6488         }
6489     },
6490     /**
6491      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6492      */
6493     activate : function(){
6494         this.tabPanel.activate(this.id);
6495     },
6496
6497     /**
6498      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6499      */
6500     disable : function(){
6501         if(this.tabPanel.active != this){
6502             this.disabled = true;
6503             this.pnode.addClass("disabled");
6504         }
6505     },
6506
6507     /**
6508      * Enables this TabPanelItem if it was previously disabled.
6509      */
6510     enable : function(){
6511         this.disabled = false;
6512         this.pnode.removeClass("disabled");
6513     },
6514
6515     /**
6516      * Sets the content for this TabPanelItem.
6517      * @param {String} content The content
6518      * @param {Boolean} loadScripts true to look for and load scripts
6519      */
6520     setContent : function(content, loadScripts){
6521         this.bodyEl.update(content, loadScripts);
6522     },
6523
6524     /**
6525      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6526      * @return {Roo.UpdateManager} The UpdateManager
6527      */
6528     getUpdateManager : function(){
6529         return this.bodyEl.getUpdateManager();
6530     },
6531
6532     /**
6533      * Set a URL to be used to load the content for this TabPanelItem.
6534      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6535      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6536      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6537      * @return {Roo.UpdateManager} The UpdateManager
6538      */
6539     setUrl : function(url, params, loadOnce){
6540         if(this.refreshDelegate){
6541             this.un('activate', this.refreshDelegate);
6542         }
6543         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6544         this.on("activate", this.refreshDelegate);
6545         return this.bodyEl.getUpdateManager();
6546     },
6547
6548     /** @private */
6549     _handleRefresh : function(url, params, loadOnce){
6550         if(!loadOnce || !this.loaded){
6551             var updater = this.bodyEl.getUpdateManager();
6552             updater.update(url, params, this._setLoaded.createDelegate(this));
6553         }
6554     },
6555
6556     /**
6557      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6558      *   Will fail silently if the setUrl method has not been called.
6559      *   This does not activate the panel, just updates its content.
6560      */
6561     refresh : function(){
6562         if(this.refreshDelegate){
6563            this.loaded = false;
6564            this.refreshDelegate();
6565         }
6566     },
6567
6568     /** @private */
6569     _setLoaded : function(){
6570         this.loaded = true;
6571     },
6572
6573     /** @private */
6574     closeClick : function(e){
6575         var o = {};
6576         e.stopEvent();
6577         this.fireEvent("beforeclose", this, o);
6578         if(o.cancel !== true){
6579             this.tabPanel.removeTab(this.id);
6580         }
6581     },
6582     /**
6583      * The text displayed in the tooltip for the close icon.
6584      * @type String
6585      */
6586     closeText : "Close this tab"
6587 });
6588
6589 /** @private */
6590 Roo.TabPanel.prototype.createStrip = function(container){
6591     var strip = document.createElement("div");
6592     strip.className = "x-tabs-wrap";
6593     container.appendChild(strip);
6594     return strip;
6595 };
6596 /** @private */
6597 Roo.TabPanel.prototype.createStripList = function(strip){
6598     // div wrapper for retard IE
6599     // returns the "tr" element.
6600     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6601         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6602         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6603     return strip.firstChild.firstChild.firstChild.firstChild;
6604 };
6605 /** @private */
6606 Roo.TabPanel.prototype.createBody = function(container){
6607     var body = document.createElement("div");
6608     Roo.id(body, "tab-body");
6609     Roo.fly(body).addClass("x-tabs-body");
6610     container.appendChild(body);
6611     return body;
6612 };
6613 /** @private */
6614 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6615     var body = Roo.getDom(id);
6616     if(!body){
6617         body = document.createElement("div");
6618         body.id = id;
6619     }
6620     Roo.fly(body).addClass("x-tabs-item-body");
6621     bodyEl.insertBefore(body, bodyEl.firstChild);
6622     return body;
6623 };
6624 /** @private */
6625 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6626     var td = document.createElement("td");
6627     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6628     //stripEl.appendChild(td);
6629     if(closable){
6630         td.className = "x-tabs-closable";
6631         if(!this.closeTpl){
6632             this.closeTpl = new Roo.Template(
6633                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6634                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6635                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6636             );
6637         }
6638         var el = this.closeTpl.overwrite(td, {"text": text});
6639         var close = el.getElementsByTagName("div")[0];
6640         var inner = el.getElementsByTagName("em")[0];
6641         return {"el": el, "close": close, "inner": inner};
6642     } else {
6643         if(!this.tabTpl){
6644             this.tabTpl = new Roo.Template(
6645                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6646                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6647             );
6648         }
6649         var el = this.tabTpl.overwrite(td, {"text": text});
6650         var inner = el.getElementsByTagName("em")[0];
6651         return {"el": el, "inner": inner};
6652     }
6653 };/*
6654  * Based on:
6655  * Ext JS Library 1.1.1
6656  * Copyright(c) 2006-2007, Ext JS, LLC.
6657  *
6658  * Originally Released Under LGPL - original licence link has changed is not relivant.
6659  *
6660  * Fork - LGPL
6661  * <script type="text/javascript">
6662  */
6663
6664 /**
6665  * @class Roo.Button
6666  * @extends Roo.util.Observable
6667  * Simple Button class
6668  * @cfg {String} text The button text
6669  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6670  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6671  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6672  * @cfg {Object} scope The scope of the handler
6673  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6674  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6675  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6676  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6677  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6678  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6679    applies if enableToggle = true)
6680  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6681  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6682   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6683  * @constructor
6684  * Create a new button
6685  * @param {Object} config The config object
6686  */
6687 Roo.Button = function(renderTo, config)
6688 {
6689     if (!config) {
6690         config = renderTo;
6691         renderTo = config.renderTo || false;
6692     }
6693     
6694     Roo.apply(this, config);
6695     this.addEvents({
6696         /**
6697              * @event click
6698              * Fires when this button is clicked
6699              * @param {Button} this
6700              * @param {EventObject} e The click event
6701              */
6702             "click" : true,
6703         /**
6704              * @event toggle
6705              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6706              * @param {Button} this
6707              * @param {Boolean} pressed
6708              */
6709             "toggle" : true,
6710         /**
6711              * @event mouseover
6712              * Fires when the mouse hovers over the button
6713              * @param {Button} this
6714              * @param {Event} e The event object
6715              */
6716         'mouseover' : true,
6717         /**
6718              * @event mouseout
6719              * Fires when the mouse exits the button
6720              * @param {Button} this
6721              * @param {Event} e The event object
6722              */
6723         'mouseout': true,
6724          /**
6725              * @event render
6726              * Fires when the button is rendered
6727              * @param {Button} this
6728              */
6729         'render': true
6730     });
6731     if(this.menu){
6732         this.menu = Roo.menu.MenuMgr.get(this.menu);
6733     }
6734     // register listeners first!!  - so render can be captured..
6735     Roo.util.Observable.call(this);
6736     if(renderTo){
6737         this.render(renderTo);
6738     }
6739     
6740   
6741 };
6742
6743 Roo.extend(Roo.Button, Roo.util.Observable, {
6744     /**
6745      * 
6746      */
6747     
6748     /**
6749      * Read-only. True if this button is hidden
6750      * @type Boolean
6751      */
6752     hidden : false,
6753     /**
6754      * Read-only. True if this button is disabled
6755      * @type Boolean
6756      */
6757     disabled : false,
6758     /**
6759      * Read-only. True if this button is pressed (only if enableToggle = true)
6760      * @type Boolean
6761      */
6762     pressed : false,
6763
6764     /**
6765      * @cfg {Number} tabIndex 
6766      * The DOM tabIndex for this button (defaults to undefined)
6767      */
6768     tabIndex : undefined,
6769
6770     /**
6771      * @cfg {Boolean} enableToggle
6772      * True to enable pressed/not pressed toggling (defaults to false)
6773      */
6774     enableToggle: false,
6775     /**
6776      * @cfg {Mixed} menu
6777      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6778      */
6779     menu : undefined,
6780     /**
6781      * @cfg {String} menuAlign
6782      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6783      */
6784     menuAlign : "tl-bl?",
6785
6786     /**
6787      * @cfg {String} iconCls
6788      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6789      */
6790     iconCls : undefined,
6791     /**
6792      * @cfg {String} type
6793      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6794      */
6795     type : 'button',
6796
6797     // private
6798     menuClassTarget: 'tr',
6799
6800     /**
6801      * @cfg {String} clickEvent
6802      * The type of event to map to the button's event handler (defaults to 'click')
6803      */
6804     clickEvent : 'click',
6805
6806     /**
6807      * @cfg {Boolean} handleMouseEvents
6808      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6809      */
6810     handleMouseEvents : true,
6811
6812     /**
6813      * @cfg {String} tooltipType
6814      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6815      */
6816     tooltipType : 'qtip',
6817
6818     /**
6819      * @cfg {String} cls
6820      * A CSS class to apply to the button's main element.
6821      */
6822     
6823     /**
6824      * @cfg {Roo.Template} template (Optional)
6825      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6826      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6827      * require code modifications if required elements (e.g. a button) aren't present.
6828      */
6829
6830     // private
6831     render : function(renderTo){
6832         var btn;
6833         if(this.hideParent){
6834             this.parentEl = Roo.get(renderTo);
6835         }
6836         if(!this.dhconfig){
6837             if(!this.template){
6838                 if(!Roo.Button.buttonTemplate){
6839                     // hideous table template
6840                     Roo.Button.buttonTemplate = new Roo.Template(
6841                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6842                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6843                         "</tr></tbody></table>");
6844                 }
6845                 this.template = Roo.Button.buttonTemplate;
6846             }
6847             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6848             var btnEl = btn.child("button:first");
6849             btnEl.on('focus', this.onFocus, this);
6850             btnEl.on('blur', this.onBlur, this);
6851             if(this.cls){
6852                 btn.addClass(this.cls);
6853             }
6854             if(this.icon){
6855                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6856             }
6857             if(this.iconCls){
6858                 btnEl.addClass(this.iconCls);
6859                 if(!this.cls){
6860                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6861                 }
6862             }
6863             if(this.tabIndex !== undefined){
6864                 btnEl.dom.tabIndex = this.tabIndex;
6865             }
6866             if(this.tooltip){
6867                 if(typeof this.tooltip == 'object'){
6868                     Roo.QuickTips.tips(Roo.apply({
6869                           target: btnEl.id
6870                     }, this.tooltip));
6871                 } else {
6872                     btnEl.dom[this.tooltipType] = this.tooltip;
6873                 }
6874             }
6875         }else{
6876             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6877         }
6878         this.el = btn;
6879         if(this.id){
6880             this.el.dom.id = this.el.id = this.id;
6881         }
6882         if(this.menu){
6883             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6884             this.menu.on("show", this.onMenuShow, this);
6885             this.menu.on("hide", this.onMenuHide, this);
6886         }
6887         btn.addClass("x-btn");
6888         if(Roo.isIE && !Roo.isIE7){
6889             this.autoWidth.defer(1, this);
6890         }else{
6891             this.autoWidth();
6892         }
6893         if(this.handleMouseEvents){
6894             btn.on("mouseover", this.onMouseOver, this);
6895             btn.on("mouseout", this.onMouseOut, this);
6896             btn.on("mousedown", this.onMouseDown, this);
6897         }
6898         btn.on(this.clickEvent, this.onClick, this);
6899         //btn.on("mouseup", this.onMouseUp, this);
6900         if(this.hidden){
6901             this.hide();
6902         }
6903         if(this.disabled){
6904             this.disable();
6905         }
6906         Roo.ButtonToggleMgr.register(this);
6907         if(this.pressed){
6908             this.el.addClass("x-btn-pressed");
6909         }
6910         if(this.repeat){
6911             var repeater = new Roo.util.ClickRepeater(btn,
6912                 typeof this.repeat == "object" ? this.repeat : {}
6913             );
6914             repeater.on("click", this.onClick,  this);
6915         }
6916         
6917         this.fireEvent('render', this);
6918         
6919     },
6920     /**
6921      * Returns the button's underlying element
6922      * @return {Roo.Element} The element
6923      */
6924     getEl : function(){
6925         return this.el;  
6926     },
6927     
6928     /**
6929      * Destroys this Button and removes any listeners.
6930      */
6931     destroy : function(){
6932         Roo.ButtonToggleMgr.unregister(this);
6933         this.el.removeAllListeners();
6934         this.purgeListeners();
6935         this.el.remove();
6936     },
6937
6938     // private
6939     autoWidth : function(){
6940         if(this.el){
6941             this.el.setWidth("auto");
6942             if(Roo.isIE7 && Roo.isStrict){
6943                 var ib = this.el.child('button');
6944                 if(ib && ib.getWidth() > 20){
6945                     ib.clip();
6946                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6947                 }
6948             }
6949             if(this.minWidth){
6950                 if(this.hidden){
6951                     this.el.beginMeasure();
6952                 }
6953                 if(this.el.getWidth() < this.minWidth){
6954                     this.el.setWidth(this.minWidth);
6955                 }
6956                 if(this.hidden){
6957                     this.el.endMeasure();
6958                 }
6959             }
6960         }
6961     },
6962
6963     /**
6964      * Assigns this button's click handler
6965      * @param {Function} handler The function to call when the button is clicked
6966      * @param {Object} scope (optional) Scope for the function passed in
6967      */
6968     setHandler : function(handler, scope){
6969         this.handler = handler;
6970         this.scope = scope;  
6971     },
6972     
6973     /**
6974      * Sets this button's text
6975      * @param {String} text The button text
6976      */
6977     setText : function(text){
6978         this.text = text;
6979         if(this.el){
6980             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6981         }
6982         this.autoWidth();
6983     },
6984     
6985     /**
6986      * Gets the text for this button
6987      * @return {String} The button text
6988      */
6989     getText : function(){
6990         return this.text;  
6991     },
6992     
6993     /**
6994      * Show this button
6995      */
6996     show: function(){
6997         this.hidden = false;
6998         if(this.el){
6999             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7000         }
7001     },
7002     
7003     /**
7004      * Hide this button
7005      */
7006     hide: function(){
7007         this.hidden = true;
7008         if(this.el){
7009             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7010         }
7011     },
7012     
7013     /**
7014      * Convenience function for boolean show/hide
7015      * @param {Boolean} visible True to show, false to hide
7016      */
7017     setVisible: function(visible){
7018         if(visible) {
7019             this.show();
7020         }else{
7021             this.hide();
7022         }
7023     },
7024     
7025     /**
7026      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7027      * @param {Boolean} state (optional) Force a particular state
7028      */
7029     toggle : function(state){
7030         state = state === undefined ? !this.pressed : state;
7031         if(state != this.pressed){
7032             if(state){
7033                 this.el.addClass("x-btn-pressed");
7034                 this.pressed = true;
7035                 this.fireEvent("toggle", this, true);
7036             }else{
7037                 this.el.removeClass("x-btn-pressed");
7038                 this.pressed = false;
7039                 this.fireEvent("toggle", this, false);
7040             }
7041             if(this.toggleHandler){
7042                 this.toggleHandler.call(this.scope || this, this, state);
7043             }
7044         }
7045     },
7046     
7047     /**
7048      * Focus the button
7049      */
7050     focus : function(){
7051         this.el.child('button:first').focus();
7052     },
7053     
7054     /**
7055      * Disable this button
7056      */
7057     disable : function(){
7058         if(this.el){
7059             this.el.addClass("x-btn-disabled");
7060         }
7061         this.disabled = true;
7062     },
7063     
7064     /**
7065      * Enable this button
7066      */
7067     enable : function(){
7068         if(this.el){
7069             this.el.removeClass("x-btn-disabled");
7070         }
7071         this.disabled = false;
7072     },
7073
7074     /**
7075      * Convenience function for boolean enable/disable
7076      * @param {Boolean} enabled True to enable, false to disable
7077      */
7078     setDisabled : function(v){
7079         this[v !== true ? "enable" : "disable"]();
7080     },
7081
7082     // private
7083     onClick : function(e)
7084     {
7085         if(e){
7086             e.preventDefault();
7087         }
7088         if(e.button != 0){
7089             return;
7090         }
7091         if(!this.disabled){
7092             if(this.enableToggle){
7093                 this.toggle();
7094             }
7095             if(this.menu && !this.menu.isVisible()){
7096                 this.menu.show(this.el, this.menuAlign);
7097             }
7098             this.fireEvent("click", this, e);
7099             if(this.handler){
7100                 this.el.removeClass("x-btn-over");
7101                 this.handler.call(this.scope || this, this, e);
7102             }
7103         }
7104     },
7105     // private
7106     onMouseOver : function(e){
7107         if(!this.disabled){
7108             this.el.addClass("x-btn-over");
7109             this.fireEvent('mouseover', this, e);
7110         }
7111     },
7112     // private
7113     onMouseOut : function(e){
7114         if(!e.within(this.el,  true)){
7115             this.el.removeClass("x-btn-over");
7116             this.fireEvent('mouseout', this, e);
7117         }
7118     },
7119     // private
7120     onFocus : function(e){
7121         if(!this.disabled){
7122             this.el.addClass("x-btn-focus");
7123         }
7124     },
7125     // private
7126     onBlur : function(e){
7127         this.el.removeClass("x-btn-focus");
7128     },
7129     // private
7130     onMouseDown : function(e){
7131         if(!this.disabled && e.button == 0){
7132             this.el.addClass("x-btn-click");
7133             Roo.get(document).on('mouseup', this.onMouseUp, this);
7134         }
7135     },
7136     // private
7137     onMouseUp : function(e){
7138         if(e.button == 0){
7139             this.el.removeClass("x-btn-click");
7140             Roo.get(document).un('mouseup', this.onMouseUp, this);
7141         }
7142     },
7143     // private
7144     onMenuShow : function(e){
7145         this.el.addClass("x-btn-menu-active");
7146     },
7147     // private
7148     onMenuHide : function(e){
7149         this.el.removeClass("x-btn-menu-active");
7150     }   
7151 });
7152
7153 // Private utility class used by Button
7154 Roo.ButtonToggleMgr = function(){
7155    var groups = {};
7156    
7157    function toggleGroup(btn, state){
7158        if(state){
7159            var g = groups[btn.toggleGroup];
7160            for(var i = 0, l = g.length; i < l; i++){
7161                if(g[i] != btn){
7162                    g[i].toggle(false);
7163                }
7164            }
7165        }
7166    }
7167    
7168    return {
7169        register : function(btn){
7170            if(!btn.toggleGroup){
7171                return;
7172            }
7173            var g = groups[btn.toggleGroup];
7174            if(!g){
7175                g = groups[btn.toggleGroup] = [];
7176            }
7177            g.push(btn);
7178            btn.on("toggle", toggleGroup);
7179        },
7180        
7181        unregister : function(btn){
7182            if(!btn.toggleGroup){
7183                return;
7184            }
7185            var g = groups[btn.toggleGroup];
7186            if(g){
7187                g.remove(btn);
7188                btn.un("toggle", toggleGroup);
7189            }
7190        }
7191    };
7192 }();/*
7193  * Based on:
7194  * Ext JS Library 1.1.1
7195  * Copyright(c) 2006-2007, Ext JS, LLC.
7196  *
7197  * Originally Released Under LGPL - original licence link has changed is not relivant.
7198  *
7199  * Fork - LGPL
7200  * <script type="text/javascript">
7201  */
7202  
7203 /**
7204  * @class Roo.SplitButton
7205  * @extends Roo.Button
7206  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7207  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7208  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7209  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7210  * @cfg {String} arrowTooltip The title attribute of the arrow
7211  * @constructor
7212  * Create a new menu button
7213  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7214  * @param {Object} config The config object
7215  */
7216 Roo.SplitButton = function(renderTo, config){
7217     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7218     /**
7219      * @event arrowclick
7220      * Fires when this button's arrow is clicked
7221      * @param {SplitButton} this
7222      * @param {EventObject} e The click event
7223      */
7224     this.addEvents({"arrowclick":true});
7225 };
7226
7227 Roo.extend(Roo.SplitButton, Roo.Button, {
7228     render : function(renderTo){
7229         // this is one sweet looking template!
7230         var tpl = new Roo.Template(
7231             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7232             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7233             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
7234             "</tbody></table></td><td>",
7235             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7236             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
7237             "</tbody></table></td></tr></table>"
7238         );
7239         var btn = tpl.append(renderTo, [this.text, this.type], true);
7240         var btnEl = btn.child("button");
7241         if(this.cls){
7242             btn.addClass(this.cls);
7243         }
7244         if(this.icon){
7245             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7246         }
7247         if(this.iconCls){
7248             btnEl.addClass(this.iconCls);
7249             if(!this.cls){
7250                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7251             }
7252         }
7253         this.el = btn;
7254         if(this.handleMouseEvents){
7255             btn.on("mouseover", this.onMouseOver, this);
7256             btn.on("mouseout", this.onMouseOut, this);
7257             btn.on("mousedown", this.onMouseDown, this);
7258             btn.on("mouseup", this.onMouseUp, this);
7259         }
7260         btn.on(this.clickEvent, this.onClick, this);
7261         if(this.tooltip){
7262             if(typeof this.tooltip == 'object'){
7263                 Roo.QuickTips.tips(Roo.apply({
7264                       target: btnEl.id
7265                 }, this.tooltip));
7266             } else {
7267                 btnEl.dom[this.tooltipType] = this.tooltip;
7268             }
7269         }
7270         if(this.arrowTooltip){
7271             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7272         }
7273         if(this.hidden){
7274             this.hide();
7275         }
7276         if(this.disabled){
7277             this.disable();
7278         }
7279         if(this.pressed){
7280             this.el.addClass("x-btn-pressed");
7281         }
7282         if(Roo.isIE && !Roo.isIE7){
7283             this.autoWidth.defer(1, this);
7284         }else{
7285             this.autoWidth();
7286         }
7287         if(this.menu){
7288             this.menu.on("show", this.onMenuShow, this);
7289             this.menu.on("hide", this.onMenuHide, this);
7290         }
7291         this.fireEvent('render', this);
7292     },
7293
7294     // private
7295     autoWidth : function(){
7296         if(this.el){
7297             var tbl = this.el.child("table:first");
7298             var tbl2 = this.el.child("table:last");
7299             this.el.setWidth("auto");
7300             tbl.setWidth("auto");
7301             if(Roo.isIE7 && Roo.isStrict){
7302                 var ib = this.el.child('button:first');
7303                 if(ib && ib.getWidth() > 20){
7304                     ib.clip();
7305                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7306                 }
7307             }
7308             if(this.minWidth){
7309                 if(this.hidden){
7310                     this.el.beginMeasure();
7311                 }
7312                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7313                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7314                 }
7315                 if(this.hidden){
7316                     this.el.endMeasure();
7317                 }
7318             }
7319             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7320         } 
7321     },
7322     /**
7323      * Sets this button's click handler
7324      * @param {Function} handler The function to call when the button is clicked
7325      * @param {Object} scope (optional) Scope for the function passed above
7326      */
7327     setHandler : function(handler, scope){
7328         this.handler = handler;
7329         this.scope = scope;  
7330     },
7331     
7332     /**
7333      * Sets this button's arrow click handler
7334      * @param {Function} handler The function to call when the arrow is clicked
7335      * @param {Object} scope (optional) Scope for the function passed above
7336      */
7337     setArrowHandler : function(handler, scope){
7338         this.arrowHandler = handler;
7339         this.scope = scope;  
7340     },
7341     
7342     /**
7343      * Focus the button
7344      */
7345     focus : function(){
7346         if(this.el){
7347             this.el.child("button:first").focus();
7348         }
7349     },
7350
7351     // private
7352     onClick : function(e){
7353         e.preventDefault();
7354         if(!this.disabled){
7355             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7356                 if(this.menu && !this.menu.isVisible()){
7357                     this.menu.show(this.el, this.menuAlign);
7358                 }
7359                 this.fireEvent("arrowclick", this, e);
7360                 if(this.arrowHandler){
7361                     this.arrowHandler.call(this.scope || this, this, e);
7362                 }
7363             }else{
7364                 this.fireEvent("click", this, e);
7365                 if(this.handler){
7366                     this.handler.call(this.scope || this, this, e);
7367                 }
7368             }
7369         }
7370     },
7371     // private
7372     onMouseDown : function(e){
7373         if(!this.disabled){
7374             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7375         }
7376     },
7377     // private
7378     onMouseUp : function(e){
7379         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7380     }   
7381 });
7382
7383
7384 // backwards compat
7385 Roo.MenuButton = Roo.SplitButton;/*
7386  * Based on:
7387  * Ext JS Library 1.1.1
7388  * Copyright(c) 2006-2007, Ext JS, LLC.
7389  *
7390  * Originally Released Under LGPL - original licence link has changed is not relivant.
7391  *
7392  * Fork - LGPL
7393  * <script type="text/javascript">
7394  */
7395
7396 /**
7397  * @class Roo.Toolbar
7398  * Basic Toolbar class.
7399  * @constructor
7400  * Creates a new Toolbar
7401  * @param {Object} container The config object
7402  */ 
7403 Roo.Toolbar = function(container, buttons, config)
7404 {
7405     /// old consturctor format still supported..
7406     if(container instanceof Array){ // omit the container for later rendering
7407         buttons = container;
7408         config = buttons;
7409         container = null;
7410     }
7411     if (typeof(container) == 'object' && container.xtype) {
7412         config = container;
7413         container = config.container;
7414         buttons = config.buttons || []; // not really - use items!!
7415     }
7416     var xitems = [];
7417     if (config && config.items) {
7418         xitems = config.items;
7419         delete config.items;
7420     }
7421     Roo.apply(this, config);
7422     this.buttons = buttons;
7423     
7424     if(container){
7425         this.render(container);
7426     }
7427     this.xitems = xitems;
7428     Roo.each(xitems, function(b) {
7429         this.add(b);
7430     }, this);
7431     
7432 };
7433
7434 Roo.Toolbar.prototype = {
7435     /**
7436      * @cfg {Array} items
7437      * array of button configs or elements to add (will be converted to a MixedCollection)
7438      */
7439     
7440     /**
7441      * @cfg {String/HTMLElement/Element} container
7442      * The id or element that will contain the toolbar
7443      */
7444     // private
7445     render : function(ct){
7446         this.el = Roo.get(ct);
7447         if(this.cls){
7448             this.el.addClass(this.cls);
7449         }
7450         // using a table allows for vertical alignment
7451         // 100% width is needed by Safari...
7452         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7453         this.tr = this.el.child("tr", true);
7454         var autoId = 0;
7455         this.items = new Roo.util.MixedCollection(false, function(o){
7456             return o.id || ("item" + (++autoId));
7457         });
7458         if(this.buttons){
7459             this.add.apply(this, this.buttons);
7460             delete this.buttons;
7461         }
7462     },
7463
7464     /**
7465      * Adds element(s) to the toolbar -- this function takes a variable number of 
7466      * arguments of mixed type and adds them to the toolbar.
7467      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7468      * <ul>
7469      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7470      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7471      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7472      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7473      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7474      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7475      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7476      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7477      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7478      * </ul>
7479      * @param {Mixed} arg2
7480      * @param {Mixed} etc.
7481      */
7482     add : function(){
7483         var a = arguments, l = a.length;
7484         for(var i = 0; i < l; i++){
7485             this._add(a[i]);
7486         }
7487     },
7488     // private..
7489     _add : function(el) {
7490         
7491         if (el.xtype) {
7492             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7493         }
7494         
7495         if (el.applyTo){ // some kind of form field
7496             return this.addField(el);
7497         } 
7498         if (el.render){ // some kind of Toolbar.Item
7499             return this.addItem(el);
7500         }
7501         if (typeof el == "string"){ // string
7502             if(el == "separator" || el == "-"){
7503                 return this.addSeparator();
7504             }
7505             if (el == " "){
7506                 return this.addSpacer();
7507             }
7508             if(el == "->"){
7509                 return this.addFill();
7510             }
7511             return this.addText(el);
7512             
7513         }
7514         if(el.tagName){ // element
7515             return this.addElement(el);
7516         }
7517         if(typeof el == "object"){ // must be button config?
7518             return this.addButton(el);
7519         }
7520         // and now what?!?!
7521         return false;
7522         
7523     },
7524     
7525     /**
7526      * Add an Xtype element
7527      * @param {Object} xtype Xtype Object
7528      * @return {Object} created Object
7529      */
7530     addxtype : function(e){
7531         return this.add(e);  
7532     },
7533     
7534     /**
7535      * Returns the Element for this toolbar.
7536      * @return {Roo.Element}
7537      */
7538     getEl : function(){
7539         return this.el;  
7540     },
7541     
7542     /**
7543      * Adds a separator
7544      * @return {Roo.Toolbar.Item} The separator item
7545      */
7546     addSeparator : function(){
7547         return this.addItem(new Roo.Toolbar.Separator());
7548     },
7549
7550     /**
7551      * Adds a spacer element
7552      * @return {Roo.Toolbar.Spacer} The spacer item
7553      */
7554     addSpacer : function(){
7555         return this.addItem(new Roo.Toolbar.Spacer());
7556     },
7557
7558     /**
7559      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7560      * @return {Roo.Toolbar.Fill} The fill item
7561      */
7562     addFill : function(){
7563         return this.addItem(new Roo.Toolbar.Fill());
7564     },
7565
7566     /**
7567      * Adds any standard HTML element to the toolbar
7568      * @param {String/HTMLElement/Element} el The element or id of the element to add
7569      * @return {Roo.Toolbar.Item} The element's item
7570      */
7571     addElement : function(el){
7572         return this.addItem(new Roo.Toolbar.Item(el));
7573     },
7574     /**
7575      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7576      * @type Roo.util.MixedCollection  
7577      */
7578     items : false,
7579      
7580     /**
7581      * Adds any Toolbar.Item or subclass
7582      * @param {Roo.Toolbar.Item} item
7583      * @return {Roo.Toolbar.Item} The item
7584      */
7585     addItem : function(item){
7586         var td = this.nextBlock();
7587         item.render(td);
7588         this.items.add(item);
7589         return item;
7590     },
7591     
7592     /**
7593      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7594      * @param {Object/Array} config A button config or array of configs
7595      * @return {Roo.Toolbar.Button/Array}
7596      */
7597     addButton : function(config){
7598         if(config instanceof Array){
7599             var buttons = [];
7600             for(var i = 0, len = config.length; i < len; i++) {
7601                 buttons.push(this.addButton(config[i]));
7602             }
7603             return buttons;
7604         }
7605         var b = config;
7606         if(!(config instanceof Roo.Toolbar.Button)){
7607             b = config.split ?
7608                 new Roo.Toolbar.SplitButton(config) :
7609                 new Roo.Toolbar.Button(config);
7610         }
7611         var td = this.nextBlock();
7612         b.render(td);
7613         this.items.add(b);
7614         return b;
7615     },
7616     
7617     /**
7618      * Adds text to the toolbar
7619      * @param {String} text The text to add
7620      * @return {Roo.Toolbar.Item} The element's item
7621      */
7622     addText : function(text){
7623         return this.addItem(new Roo.Toolbar.TextItem(text));
7624     },
7625     
7626     /**
7627      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7628      * @param {Number} index The index where the item is to be inserted
7629      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7630      * @return {Roo.Toolbar.Button/Item}
7631      */
7632     insertButton : function(index, item){
7633         if(item instanceof Array){
7634             var buttons = [];
7635             for(var i = 0, len = item.length; i < len; i++) {
7636                buttons.push(this.insertButton(index + i, item[i]));
7637             }
7638             return buttons;
7639         }
7640         if (!(item instanceof Roo.Toolbar.Button)){
7641            item = new Roo.Toolbar.Button(item);
7642         }
7643         var td = document.createElement("td");
7644         this.tr.insertBefore(td, this.tr.childNodes[index]);
7645         item.render(td);
7646         this.items.insert(index, item);
7647         return item;
7648     },
7649     
7650     /**
7651      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7652      * @param {Object} config
7653      * @return {Roo.Toolbar.Item} The element's item
7654      */
7655     addDom : function(config, returnEl){
7656         var td = this.nextBlock();
7657         Roo.DomHelper.overwrite(td, config);
7658         var ti = new Roo.Toolbar.Item(td.firstChild);
7659         ti.render(td);
7660         this.items.add(ti);
7661         return ti;
7662     },
7663
7664     /**
7665      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7666      * @type Roo.util.MixedCollection  
7667      */
7668     fields : false,
7669     
7670     /**
7671      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7672      * Note: the field should not have been rendered yet. For a field that has already been
7673      * rendered, use {@link #addElement}.
7674      * @param {Roo.form.Field} field
7675      * @return {Roo.ToolbarItem}
7676      */
7677      
7678       
7679     addField : function(field) {
7680         if (!this.fields) {
7681             var autoId = 0;
7682             this.fields = new Roo.util.MixedCollection(false, function(o){
7683                 return o.id || ("item" + (++autoId));
7684             });
7685
7686         }
7687         
7688         var td = this.nextBlock();
7689         field.render(td);
7690         var ti = new Roo.Toolbar.Item(td.firstChild);
7691         ti.render(td);
7692         this.items.add(ti);
7693         this.fields.add(field);
7694         return ti;
7695     },
7696     /**
7697      * Hide the toolbar
7698      * @method hide
7699      */
7700      
7701       
7702     hide : function()
7703     {
7704         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7705         this.el.child('div').hide();
7706     },
7707     /**
7708      * Show the toolbar
7709      * @method show
7710      */
7711     show : function()
7712     {
7713         this.el.child('div').show();
7714     },
7715       
7716     // private
7717     nextBlock : function(){
7718         var td = document.createElement("td");
7719         this.tr.appendChild(td);
7720         return td;
7721     },
7722
7723     // private
7724     destroy : function(){
7725         if(this.items){ // rendered?
7726             Roo.destroy.apply(Roo, this.items.items);
7727         }
7728         if(this.fields){ // rendered?
7729             Roo.destroy.apply(Roo, this.fields.items);
7730         }
7731         Roo.Element.uncache(this.el, this.tr);
7732     }
7733 };
7734
7735 /**
7736  * @class Roo.Toolbar.Item
7737  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7738  * @constructor
7739  * Creates a new Item
7740  * @param {HTMLElement} el 
7741  */
7742 Roo.Toolbar.Item = function(el){
7743     var cfg = {};
7744     if (typeof (el.xtype) != 'undefined') {
7745         cfg = el;
7746         el = cfg.el;
7747     }
7748     
7749     this.el = Roo.getDom(el);
7750     this.id = Roo.id(this.el);
7751     this.hidden = false;
7752     
7753     this.addEvents({
7754          /**
7755              * @event render
7756              * Fires when the button is rendered
7757              * @param {Button} this
7758              */
7759         'render': true
7760     });
7761     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7762 };
7763 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7764 //Roo.Toolbar.Item.prototype = {
7765     
7766     /**
7767      * Get this item's HTML Element
7768      * @return {HTMLElement}
7769      */
7770     getEl : function(){
7771        return this.el;  
7772     },
7773
7774     // private
7775     render : function(td){
7776         
7777          this.td = td;
7778         td.appendChild(this.el);
7779         
7780         this.fireEvent('render', this);
7781     },
7782     
7783     /**
7784      * Removes and destroys this item.
7785      */
7786     destroy : function(){
7787         this.td.parentNode.removeChild(this.td);
7788     },
7789     
7790     /**
7791      * Shows this item.
7792      */
7793     show: function(){
7794         this.hidden = false;
7795         this.td.style.display = "";
7796     },
7797     
7798     /**
7799      * Hides this item.
7800      */
7801     hide: function(){
7802         this.hidden = true;
7803         this.td.style.display = "none";
7804     },
7805     
7806     /**
7807      * Convenience function for boolean show/hide.
7808      * @param {Boolean} visible true to show/false to hide
7809      */
7810     setVisible: function(visible){
7811         if(visible) {
7812             this.show();
7813         }else{
7814             this.hide();
7815         }
7816     },
7817     
7818     /**
7819      * Try to focus this item.
7820      */
7821     focus : function(){
7822         Roo.fly(this.el).focus();
7823     },
7824     
7825     /**
7826      * Disables this item.
7827      */
7828     disable : function(){
7829         Roo.fly(this.td).addClass("x-item-disabled");
7830         this.disabled = true;
7831         this.el.disabled = true;
7832     },
7833     
7834     /**
7835      * Enables this item.
7836      */
7837     enable : function(){
7838         Roo.fly(this.td).removeClass("x-item-disabled");
7839         this.disabled = false;
7840         this.el.disabled = false;
7841     }
7842 });
7843
7844
7845 /**
7846  * @class Roo.Toolbar.Separator
7847  * @extends Roo.Toolbar.Item
7848  * A simple toolbar separator class
7849  * @constructor
7850  * Creates a new Separator
7851  */
7852 Roo.Toolbar.Separator = function(cfg){
7853     
7854     var s = document.createElement("span");
7855     s.className = "ytb-sep";
7856     if (cfg) {
7857         cfg.el = s;
7858     }
7859     
7860     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7861 };
7862 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7863     enable:Roo.emptyFn,
7864     disable:Roo.emptyFn,
7865     focus:Roo.emptyFn
7866 });
7867
7868 /**
7869  * @class Roo.Toolbar.Spacer
7870  * @extends Roo.Toolbar.Item
7871  * A simple element that adds extra horizontal space to a toolbar.
7872  * @constructor
7873  * Creates a new Spacer
7874  */
7875 Roo.Toolbar.Spacer = function(cfg){
7876     var s = document.createElement("div");
7877     s.className = "ytb-spacer";
7878     if (cfg) {
7879         cfg.el = s;
7880     }
7881     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7882 };
7883 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7884     enable:Roo.emptyFn,
7885     disable:Roo.emptyFn,
7886     focus:Roo.emptyFn
7887 });
7888
7889 /**
7890  * @class Roo.Toolbar.Fill
7891  * @extends Roo.Toolbar.Spacer
7892  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7893  * @constructor
7894  * Creates a new Spacer
7895  */
7896 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7897     // private
7898     render : function(td){
7899         td.style.width = '100%';
7900         Roo.Toolbar.Fill.superclass.render.call(this, td);
7901     }
7902 });
7903
7904 /**
7905  * @class Roo.Toolbar.TextItem
7906  * @extends Roo.Toolbar.Item
7907  * A simple class that renders text directly into a toolbar.
7908  * @constructor
7909  * Creates a new TextItem
7910  * @param {String} text
7911  */
7912 Roo.Toolbar.TextItem = function(cfg){
7913     var  text = cfg || "";
7914     if (typeof(cfg) == 'object') {
7915         text = cfg.text || "";
7916     }  else {
7917         cfg = null;
7918     }
7919     var s = document.createElement("span");
7920     s.className = "ytb-text";
7921     s.innerHTML = text;
7922     if (cfg) {
7923         cfg.el  = s;
7924     }
7925     
7926     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7927 };
7928 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7929     
7930      
7931     enable:Roo.emptyFn,
7932     disable:Roo.emptyFn,
7933     focus:Roo.emptyFn
7934 });
7935
7936 /**
7937  * @class Roo.Toolbar.Button
7938  * @extends Roo.Button
7939  * A button that renders into a toolbar.
7940  * @constructor
7941  * Creates a new Button
7942  * @param {Object} config A standard {@link Roo.Button} config object
7943  */
7944 Roo.Toolbar.Button = function(config){
7945     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7946 };
7947 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7948     render : function(td){
7949         this.td = td;
7950         Roo.Toolbar.Button.superclass.render.call(this, td);
7951     },
7952     
7953     /**
7954      * Removes and destroys this button
7955      */
7956     destroy : function(){
7957         Roo.Toolbar.Button.superclass.destroy.call(this);
7958         this.td.parentNode.removeChild(this.td);
7959     },
7960     
7961     /**
7962      * Shows this button
7963      */
7964     show: function(){
7965         this.hidden = false;
7966         this.td.style.display = "";
7967     },
7968     
7969     /**
7970      * Hides this button
7971      */
7972     hide: function(){
7973         this.hidden = true;
7974         this.td.style.display = "none";
7975     },
7976
7977     /**
7978      * Disables this item
7979      */
7980     disable : function(){
7981         Roo.fly(this.td).addClass("x-item-disabled");
7982         this.disabled = true;
7983     },
7984
7985     /**
7986      * Enables this item
7987      */
7988     enable : function(){
7989         Roo.fly(this.td).removeClass("x-item-disabled");
7990         this.disabled = false;
7991     }
7992 });
7993 // backwards compat
7994 Roo.ToolbarButton = Roo.Toolbar.Button;
7995
7996 /**
7997  * @class Roo.Toolbar.SplitButton
7998  * @extends Roo.SplitButton
7999  * A menu button that renders into a toolbar.
8000  * @constructor
8001  * Creates a new SplitButton
8002  * @param {Object} config A standard {@link Roo.SplitButton} config object
8003  */
8004 Roo.Toolbar.SplitButton = function(config){
8005     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8006 };
8007 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8008     render : function(td){
8009         this.td = td;
8010         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8011     },
8012     
8013     /**
8014      * Removes and destroys this button
8015      */
8016     destroy : function(){
8017         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8018         this.td.parentNode.removeChild(this.td);
8019     },
8020     
8021     /**
8022      * Shows this button
8023      */
8024     show: function(){
8025         this.hidden = false;
8026         this.td.style.display = "";
8027     },
8028     
8029     /**
8030      * Hides this button
8031      */
8032     hide: function(){
8033         this.hidden = true;
8034         this.td.style.display = "none";
8035     }
8036 });
8037
8038 // backwards compat
8039 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8040  * Based on:
8041  * Ext JS Library 1.1.1
8042  * Copyright(c) 2006-2007, Ext JS, LLC.
8043  *
8044  * Originally Released Under LGPL - original licence link has changed is not relivant.
8045  *
8046  * Fork - LGPL
8047  * <script type="text/javascript">
8048  */
8049  
8050 /**
8051  * @class Roo.PagingToolbar
8052  * @extends Roo.Toolbar
8053  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8054  * @constructor
8055  * Create a new PagingToolbar
8056  * @param {Object} config The config object
8057  */
8058 Roo.PagingToolbar = function(el, ds, config)
8059 {
8060     // old args format still supported... - xtype is prefered..
8061     if (typeof(el) == 'object' && el.xtype) {
8062         // created from xtype...
8063         config = el;
8064         ds = el.dataSource;
8065         el = config.container;
8066     }
8067     var items = [];
8068     if (config.items) {
8069         items = config.items;
8070         config.items = [];
8071     }
8072     
8073     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8074     this.ds = ds;
8075     this.cursor = 0;
8076     this.renderButtons(this.el);
8077     this.bind(ds);
8078     
8079     // supprot items array.
8080    
8081     Roo.each(items, function(e) {
8082         this.add(Roo.factory(e));
8083     },this);
8084     
8085 };
8086
8087 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8088     /**
8089      * @cfg {Roo.data.Store} dataSource
8090      * The underlying data store providing the paged data
8091      */
8092     /**
8093      * @cfg {String/HTMLElement/Element} container
8094      * container The id or element that will contain the toolbar
8095      */
8096     /**
8097      * @cfg {Boolean} displayInfo
8098      * True to display the displayMsg (defaults to false)
8099      */
8100     /**
8101      * @cfg {Number} pageSize
8102      * The number of records to display per page (defaults to 20)
8103      */
8104     pageSize: 20,
8105     /**
8106      * @cfg {String} displayMsg
8107      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8108      */
8109     displayMsg : 'Displaying {0} - {1} of {2}',
8110     /**
8111      * @cfg {String} emptyMsg
8112      * The message to display when no records are found (defaults to "No data to display")
8113      */
8114     emptyMsg : 'No data to display',
8115     /**
8116      * Customizable piece of the default paging text (defaults to "Page")
8117      * @type String
8118      */
8119     beforePageText : "Page",
8120     /**
8121      * Customizable piece of the default paging text (defaults to "of %0")
8122      * @type String
8123      */
8124     afterPageText : "of {0}",
8125     /**
8126      * Customizable piece of the default paging text (defaults to "First Page")
8127      * @type String
8128      */
8129     firstText : "First Page",
8130     /**
8131      * Customizable piece of the default paging text (defaults to "Previous Page")
8132      * @type String
8133      */
8134     prevText : "Previous Page",
8135     /**
8136      * Customizable piece of the default paging text (defaults to "Next Page")
8137      * @type String
8138      */
8139     nextText : "Next Page",
8140     /**
8141      * Customizable piece of the default paging text (defaults to "Last Page")
8142      * @type String
8143      */
8144     lastText : "Last Page",
8145     /**
8146      * Customizable piece of the default paging text (defaults to "Refresh")
8147      * @type String
8148      */
8149     refreshText : "Refresh",
8150
8151     // private
8152     renderButtons : function(el){
8153         Roo.PagingToolbar.superclass.render.call(this, el);
8154         this.first = this.addButton({
8155             tooltip: this.firstText,
8156             cls: "x-btn-icon x-grid-page-first",
8157             disabled: true,
8158             handler: this.onClick.createDelegate(this, ["first"])
8159         });
8160         this.prev = this.addButton({
8161             tooltip: this.prevText,
8162             cls: "x-btn-icon x-grid-page-prev",
8163             disabled: true,
8164             handler: this.onClick.createDelegate(this, ["prev"])
8165         });
8166         //this.addSeparator();
8167         this.add(this.beforePageText);
8168         this.field = Roo.get(this.addDom({
8169            tag: "input",
8170            type: "text",
8171            size: "3",
8172            value: "1",
8173            cls: "x-grid-page-number"
8174         }).el);
8175         this.field.on("keydown", this.onPagingKeydown, this);
8176         this.field.on("focus", function(){this.dom.select();});
8177         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8178         this.field.setHeight(18);
8179         //this.addSeparator();
8180         this.next = this.addButton({
8181             tooltip: this.nextText,
8182             cls: "x-btn-icon x-grid-page-next",
8183             disabled: true,
8184             handler: this.onClick.createDelegate(this, ["next"])
8185         });
8186         this.last = this.addButton({
8187             tooltip: this.lastText,
8188             cls: "x-btn-icon x-grid-page-last",
8189             disabled: true,
8190             handler: this.onClick.createDelegate(this, ["last"])
8191         });
8192         //this.addSeparator();
8193         this.loading = this.addButton({
8194             tooltip: this.refreshText,
8195             cls: "x-btn-icon x-grid-loading",
8196             handler: this.onClick.createDelegate(this, ["refresh"])
8197         });
8198
8199         if(this.displayInfo){
8200             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8201         }
8202     },
8203
8204     // private
8205     updateInfo : function(){
8206         if(this.displayEl){
8207             var count = this.ds.getCount();
8208             var msg = count == 0 ?
8209                 this.emptyMsg :
8210                 String.format(
8211                     this.displayMsg,
8212                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8213                 );
8214             this.displayEl.update(msg);
8215         }
8216     },
8217
8218     // private
8219     onLoad : function(ds, r, o){
8220        this.cursor = o.params ? o.params.start : 0;
8221        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8222
8223        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8224        this.field.dom.value = ap;
8225        this.first.setDisabled(ap == 1);
8226        this.prev.setDisabled(ap == 1);
8227        this.next.setDisabled(ap == ps);
8228        this.last.setDisabled(ap == ps);
8229        this.loading.enable();
8230        this.updateInfo();
8231     },
8232
8233     // private
8234     getPageData : function(){
8235         var total = this.ds.getTotalCount();
8236         return {
8237             total : total,
8238             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8239             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8240         };
8241     },
8242
8243     // private
8244     onLoadError : function(){
8245         this.loading.enable();
8246     },
8247
8248     // private
8249     onPagingKeydown : function(e){
8250         var k = e.getKey();
8251         var d = this.getPageData();
8252         if(k == e.RETURN){
8253             var v = this.field.dom.value, pageNum;
8254             if(!v || isNaN(pageNum = parseInt(v, 10))){
8255                 this.field.dom.value = d.activePage;
8256                 return;
8257             }
8258             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8259             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8260             e.stopEvent();
8261         }
8262         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
8263         {
8264           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8265           this.field.dom.value = pageNum;
8266           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8267           e.stopEvent();
8268         }
8269         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8270         {
8271           var v = this.field.dom.value, pageNum; 
8272           var increment = (e.shiftKey) ? 10 : 1;
8273           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8274             increment *= -1;
8275           }
8276           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8277             this.field.dom.value = d.activePage;
8278             return;
8279           }
8280           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8281           {
8282             this.field.dom.value = parseInt(v, 10) + increment;
8283             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8284             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8285           }
8286           e.stopEvent();
8287         }
8288     },
8289
8290     // private
8291     beforeLoad : function(){
8292         if(this.loading){
8293             this.loading.disable();
8294         }
8295     },
8296
8297     // private
8298     onClick : function(which){
8299         var ds = this.ds;
8300         switch(which){
8301             case "first":
8302                 ds.load({params:{start: 0, limit: this.pageSize}});
8303             break;
8304             case "prev":
8305                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8306             break;
8307             case "next":
8308                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8309             break;
8310             case "last":
8311                 var total = ds.getTotalCount();
8312                 var extra = total % this.pageSize;
8313                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8314                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8315             break;
8316             case "refresh":
8317                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8318             break;
8319         }
8320     },
8321
8322     /**
8323      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8324      * @param {Roo.data.Store} store The data store to unbind
8325      */
8326     unbind : function(ds){
8327         ds.un("beforeload", this.beforeLoad, this);
8328         ds.un("load", this.onLoad, this);
8329         ds.un("loadexception", this.onLoadError, this);
8330         ds.un("remove", this.updateInfo, this);
8331         ds.un("add", this.updateInfo, this);
8332         this.ds = undefined;
8333     },
8334
8335     /**
8336      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8337      * @param {Roo.data.Store} store The data store to bind
8338      */
8339     bind : function(ds){
8340         ds.on("beforeload", this.beforeLoad, this);
8341         ds.on("load", this.onLoad, this);
8342         ds.on("loadexception", this.onLoadError, this);
8343         ds.on("remove", this.updateInfo, this);
8344         ds.on("add", this.updateInfo, this);
8345         this.ds = ds;
8346     }
8347 });/*
8348  * Based on:
8349  * Ext JS Library 1.1.1
8350  * Copyright(c) 2006-2007, Ext JS, LLC.
8351  *
8352  * Originally Released Under LGPL - original licence link has changed is not relivant.
8353  *
8354  * Fork - LGPL
8355  * <script type="text/javascript">
8356  */
8357
8358 /**
8359  * @class Roo.Resizable
8360  * @extends Roo.util.Observable
8361  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8362  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8363  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
8364  * the element will be wrapped for you automatically.</p>
8365  * <p>Here is the list of valid resize handles:</p>
8366  * <pre>
8367 Value   Description
8368 ------  -------------------
8369  'n'     north
8370  's'     south
8371  'e'     east
8372  'w'     west
8373  'nw'    northwest
8374  'sw'    southwest
8375  'se'    southeast
8376  'ne'    northeast
8377  'hd'    horizontal drag
8378  'all'   all
8379 </pre>
8380  * <p>Here's an example showing the creation of a typical Resizable:</p>
8381  * <pre><code>
8382 var resizer = new Roo.Resizable("element-id", {
8383     handles: 'all',
8384     minWidth: 200,
8385     minHeight: 100,
8386     maxWidth: 500,
8387     maxHeight: 400,
8388     pinned: true
8389 });
8390 resizer.on("resize", myHandler);
8391 </code></pre>
8392  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8393  * resizer.east.setDisplayed(false);</p>
8394  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8395  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8396  * resize operation's new size (defaults to [0, 0])
8397  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8398  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8399  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8400  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8401  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8402  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8403  * @cfg {Number} width The width of the element in pixels (defaults to null)
8404  * @cfg {Number} height The height of the element in pixels (defaults to null)
8405  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8406  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8407  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8408  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8409  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8410  * in favor of the handles config option (defaults to false)
8411  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8412  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8413  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8414  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8415  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8416  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8417  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8418  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8419  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8420  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8421  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8422  * @constructor
8423  * Create a new resizable component
8424  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8425  * @param {Object} config configuration options
8426   */
8427 Roo.Resizable = function(el, config)
8428 {
8429     this.el = Roo.get(el);
8430
8431     if(config && config.wrap){
8432         config.resizeChild = this.el;
8433         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8434         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8435         this.el.setStyle("overflow", "hidden");
8436         this.el.setPositioning(config.resizeChild.getPositioning());
8437         config.resizeChild.clearPositioning();
8438         if(!config.width || !config.height){
8439             var csize = config.resizeChild.getSize();
8440             this.el.setSize(csize.width, csize.height);
8441         }
8442         if(config.pinned && !config.adjustments){
8443             config.adjustments = "auto";
8444         }
8445     }
8446
8447     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8448     this.proxy.unselectable();
8449     this.proxy.enableDisplayMode('block');
8450
8451     Roo.apply(this, config);
8452
8453     if(this.pinned){
8454         this.disableTrackOver = true;
8455         this.el.addClass("x-resizable-pinned");
8456     }
8457     // if the element isn't positioned, make it relative
8458     var position = this.el.getStyle("position");
8459     if(position != "absolute" && position != "fixed"){
8460         this.el.setStyle("position", "relative");
8461     }
8462     if(!this.handles){ // no handles passed, must be legacy style
8463         this.handles = 's,e,se';
8464         if(this.multiDirectional){
8465             this.handles += ',n,w';
8466         }
8467     }
8468     if(this.handles == "all"){
8469         this.handles = "n s e w ne nw se sw";
8470     }
8471     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8472     var ps = Roo.Resizable.positions;
8473     for(var i = 0, len = hs.length; i < len; i++){
8474         if(hs[i] && ps[hs[i]]){
8475             var pos = ps[hs[i]];
8476             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8477         }
8478     }
8479     // legacy
8480     this.corner = this.southeast;
8481     
8482     // updateBox = the box can move..
8483     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8484         this.updateBox = true;
8485     }
8486
8487     this.activeHandle = null;
8488
8489     if(this.resizeChild){
8490         if(typeof this.resizeChild == "boolean"){
8491             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8492         }else{
8493             this.resizeChild = Roo.get(this.resizeChild, true);
8494         }
8495     }
8496     
8497     if(this.adjustments == "auto"){
8498         var rc = this.resizeChild;
8499         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8500         if(rc && (hw || hn)){
8501             rc.position("relative");
8502             rc.setLeft(hw ? hw.el.getWidth() : 0);
8503             rc.setTop(hn ? hn.el.getHeight() : 0);
8504         }
8505         this.adjustments = [
8506             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8507             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8508         ];
8509     }
8510
8511     if(this.draggable){
8512         this.dd = this.dynamic ?
8513             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8514         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8515     }
8516
8517     // public events
8518     this.addEvents({
8519         /**
8520          * @event beforeresize
8521          * Fired before resize is allowed. Set enabled to false to cancel resize.
8522          * @param {Roo.Resizable} this
8523          * @param {Roo.EventObject} e The mousedown event
8524          */
8525         "beforeresize" : true,
8526         /**
8527          * @event resizing
8528          * Fired a resizing.
8529          * @param {Roo.Resizable} this
8530          * @param {Number} x The new x position
8531          * @param {Number} y The new y position
8532          * @param {Number} w The new w width
8533          * @param {Number} h The new h hight
8534          * @param {Roo.EventObject} e The mouseup event
8535          */
8536         "resizing" : true,
8537         /**
8538          * @event resize
8539          * Fired after a resize.
8540          * @param {Roo.Resizable} this
8541          * @param {Number} width The new width
8542          * @param {Number} height The new height
8543          * @param {Roo.EventObject} e The mouseup event
8544          */
8545         "resize" : true
8546     });
8547
8548     if(this.width !== null && this.height !== null){
8549         this.resizeTo(this.width, this.height);
8550     }else{
8551         this.updateChildSize();
8552     }
8553     if(Roo.isIE){
8554         this.el.dom.style.zoom = 1;
8555     }
8556     Roo.Resizable.superclass.constructor.call(this);
8557 };
8558
8559 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8560         resizeChild : false,
8561         adjustments : [0, 0],
8562         minWidth : 5,
8563         minHeight : 5,
8564         maxWidth : 10000,
8565         maxHeight : 10000,
8566         enabled : true,
8567         animate : false,
8568         duration : .35,
8569         dynamic : false,
8570         handles : false,
8571         multiDirectional : false,
8572         disableTrackOver : false,
8573         easing : 'easeOutStrong',
8574         widthIncrement : 0,
8575         heightIncrement : 0,
8576         pinned : false,
8577         width : null,
8578         height : null,
8579         preserveRatio : false,
8580         transparent: false,
8581         minX: 0,
8582         minY: 0,
8583         draggable: false,
8584
8585         /**
8586          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8587          */
8588         constrainTo: undefined,
8589         /**
8590          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8591          */
8592         resizeRegion: undefined,
8593
8594
8595     /**
8596      * Perform a manual resize
8597      * @param {Number} width
8598      * @param {Number} height
8599      */
8600     resizeTo : function(width, height){
8601         this.el.setSize(width, height);
8602         this.updateChildSize();
8603         this.fireEvent("resize", this, width, height, null);
8604     },
8605
8606     // private
8607     startSizing : function(e, handle){
8608         this.fireEvent("beforeresize", this, e);
8609         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8610
8611             if(!this.overlay){
8612                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8613                 this.overlay.unselectable();
8614                 this.overlay.enableDisplayMode("block");
8615                 this.overlay.on("mousemove", this.onMouseMove, this);
8616                 this.overlay.on("mouseup", this.onMouseUp, this);
8617             }
8618             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8619
8620             this.resizing = true;
8621             this.startBox = this.el.getBox();
8622             this.startPoint = e.getXY();
8623             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8624                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8625
8626             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8627             this.overlay.show();
8628
8629             if(this.constrainTo) {
8630                 var ct = Roo.get(this.constrainTo);
8631                 this.resizeRegion = ct.getRegion().adjust(
8632                     ct.getFrameWidth('t'),
8633                     ct.getFrameWidth('l'),
8634                     -ct.getFrameWidth('b'),
8635                     -ct.getFrameWidth('r')
8636                 );
8637             }
8638
8639             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8640             this.proxy.show();
8641             this.proxy.setBox(this.startBox);
8642             if(!this.dynamic){
8643                 this.proxy.setStyle('visibility', 'visible');
8644             }
8645         }
8646     },
8647
8648     // private
8649     onMouseDown : function(handle, e){
8650         if(this.enabled){
8651             e.stopEvent();
8652             this.activeHandle = handle;
8653             this.startSizing(e, handle);
8654         }
8655     },
8656
8657     // private
8658     onMouseUp : function(e){
8659         var size = this.resizeElement();
8660         this.resizing = false;
8661         this.handleOut();
8662         this.overlay.hide();
8663         this.proxy.hide();
8664         this.fireEvent("resize", this, size.width, size.height, e);
8665     },
8666
8667     // private
8668     updateChildSize : function(){
8669         
8670         if(this.resizeChild){
8671             var el = this.el;
8672             var child = this.resizeChild;
8673             var adj = this.adjustments;
8674             if(el.dom.offsetWidth){
8675                 var b = el.getSize(true);
8676                 child.setSize(b.width+adj[0], b.height+adj[1]);
8677             }
8678             // Second call here for IE
8679             // The first call enables instant resizing and
8680             // the second call corrects scroll bars if they
8681             // exist
8682             if(Roo.isIE){
8683                 setTimeout(function(){
8684                     if(el.dom.offsetWidth){
8685                         var b = el.getSize(true);
8686                         child.setSize(b.width+adj[0], b.height+adj[1]);
8687                     }
8688                 }, 10);
8689             }
8690         }
8691     },
8692
8693     // private
8694     snap : function(value, inc, min){
8695         if(!inc || !value) {
8696             return value;
8697         }
8698         var newValue = value;
8699         var m = value % inc;
8700         if(m > 0){
8701             if(m > (inc/2)){
8702                 newValue = value + (inc-m);
8703             }else{
8704                 newValue = value - m;
8705             }
8706         }
8707         return Math.max(min, newValue);
8708     },
8709
8710     // private
8711     resizeElement : function(){
8712         var box = this.proxy.getBox();
8713         if(this.updateBox){
8714             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8715         }else{
8716             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8717         }
8718         this.updateChildSize();
8719         if(!this.dynamic){
8720             this.proxy.hide();
8721         }
8722         return box;
8723     },
8724
8725     // private
8726     constrain : function(v, diff, m, mx){
8727         if(v - diff < m){
8728             diff = v - m;
8729         }else if(v - diff > mx){
8730             diff = mx - v;
8731         }
8732         return diff;
8733     },
8734
8735     // private
8736     onMouseMove : function(e){
8737         
8738         if(this.enabled){
8739             try{// try catch so if something goes wrong the user doesn't get hung
8740
8741             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8742                 return;
8743             }
8744
8745             //var curXY = this.startPoint;
8746             var curSize = this.curSize || this.startBox;
8747             var x = this.startBox.x, y = this.startBox.y;
8748             var ox = x, oy = y;
8749             var w = curSize.width, h = curSize.height;
8750             var ow = w, oh = h;
8751             var mw = this.minWidth, mh = this.minHeight;
8752             var mxw = this.maxWidth, mxh = this.maxHeight;
8753             var wi = this.widthIncrement;
8754             var hi = this.heightIncrement;
8755
8756             var eventXY = e.getXY();
8757             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8758             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8759
8760             var pos = this.activeHandle.position;
8761
8762             switch(pos){
8763                 case "east":
8764                     w += diffX;
8765                     w = Math.min(Math.max(mw, w), mxw);
8766                     break;
8767              
8768                 case "south":
8769                     h += diffY;
8770                     h = Math.min(Math.max(mh, h), mxh);
8771                     break;
8772                 case "southeast":
8773                     w += diffX;
8774                     h += diffY;
8775                     w = Math.min(Math.max(mw, w), mxw);
8776                     h = Math.min(Math.max(mh, h), mxh);
8777                     break;
8778                 case "north":
8779                     diffY = this.constrain(h, diffY, mh, mxh);
8780                     y += diffY;
8781                     h -= diffY;
8782                     break;
8783                 case "hdrag":
8784                     
8785                     if (wi) {
8786                         var adiffX = Math.abs(diffX);
8787                         var sub = (adiffX % wi); // how much 
8788                         if (sub > (wi/2)) { // far enough to snap
8789                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8790                         } else {
8791                             // remove difference.. 
8792                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8793                         }
8794                     }
8795                     x += diffX;
8796                     x = Math.max(this.minX, x);
8797                     break;
8798                 case "west":
8799                     diffX = this.constrain(w, diffX, mw, mxw);
8800                     x += diffX;
8801                     w -= diffX;
8802                     break;
8803                 case "northeast":
8804                     w += diffX;
8805                     w = Math.min(Math.max(mw, w), mxw);
8806                     diffY = this.constrain(h, diffY, mh, mxh);
8807                     y += diffY;
8808                     h -= diffY;
8809                     break;
8810                 case "northwest":
8811                     diffX = this.constrain(w, diffX, mw, mxw);
8812                     diffY = this.constrain(h, diffY, mh, mxh);
8813                     y += diffY;
8814                     h -= diffY;
8815                     x += diffX;
8816                     w -= diffX;
8817                     break;
8818                case "southwest":
8819                     diffX = this.constrain(w, diffX, mw, mxw);
8820                     h += diffY;
8821                     h = Math.min(Math.max(mh, h), mxh);
8822                     x += diffX;
8823                     w -= diffX;
8824                     break;
8825             }
8826
8827             var sw = this.snap(w, wi, mw);
8828             var sh = this.snap(h, hi, mh);
8829             if(sw != w || sh != h){
8830                 switch(pos){
8831                     case "northeast":
8832                         y -= sh - h;
8833                     break;
8834                     case "north":
8835                         y -= sh - h;
8836                         break;
8837                     case "southwest":
8838                         x -= sw - w;
8839                     break;
8840                     case "west":
8841                         x -= sw - w;
8842                         break;
8843                     case "northwest":
8844                         x -= sw - w;
8845                         y -= sh - h;
8846                     break;
8847                 }
8848                 w = sw;
8849                 h = sh;
8850             }
8851
8852             if(this.preserveRatio){
8853                 switch(pos){
8854                     case "southeast":
8855                     case "east":
8856                         h = oh * (w/ow);
8857                         h = Math.min(Math.max(mh, h), mxh);
8858                         w = ow * (h/oh);
8859                        break;
8860                     case "south":
8861                         w = ow * (h/oh);
8862                         w = Math.min(Math.max(mw, w), mxw);
8863                         h = oh * (w/ow);
8864                         break;
8865                     case "northeast":
8866                         w = ow * (h/oh);
8867                         w = Math.min(Math.max(mw, w), mxw);
8868                         h = oh * (w/ow);
8869                     break;
8870                     case "north":
8871                         var tw = w;
8872                         w = ow * (h/oh);
8873                         w = Math.min(Math.max(mw, w), mxw);
8874                         h = oh * (w/ow);
8875                         x += (tw - w) / 2;
8876                         break;
8877                     case "southwest":
8878                         h = oh * (w/ow);
8879                         h = Math.min(Math.max(mh, h), mxh);
8880                         var tw = w;
8881                         w = ow * (h/oh);
8882                         x += tw - w;
8883                         break;
8884                     case "west":
8885                         var th = h;
8886                         h = oh * (w/ow);
8887                         h = Math.min(Math.max(mh, h), mxh);
8888                         y += (th - h) / 2;
8889                         var tw = w;
8890                         w = ow * (h/oh);
8891                         x += tw - w;
8892                        break;
8893                     case "northwest":
8894                         var tw = w;
8895                         var th = h;
8896                         h = oh * (w/ow);
8897                         h = Math.min(Math.max(mh, h), mxh);
8898                         w = ow * (h/oh);
8899                         y += th - h;
8900                         x += tw - w;
8901                        break;
8902
8903                 }
8904             }
8905             if (pos == 'hdrag') {
8906                 w = ow;
8907             }
8908             this.proxy.setBounds(x, y, w, h);
8909             if(this.dynamic){
8910                 this.resizeElement();
8911             }
8912             }catch(e){}
8913         }
8914         this.fireEvent("resizing", this, x, y, w, h, e);
8915     },
8916
8917     // private
8918     handleOver : function(){
8919         if(this.enabled){
8920             this.el.addClass("x-resizable-over");
8921         }
8922     },
8923
8924     // private
8925     handleOut : function(){
8926         if(!this.resizing){
8927             this.el.removeClass("x-resizable-over");
8928         }
8929     },
8930
8931     /**
8932      * Returns the element this component is bound to.
8933      * @return {Roo.Element}
8934      */
8935     getEl : function(){
8936         return this.el;
8937     },
8938
8939     /**
8940      * Returns the resizeChild element (or null).
8941      * @return {Roo.Element}
8942      */
8943     getResizeChild : function(){
8944         return this.resizeChild;
8945     },
8946     groupHandler : function()
8947     {
8948         
8949     },
8950     /**
8951      * Destroys this resizable. If the element was wrapped and
8952      * removeEl is not true then the element remains.
8953      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8954      */
8955     destroy : function(removeEl){
8956         this.proxy.remove();
8957         if(this.overlay){
8958             this.overlay.removeAllListeners();
8959             this.overlay.remove();
8960         }
8961         var ps = Roo.Resizable.positions;
8962         for(var k in ps){
8963             if(typeof ps[k] != "function" && this[ps[k]]){
8964                 var h = this[ps[k]];
8965                 h.el.removeAllListeners();
8966                 h.el.remove();
8967             }
8968         }
8969         if(removeEl){
8970             this.el.update("");
8971             this.el.remove();
8972         }
8973     }
8974 });
8975
8976 // private
8977 // hash to map config positions to true positions
8978 Roo.Resizable.positions = {
8979     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8980     hd: "hdrag"
8981 };
8982
8983 // private
8984 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8985     if(!this.tpl){
8986         // only initialize the template if resizable is used
8987         var tpl = Roo.DomHelper.createTemplate(
8988             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8989         );
8990         tpl.compile();
8991         Roo.Resizable.Handle.prototype.tpl = tpl;
8992     }
8993     this.position = pos;
8994     this.rz = rz;
8995     // show north drag fro topdra
8996     var handlepos = pos == 'hdrag' ? 'north' : pos;
8997     
8998     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8999     if (pos == 'hdrag') {
9000         this.el.setStyle('cursor', 'pointer');
9001     }
9002     this.el.unselectable();
9003     if(transparent){
9004         this.el.setOpacity(0);
9005     }
9006     this.el.on("mousedown", this.onMouseDown, this);
9007     if(!disableTrackOver){
9008         this.el.on("mouseover", this.onMouseOver, this);
9009         this.el.on("mouseout", this.onMouseOut, this);
9010     }
9011 };
9012
9013 // private
9014 Roo.Resizable.Handle.prototype = {
9015     afterResize : function(rz){
9016         Roo.log('after?');
9017         // do nothing
9018     },
9019     // private
9020     onMouseDown : function(e){
9021         this.rz.onMouseDown(this, e);
9022     },
9023     // private
9024     onMouseOver : function(e){
9025         this.rz.handleOver(this, e);
9026     },
9027     // private
9028     onMouseOut : function(e){
9029         this.rz.handleOut(this, e);
9030     }
9031 };/*
9032  * Based on:
9033  * Ext JS Library 1.1.1
9034  * Copyright(c) 2006-2007, Ext JS, LLC.
9035  *
9036  * Originally Released Under LGPL - original licence link has changed is not relivant.
9037  *
9038  * Fork - LGPL
9039  * <script type="text/javascript">
9040  */
9041
9042 /**
9043  * @class Roo.Editor
9044  * @extends Roo.Component
9045  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9046  * @constructor
9047  * Create a new Editor
9048  * @param {Roo.form.Field} field The Field object (or descendant)
9049  * @param {Object} config The config object
9050  */
9051 Roo.Editor = function(field, config){
9052     Roo.Editor.superclass.constructor.call(this, config);
9053     this.field = field;
9054     this.addEvents({
9055         /**
9056              * @event beforestartedit
9057              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9058              * false from the handler of this event.
9059              * @param {Editor} this
9060              * @param {Roo.Element} boundEl The underlying element bound to this editor
9061              * @param {Mixed} value The field value being set
9062              */
9063         "beforestartedit" : true,
9064         /**
9065              * @event startedit
9066              * Fires when this editor is displayed
9067              * @param {Roo.Element} boundEl The underlying element bound to this editor
9068              * @param {Mixed} value The starting field value
9069              */
9070         "startedit" : true,
9071         /**
9072              * @event beforecomplete
9073              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9074              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9075              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9076              * event will not fire since no edit actually occurred.
9077              * @param {Editor} this
9078              * @param {Mixed} value The current field value
9079              * @param {Mixed} startValue The original field value
9080              */
9081         "beforecomplete" : true,
9082         /**
9083              * @event complete
9084              * Fires after editing is complete and any changed value has been written to the underlying field.
9085              * @param {Editor} this
9086              * @param {Mixed} value The current field value
9087              * @param {Mixed} startValue The original field value
9088              */
9089         "complete" : true,
9090         /**
9091          * @event specialkey
9092          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9093          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9094          * @param {Roo.form.Field} this
9095          * @param {Roo.EventObject} e The event object
9096          */
9097         "specialkey" : true
9098     });
9099 };
9100
9101 Roo.extend(Roo.Editor, Roo.Component, {
9102     /**
9103      * @cfg {Boolean/String} autosize
9104      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9105      * or "height" to adopt the height only (defaults to false)
9106      */
9107     /**
9108      * @cfg {Boolean} revertInvalid
9109      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9110      * validation fails (defaults to true)
9111      */
9112     /**
9113      * @cfg {Boolean} ignoreNoChange
9114      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9115      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9116      * will never be ignored.
9117      */
9118     /**
9119      * @cfg {Boolean} hideEl
9120      * False to keep the bound element visible while the editor is displayed (defaults to true)
9121      */
9122     /**
9123      * @cfg {Mixed} value
9124      * The data value of the underlying field (defaults to "")
9125      */
9126     value : "",
9127     /**
9128      * @cfg {String} alignment
9129      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9130      */
9131     alignment: "c-c?",
9132     /**
9133      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9134      * for bottom-right shadow (defaults to "frame")
9135      */
9136     shadow : "frame",
9137     /**
9138      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9139      */
9140     constrain : false,
9141     /**
9142      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9143      */
9144     completeOnEnter : false,
9145     /**
9146      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9147      */
9148     cancelOnEsc : false,
9149     /**
9150      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9151      */
9152     updateEl : false,
9153
9154     // private
9155     onRender : function(ct, position){
9156         this.el = new Roo.Layer({
9157             shadow: this.shadow,
9158             cls: "x-editor",
9159             parentEl : ct,
9160             shim : this.shim,
9161             shadowOffset:4,
9162             id: this.id,
9163             constrain: this.constrain
9164         });
9165         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9166         if(this.field.msgTarget != 'title'){
9167             this.field.msgTarget = 'qtip';
9168         }
9169         this.field.render(this.el);
9170         if(Roo.isGecko){
9171             this.field.el.dom.setAttribute('autocomplete', 'off');
9172         }
9173         this.field.on("specialkey", this.onSpecialKey, this);
9174         if(this.swallowKeys){
9175             this.field.el.swallowEvent(['keydown','keypress']);
9176         }
9177         this.field.show();
9178         this.field.on("blur", this.onBlur, this);
9179         if(this.field.grow){
9180             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9181         }
9182     },
9183
9184     onSpecialKey : function(field, e)
9185     {
9186         //Roo.log('editor onSpecialKey');
9187         if(this.completeOnEnter && e.getKey() == e.ENTER){
9188             e.stopEvent();
9189             this.completeEdit();
9190             return;
9191         }
9192         // do not fire special key otherwise it might hide close the editor...
9193         if(e.getKey() == e.ENTER){    
9194             return;
9195         }
9196         if(this.cancelOnEsc && e.getKey() == e.ESC){
9197             this.cancelEdit();
9198             return;
9199         } 
9200         this.fireEvent('specialkey', field, e);
9201     
9202     },
9203
9204     /**
9205      * Starts the editing process and shows the editor.
9206      * @param {String/HTMLElement/Element} el The element to edit
9207      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9208       * to the innerHTML of el.
9209      */
9210     startEdit : function(el, value){
9211         if(this.editing){
9212             this.completeEdit();
9213         }
9214         this.boundEl = Roo.get(el);
9215         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9216         if(!this.rendered){
9217             this.render(this.parentEl || document.body);
9218         }
9219         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9220             return;
9221         }
9222         this.startValue = v;
9223         this.field.setValue(v);
9224         if(this.autoSize){
9225             var sz = this.boundEl.getSize();
9226             switch(this.autoSize){
9227                 case "width":
9228                 this.setSize(sz.width,  "");
9229                 break;
9230                 case "height":
9231                 this.setSize("",  sz.height);
9232                 break;
9233                 default:
9234                 this.setSize(sz.width,  sz.height);
9235             }
9236         }
9237         this.el.alignTo(this.boundEl, this.alignment);
9238         this.editing = true;
9239         if(Roo.QuickTips){
9240             Roo.QuickTips.disable();
9241         }
9242         this.show();
9243     },
9244
9245     /**
9246      * Sets the height and width of this editor.
9247      * @param {Number} width The new width
9248      * @param {Number} height The new height
9249      */
9250     setSize : function(w, h){
9251         this.field.setSize(w, h);
9252         if(this.el){
9253             this.el.sync();
9254         }
9255     },
9256
9257     /**
9258      * Realigns the editor to the bound field based on the current alignment config value.
9259      */
9260     realign : function(){
9261         this.el.alignTo(this.boundEl, this.alignment);
9262     },
9263
9264     /**
9265      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9266      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9267      */
9268     completeEdit : function(remainVisible){
9269         if(!this.editing){
9270             return;
9271         }
9272         var v = this.getValue();
9273         if(this.revertInvalid !== false && !this.field.isValid()){
9274             v = this.startValue;
9275             this.cancelEdit(true);
9276         }
9277         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9278             this.editing = false;
9279             this.hide();
9280             return;
9281         }
9282         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9283             this.editing = false;
9284             if(this.updateEl && this.boundEl){
9285                 this.boundEl.update(v);
9286             }
9287             if(remainVisible !== true){
9288                 this.hide();
9289             }
9290             this.fireEvent("complete", this, v, this.startValue);
9291         }
9292     },
9293
9294     // private
9295     onShow : function(){
9296         this.el.show();
9297         if(this.hideEl !== false){
9298             this.boundEl.hide();
9299         }
9300         this.field.show();
9301         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9302             this.fixIEFocus = true;
9303             this.deferredFocus.defer(50, this);
9304         }else{
9305             this.field.focus();
9306         }
9307         this.fireEvent("startedit", this.boundEl, this.startValue);
9308     },
9309
9310     deferredFocus : function(){
9311         if(this.editing){
9312             this.field.focus();
9313         }
9314     },
9315
9316     /**
9317      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9318      * reverted to the original starting value.
9319      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9320      * cancel (defaults to false)
9321      */
9322     cancelEdit : function(remainVisible){
9323         if(this.editing){
9324             this.setValue(this.startValue);
9325             if(remainVisible !== true){
9326                 this.hide();
9327             }
9328         }
9329     },
9330
9331     // private
9332     onBlur : function(){
9333         if(this.allowBlur !== true && this.editing){
9334             this.completeEdit();
9335         }
9336     },
9337
9338     // private
9339     onHide : function(){
9340         if(this.editing){
9341             this.completeEdit();
9342             return;
9343         }
9344         this.field.blur();
9345         if(this.field.collapse){
9346             this.field.collapse();
9347         }
9348         this.el.hide();
9349         if(this.hideEl !== false){
9350             this.boundEl.show();
9351         }
9352         if(Roo.QuickTips){
9353             Roo.QuickTips.enable();
9354         }
9355     },
9356
9357     /**
9358      * Sets the data value of the editor
9359      * @param {Mixed} value Any valid value supported by the underlying field
9360      */
9361     setValue : function(v){
9362         this.field.setValue(v);
9363     },
9364
9365     /**
9366      * Gets the data value of the editor
9367      * @return {Mixed} The data value
9368      */
9369     getValue : function(){
9370         return this.field.getValue();
9371     }
9372 });/*
9373  * Based on:
9374  * Ext JS Library 1.1.1
9375  * Copyright(c) 2006-2007, Ext JS, LLC.
9376  *
9377  * Originally Released Under LGPL - original licence link has changed is not relivant.
9378  *
9379  * Fork - LGPL
9380  * <script type="text/javascript">
9381  */
9382  
9383 /**
9384  * @class Roo.BasicDialog
9385  * @extends Roo.util.Observable
9386  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9387  * <pre><code>
9388 var dlg = new Roo.BasicDialog("my-dlg", {
9389     height: 200,
9390     width: 300,
9391     minHeight: 100,
9392     minWidth: 150,
9393     modal: true,
9394     proxyDrag: true,
9395     shadow: true
9396 });
9397 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9398 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9399 dlg.addButton('Cancel', dlg.hide, dlg);
9400 dlg.show();
9401 </code></pre>
9402   <b>A Dialog should always be a direct child of the body element.</b>
9403  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9404  * @cfg {String} title Default text to display in the title bar (defaults to null)
9405  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9406  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9407  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9408  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9409  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9410  * (defaults to null with no animation)
9411  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9412  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9413  * property for valid values (defaults to 'all')
9414  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9415  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9416  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9417  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9418  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9419  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9420  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9421  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9422  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9423  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9424  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9425  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9426  * draggable = true (defaults to false)
9427  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9428  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9429  * shadow (defaults to false)
9430  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9431  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9432  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9433  * @cfg {Array} buttons Array of buttons
9434  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9435  * @constructor
9436  * Create a new BasicDialog.
9437  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9438  * @param {Object} config Configuration options
9439  */
9440 Roo.BasicDialog = function(el, config){
9441     this.el = Roo.get(el);
9442     var dh = Roo.DomHelper;
9443     if(!this.el && config && config.autoCreate){
9444         if(typeof config.autoCreate == "object"){
9445             if(!config.autoCreate.id){
9446                 config.autoCreate.id = el;
9447             }
9448             this.el = dh.append(document.body,
9449                         config.autoCreate, true);
9450         }else{
9451             this.el = dh.append(document.body,
9452                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9453         }
9454     }
9455     el = this.el;
9456     el.setDisplayed(true);
9457     el.hide = this.hideAction;
9458     this.id = el.id;
9459     el.addClass("x-dlg");
9460
9461     Roo.apply(this, config);
9462
9463     this.proxy = el.createProxy("x-dlg-proxy");
9464     this.proxy.hide = this.hideAction;
9465     this.proxy.setOpacity(.5);
9466     this.proxy.hide();
9467
9468     if(config.width){
9469         el.setWidth(config.width);
9470     }
9471     if(config.height){
9472         el.setHeight(config.height);
9473     }
9474     this.size = el.getSize();
9475     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9476         this.xy = [config.x,config.y];
9477     }else{
9478         this.xy = el.getCenterXY(true);
9479     }
9480     /** The header element @type Roo.Element */
9481     this.header = el.child("> .x-dlg-hd");
9482     /** The body element @type Roo.Element */
9483     this.body = el.child("> .x-dlg-bd");
9484     /** The footer element @type Roo.Element */
9485     this.footer = el.child("> .x-dlg-ft");
9486
9487     if(!this.header){
9488         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9489     }
9490     if(!this.body){
9491         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9492     }
9493
9494     this.header.unselectable();
9495     if(this.title){
9496         this.header.update(this.title);
9497     }
9498     // this element allows the dialog to be focused for keyboard event
9499     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9500     this.focusEl.swallowEvent("click", true);
9501
9502     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9503
9504     // wrap the body and footer for special rendering
9505     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9506     if(this.footer){
9507         this.bwrap.dom.appendChild(this.footer.dom);
9508     }
9509
9510     this.bg = this.el.createChild({
9511         tag: "div", cls:"x-dlg-bg",
9512         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9513     });
9514     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9515
9516
9517     if(this.autoScroll !== false && !this.autoTabs){
9518         this.body.setStyle("overflow", "auto");
9519     }
9520
9521     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9522
9523     if(this.closable !== false){
9524         this.el.addClass("x-dlg-closable");
9525         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9526         this.close.on("click", this.closeClick, this);
9527         this.close.addClassOnOver("x-dlg-close-over");
9528     }
9529     if(this.collapsible !== false){
9530         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9531         this.collapseBtn.on("click", this.collapseClick, this);
9532         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9533         this.header.on("dblclick", this.collapseClick, this);
9534     }
9535     if(this.resizable !== false){
9536         this.el.addClass("x-dlg-resizable");
9537         this.resizer = new Roo.Resizable(el, {
9538             minWidth: this.minWidth || 80,
9539             minHeight:this.minHeight || 80,
9540             handles: this.resizeHandles || "all",
9541             pinned: true
9542         });
9543         this.resizer.on("beforeresize", this.beforeResize, this);
9544         this.resizer.on("resize", this.onResize, this);
9545     }
9546     if(this.draggable !== false){
9547         el.addClass("x-dlg-draggable");
9548         if (!this.proxyDrag) {
9549             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9550         }
9551         else {
9552             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9553         }
9554         dd.setHandleElId(this.header.id);
9555         dd.endDrag = this.endMove.createDelegate(this);
9556         dd.startDrag = this.startMove.createDelegate(this);
9557         dd.onDrag = this.onDrag.createDelegate(this);
9558         dd.scroll = false;
9559         this.dd = dd;
9560     }
9561     if(this.modal){
9562         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9563         this.mask.enableDisplayMode("block");
9564         this.mask.hide();
9565         this.el.addClass("x-dlg-modal");
9566     }
9567     if(this.shadow){
9568         this.shadow = new Roo.Shadow({
9569             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9570             offset : this.shadowOffset
9571         });
9572     }else{
9573         this.shadowOffset = 0;
9574     }
9575     if(Roo.useShims && this.shim !== false){
9576         this.shim = this.el.createShim();
9577         this.shim.hide = this.hideAction;
9578         this.shim.hide();
9579     }else{
9580         this.shim = false;
9581     }
9582     if(this.autoTabs){
9583         this.initTabs();
9584     }
9585     if (this.buttons) { 
9586         var bts= this.buttons;
9587         this.buttons = [];
9588         Roo.each(bts, function(b) {
9589             this.addButton(b);
9590         }, this);
9591     }
9592     
9593     
9594     this.addEvents({
9595         /**
9596          * @event keydown
9597          * Fires when a key is pressed
9598          * @param {Roo.BasicDialog} this
9599          * @param {Roo.EventObject} e
9600          */
9601         "keydown" : true,
9602         /**
9603          * @event move
9604          * Fires when this dialog is moved by the user.
9605          * @param {Roo.BasicDialog} this
9606          * @param {Number} x The new page X
9607          * @param {Number} y The new page Y
9608          */
9609         "move" : true,
9610         /**
9611          * @event resize
9612          * Fires when this dialog is resized by the user.
9613          * @param {Roo.BasicDialog} this
9614          * @param {Number} width The new width
9615          * @param {Number} height The new height
9616          */
9617         "resize" : true,
9618         /**
9619          * @event beforehide
9620          * Fires before this dialog is hidden.
9621          * @param {Roo.BasicDialog} this
9622          */
9623         "beforehide" : true,
9624         /**
9625          * @event hide
9626          * Fires when this dialog is hidden.
9627          * @param {Roo.BasicDialog} this
9628          */
9629         "hide" : true,
9630         /**
9631          * @event beforeshow
9632          * Fires before this dialog is shown.
9633          * @param {Roo.BasicDialog} this
9634          */
9635         "beforeshow" : true,
9636         /**
9637          * @event show
9638          * Fires when this dialog is shown.
9639          * @param {Roo.BasicDialog} this
9640          */
9641         "show" : true
9642     });
9643     el.on("keydown", this.onKeyDown, this);
9644     el.on("mousedown", this.toFront, this);
9645     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9646     this.el.hide();
9647     Roo.DialogManager.register(this);
9648     Roo.BasicDialog.superclass.constructor.call(this);
9649 };
9650
9651 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9652     shadowOffset: Roo.isIE ? 6 : 5,
9653     minHeight: 80,
9654     minWidth: 200,
9655     minButtonWidth: 75,
9656     defaultButton: null,
9657     buttonAlign: "right",
9658     tabTag: 'div',
9659     firstShow: true,
9660
9661     /**
9662      * Sets the dialog title text
9663      * @param {String} text The title text to display
9664      * @return {Roo.BasicDialog} this
9665      */
9666     setTitle : function(text){
9667         this.header.update(text);
9668         return this;
9669     },
9670
9671     // private
9672     closeClick : function(){
9673         this.hide();
9674     },
9675
9676     // private
9677     collapseClick : function(){
9678         this[this.collapsed ? "expand" : "collapse"]();
9679     },
9680
9681     /**
9682      * Collapses the dialog to its minimized state (only the title bar is visible).
9683      * Equivalent to the user clicking the collapse dialog button.
9684      */
9685     collapse : function(){
9686         if(!this.collapsed){
9687             this.collapsed = true;
9688             this.el.addClass("x-dlg-collapsed");
9689             this.restoreHeight = this.el.getHeight();
9690             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9691         }
9692     },
9693
9694     /**
9695      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9696      * clicking the expand dialog button.
9697      */
9698     expand : function(){
9699         if(this.collapsed){
9700             this.collapsed = false;
9701             this.el.removeClass("x-dlg-collapsed");
9702             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9703         }
9704     },
9705
9706     /**
9707      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9708      * @return {Roo.TabPanel} The tabs component
9709      */
9710     initTabs : function(){
9711         var tabs = this.getTabs();
9712         while(tabs.getTab(0)){
9713             tabs.removeTab(0);
9714         }
9715         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9716             var dom = el.dom;
9717             tabs.addTab(Roo.id(dom), dom.title);
9718             dom.title = "";
9719         });
9720         tabs.activate(0);
9721         return tabs;
9722     },
9723
9724     // private
9725     beforeResize : function(){
9726         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9727     },
9728
9729     // private
9730     onResize : function(){
9731         this.refreshSize();
9732         this.syncBodyHeight();
9733         this.adjustAssets();
9734         this.focus();
9735         this.fireEvent("resize", this, this.size.width, this.size.height);
9736     },
9737
9738     // private
9739     onKeyDown : function(e){
9740         if(this.isVisible()){
9741             this.fireEvent("keydown", this, e);
9742         }
9743     },
9744
9745     /**
9746      * Resizes the dialog.
9747      * @param {Number} width
9748      * @param {Number} height
9749      * @return {Roo.BasicDialog} this
9750      */
9751     resizeTo : function(width, height){
9752         this.el.setSize(width, height);
9753         this.size = {width: width, height: height};
9754         this.syncBodyHeight();
9755         if(this.fixedcenter){
9756             this.center();
9757         }
9758         if(this.isVisible()){
9759             this.constrainXY();
9760             this.adjustAssets();
9761         }
9762         this.fireEvent("resize", this, width, height);
9763         return this;
9764     },
9765
9766
9767     /**
9768      * Resizes the dialog to fit the specified content size.
9769      * @param {Number} width
9770      * @param {Number} height
9771      * @return {Roo.BasicDialog} this
9772      */
9773     setContentSize : function(w, h){
9774         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9775         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9776         //if(!this.el.isBorderBox()){
9777             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9778             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9779         //}
9780         if(this.tabs){
9781             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9782             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9783         }
9784         this.resizeTo(w, h);
9785         return this;
9786     },
9787
9788     /**
9789      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9790      * executed in response to a particular key being pressed while the dialog is active.
9791      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9792      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9793      * @param {Function} fn The function to call
9794      * @param {Object} scope (optional) The scope of the function
9795      * @return {Roo.BasicDialog} this
9796      */
9797     addKeyListener : function(key, fn, scope){
9798         var keyCode, shift, ctrl, alt;
9799         if(typeof key == "object" && !(key instanceof Array)){
9800             keyCode = key["key"];
9801             shift = key["shift"];
9802             ctrl = key["ctrl"];
9803             alt = key["alt"];
9804         }else{
9805             keyCode = key;
9806         }
9807         var handler = function(dlg, e){
9808             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9809                 var k = e.getKey();
9810                 if(keyCode instanceof Array){
9811                     for(var i = 0, len = keyCode.length; i < len; i++){
9812                         if(keyCode[i] == k){
9813                           fn.call(scope || window, dlg, k, e);
9814                           return;
9815                         }
9816                     }
9817                 }else{
9818                     if(k == keyCode){
9819                         fn.call(scope || window, dlg, k, e);
9820                     }
9821                 }
9822             }
9823         };
9824         this.on("keydown", handler);
9825         return this;
9826     },
9827
9828     /**
9829      * Returns the TabPanel component (creates it if it doesn't exist).
9830      * Note: If you wish to simply check for the existence of tabs without creating them,
9831      * check for a null 'tabs' property.
9832      * @return {Roo.TabPanel} The tabs component
9833      */
9834     getTabs : function(){
9835         if(!this.tabs){
9836             this.el.addClass("x-dlg-auto-tabs");
9837             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9838             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9839         }
9840         return this.tabs;
9841     },
9842
9843     /**
9844      * Adds a button to the footer section of the dialog.
9845      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9846      * object or a valid Roo.DomHelper element config
9847      * @param {Function} handler The function called when the button is clicked
9848      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9849      * @return {Roo.Button} The new button
9850      */
9851     addButton : function(config, handler, scope){
9852         var dh = Roo.DomHelper;
9853         if(!this.footer){
9854             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9855         }
9856         if(!this.btnContainer){
9857             var tb = this.footer.createChild({
9858
9859                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9860                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9861             }, null, true);
9862             this.btnContainer = tb.firstChild.firstChild.firstChild;
9863         }
9864         var bconfig = {
9865             handler: handler,
9866             scope: scope,
9867             minWidth: this.minButtonWidth,
9868             hideParent:true
9869         };
9870         if(typeof config == "string"){
9871             bconfig.text = config;
9872         }else{
9873             if(config.tag){
9874                 bconfig.dhconfig = config;
9875             }else{
9876                 Roo.apply(bconfig, config);
9877             }
9878         }
9879         var fc = false;
9880         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9881             bconfig.position = Math.max(0, bconfig.position);
9882             fc = this.btnContainer.childNodes[bconfig.position];
9883         }
9884          
9885         var btn = new Roo.Button(
9886             fc ? 
9887                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9888                 : this.btnContainer.appendChild(document.createElement("td")),
9889             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9890             bconfig
9891         );
9892         this.syncBodyHeight();
9893         if(!this.buttons){
9894             /**
9895              * Array of all the buttons that have been added to this dialog via addButton
9896              * @type Array
9897              */
9898             this.buttons = [];
9899         }
9900         this.buttons.push(btn);
9901         return btn;
9902     },
9903
9904     /**
9905      * Sets the default button to be focused when the dialog is displayed.
9906      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9907      * @return {Roo.BasicDialog} this
9908      */
9909     setDefaultButton : function(btn){
9910         this.defaultButton = btn;
9911         return this;
9912     },
9913
9914     // private
9915     getHeaderFooterHeight : function(safe){
9916         var height = 0;
9917         if(this.header){
9918            height += this.header.getHeight();
9919         }
9920         if(this.footer){
9921            var fm = this.footer.getMargins();
9922             height += (this.footer.getHeight()+fm.top+fm.bottom);
9923         }
9924         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9925         height += this.centerBg.getPadding("tb");
9926         return height;
9927     },
9928
9929     // private
9930     syncBodyHeight : function()
9931     {
9932         var bd = this.body, // the text
9933             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9934             bw = this.bwrap;
9935         var height = this.size.height - this.getHeaderFooterHeight(false);
9936         bd.setHeight(height-bd.getMargins("tb"));
9937         var hh = this.header.getHeight();
9938         var h = this.size.height-hh;
9939         cb.setHeight(h);
9940         
9941         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9942         bw.setHeight(h-cb.getPadding("tb"));
9943         
9944         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9945         bd.setWidth(bw.getWidth(true));
9946         if(this.tabs){
9947             this.tabs.syncHeight();
9948             if(Roo.isIE){
9949                 this.tabs.el.repaint();
9950             }
9951         }
9952     },
9953
9954     /**
9955      * Restores the previous state of the dialog if Roo.state is configured.
9956      * @return {Roo.BasicDialog} this
9957      */
9958     restoreState : function(){
9959         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9960         if(box && box.width){
9961             this.xy = [box.x, box.y];
9962             this.resizeTo(box.width, box.height);
9963         }
9964         return this;
9965     },
9966
9967     // private
9968     beforeShow : function(){
9969         this.expand();
9970         if(this.fixedcenter){
9971             this.xy = this.el.getCenterXY(true);
9972         }
9973         if(this.modal){
9974             Roo.get(document.body).addClass("x-body-masked");
9975             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9976             this.mask.show();
9977         }
9978         this.constrainXY();
9979     },
9980
9981     // private
9982     animShow : function(){
9983         var b = Roo.get(this.animateTarget).getBox();
9984         this.proxy.setSize(b.width, b.height);
9985         this.proxy.setLocation(b.x, b.y);
9986         this.proxy.show();
9987         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9988                     true, .35, this.showEl.createDelegate(this));
9989     },
9990
9991     /**
9992      * Shows the dialog.
9993      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9994      * @return {Roo.BasicDialog} this
9995      */
9996     show : function(animateTarget){
9997         if (this.fireEvent("beforeshow", this) === false){
9998             return;
9999         }
10000         if(this.syncHeightBeforeShow){
10001             this.syncBodyHeight();
10002         }else if(this.firstShow){
10003             this.firstShow = false;
10004             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10005         }
10006         this.animateTarget = animateTarget || this.animateTarget;
10007         if(!this.el.isVisible()){
10008             this.beforeShow();
10009             if(this.animateTarget && Roo.get(this.animateTarget)){
10010                 this.animShow();
10011             }else{
10012                 this.showEl();
10013             }
10014         }
10015         return this;
10016     },
10017
10018     // private
10019     showEl : function(){
10020         this.proxy.hide();
10021         this.el.setXY(this.xy);
10022         this.el.show();
10023         this.adjustAssets(true);
10024         this.toFront();
10025         this.focus();
10026         // IE peekaboo bug - fix found by Dave Fenwick
10027         if(Roo.isIE){
10028             this.el.repaint();
10029         }
10030         this.fireEvent("show", this);
10031     },
10032
10033     /**
10034      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10035      * dialog itself will receive focus.
10036      */
10037     focus : function(){
10038         if(this.defaultButton){
10039             this.defaultButton.focus();
10040         }else{
10041             this.focusEl.focus();
10042         }
10043     },
10044
10045     // private
10046     constrainXY : function(){
10047         if(this.constraintoviewport !== false){
10048             if(!this.viewSize){
10049                 if(this.container){
10050                     var s = this.container.getSize();
10051                     this.viewSize = [s.width, s.height];
10052                 }else{
10053                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10054                 }
10055             }
10056             var s = Roo.get(this.container||document).getScroll();
10057
10058             var x = this.xy[0], y = this.xy[1];
10059             var w = this.size.width, h = this.size.height;
10060             var vw = this.viewSize[0], vh = this.viewSize[1];
10061             // only move it if it needs it
10062             var moved = false;
10063             // first validate right/bottom
10064             if(x + w > vw+s.left){
10065                 x = vw - w;
10066                 moved = true;
10067             }
10068             if(y + h > vh+s.top){
10069                 y = vh - h;
10070                 moved = true;
10071             }
10072             // then make sure top/left isn't negative
10073             if(x < s.left){
10074                 x = s.left;
10075                 moved = true;
10076             }
10077             if(y < s.top){
10078                 y = s.top;
10079                 moved = true;
10080             }
10081             if(moved){
10082                 // cache xy
10083                 this.xy = [x, y];
10084                 if(this.isVisible()){
10085                     this.el.setLocation(x, y);
10086                     this.adjustAssets();
10087                 }
10088             }
10089         }
10090     },
10091
10092     // private
10093     onDrag : function(){
10094         if(!this.proxyDrag){
10095             this.xy = this.el.getXY();
10096             this.adjustAssets();
10097         }
10098     },
10099
10100     // private
10101     adjustAssets : function(doShow){
10102         var x = this.xy[0], y = this.xy[1];
10103         var w = this.size.width, h = this.size.height;
10104         if(doShow === true){
10105             if(this.shadow){
10106                 this.shadow.show(this.el);
10107             }
10108             if(this.shim){
10109                 this.shim.show();
10110             }
10111         }
10112         if(this.shadow && this.shadow.isVisible()){
10113             this.shadow.show(this.el);
10114         }
10115         if(this.shim && this.shim.isVisible()){
10116             this.shim.setBounds(x, y, w, h);
10117         }
10118     },
10119
10120     // private
10121     adjustViewport : function(w, h){
10122         if(!w || !h){
10123             w = Roo.lib.Dom.getViewWidth();
10124             h = Roo.lib.Dom.getViewHeight();
10125         }
10126         // cache the size
10127         this.viewSize = [w, h];
10128         if(this.modal && this.mask.isVisible()){
10129             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10130             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10131         }
10132         if(this.isVisible()){
10133             this.constrainXY();
10134         }
10135     },
10136
10137     /**
10138      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10139      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10140      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10141      */
10142     destroy : function(removeEl){
10143         if(this.isVisible()){
10144             this.animateTarget = null;
10145             this.hide();
10146         }
10147         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10148         if(this.tabs){
10149             this.tabs.destroy(removeEl);
10150         }
10151         Roo.destroy(
10152              this.shim,
10153              this.proxy,
10154              this.resizer,
10155              this.close,
10156              this.mask
10157         );
10158         if(this.dd){
10159             this.dd.unreg();
10160         }
10161         if(this.buttons){
10162            for(var i = 0, len = this.buttons.length; i < len; i++){
10163                this.buttons[i].destroy();
10164            }
10165         }
10166         this.el.removeAllListeners();
10167         if(removeEl === true){
10168             this.el.update("");
10169             this.el.remove();
10170         }
10171         Roo.DialogManager.unregister(this);
10172     },
10173
10174     // private
10175     startMove : function(){
10176         if(this.proxyDrag){
10177             this.proxy.show();
10178         }
10179         if(this.constraintoviewport !== false){
10180             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10181         }
10182     },
10183
10184     // private
10185     endMove : function(){
10186         if(!this.proxyDrag){
10187             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10188         }else{
10189             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10190             this.proxy.hide();
10191         }
10192         this.refreshSize();
10193         this.adjustAssets();
10194         this.focus();
10195         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10196     },
10197
10198     /**
10199      * Brings this dialog to the front of any other visible dialogs
10200      * @return {Roo.BasicDialog} this
10201      */
10202     toFront : function(){
10203         Roo.DialogManager.bringToFront(this);
10204         return this;
10205     },
10206
10207     /**
10208      * Sends this dialog to the back (under) of any other visible dialogs
10209      * @return {Roo.BasicDialog} this
10210      */
10211     toBack : function(){
10212         Roo.DialogManager.sendToBack(this);
10213         return this;
10214     },
10215
10216     /**
10217      * Centers this dialog in the viewport
10218      * @return {Roo.BasicDialog} this
10219      */
10220     center : function(){
10221         var xy = this.el.getCenterXY(true);
10222         this.moveTo(xy[0], xy[1]);
10223         return this;
10224     },
10225
10226     /**
10227      * Moves the dialog's top-left corner to the specified point
10228      * @param {Number} x
10229      * @param {Number} y
10230      * @return {Roo.BasicDialog} this
10231      */
10232     moveTo : function(x, y){
10233         this.xy = [x,y];
10234         if(this.isVisible()){
10235             this.el.setXY(this.xy);
10236             this.adjustAssets();
10237         }
10238         return this;
10239     },
10240
10241     /**
10242      * Aligns the dialog to the specified element
10243      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10244      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10245      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10246      * @return {Roo.BasicDialog} this
10247      */
10248     alignTo : function(element, position, offsets){
10249         this.xy = this.el.getAlignToXY(element, position, offsets);
10250         if(this.isVisible()){
10251             this.el.setXY(this.xy);
10252             this.adjustAssets();
10253         }
10254         return this;
10255     },
10256
10257     /**
10258      * Anchors an element to another element and realigns it when the window is resized.
10259      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10260      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10261      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10262      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10263      * is a number, it is used as the buffer delay (defaults to 50ms).
10264      * @return {Roo.BasicDialog} this
10265      */
10266     anchorTo : function(el, alignment, offsets, monitorScroll){
10267         var action = function(){
10268             this.alignTo(el, alignment, offsets);
10269         };
10270         Roo.EventManager.onWindowResize(action, this);
10271         var tm = typeof monitorScroll;
10272         if(tm != 'undefined'){
10273             Roo.EventManager.on(window, 'scroll', action, this,
10274                 {buffer: tm == 'number' ? monitorScroll : 50});
10275         }
10276         action.call(this);
10277         return this;
10278     },
10279
10280     /**
10281      * Returns true if the dialog is visible
10282      * @return {Boolean}
10283      */
10284     isVisible : function(){
10285         return this.el.isVisible();
10286     },
10287
10288     // private
10289     animHide : function(callback){
10290         var b = Roo.get(this.animateTarget).getBox();
10291         this.proxy.show();
10292         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10293         this.el.hide();
10294         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10295                     this.hideEl.createDelegate(this, [callback]));
10296     },
10297
10298     /**
10299      * Hides the dialog.
10300      * @param {Function} callback (optional) Function to call when the dialog is hidden
10301      * @return {Roo.BasicDialog} this
10302      */
10303     hide : function(callback){
10304         if (this.fireEvent("beforehide", this) === false){
10305             return;
10306         }
10307         if(this.shadow){
10308             this.shadow.hide();
10309         }
10310         if(this.shim) {
10311           this.shim.hide();
10312         }
10313         // sometimes animateTarget seems to get set.. causing problems...
10314         // this just double checks..
10315         if(this.animateTarget && Roo.get(this.animateTarget)) {
10316            this.animHide(callback);
10317         }else{
10318             this.el.hide();
10319             this.hideEl(callback);
10320         }
10321         return this;
10322     },
10323
10324     // private
10325     hideEl : function(callback){
10326         this.proxy.hide();
10327         if(this.modal){
10328             this.mask.hide();
10329             Roo.get(document.body).removeClass("x-body-masked");
10330         }
10331         this.fireEvent("hide", this);
10332         if(typeof callback == "function"){
10333             callback();
10334         }
10335     },
10336
10337     // private
10338     hideAction : function(){
10339         this.setLeft("-10000px");
10340         this.setTop("-10000px");
10341         this.setStyle("visibility", "hidden");
10342     },
10343
10344     // private
10345     refreshSize : function(){
10346         this.size = this.el.getSize();
10347         this.xy = this.el.getXY();
10348         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10349     },
10350
10351     // private
10352     // z-index is managed by the DialogManager and may be overwritten at any time
10353     setZIndex : function(index){
10354         if(this.modal){
10355             this.mask.setStyle("z-index", index);
10356         }
10357         if(this.shim){
10358             this.shim.setStyle("z-index", ++index);
10359         }
10360         if(this.shadow){
10361             this.shadow.setZIndex(++index);
10362         }
10363         this.el.setStyle("z-index", ++index);
10364         if(this.proxy){
10365             this.proxy.setStyle("z-index", ++index);
10366         }
10367         if(this.resizer){
10368             this.resizer.proxy.setStyle("z-index", ++index);
10369         }
10370
10371         this.lastZIndex = index;
10372     },
10373
10374     /**
10375      * Returns the element for this dialog
10376      * @return {Roo.Element} The underlying dialog Element
10377      */
10378     getEl : function(){
10379         return this.el;
10380     }
10381 });
10382
10383 /**
10384  * @class Roo.DialogManager
10385  * Provides global access to BasicDialogs that have been created and
10386  * support for z-indexing (layering) multiple open dialogs.
10387  */
10388 Roo.DialogManager = function(){
10389     var list = {};
10390     var accessList = [];
10391     var front = null;
10392
10393     // private
10394     var sortDialogs = function(d1, d2){
10395         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10396     };
10397
10398     // private
10399     var orderDialogs = function(){
10400         accessList.sort(sortDialogs);
10401         var seed = Roo.DialogManager.zseed;
10402         for(var i = 0, len = accessList.length; i < len; i++){
10403             var dlg = accessList[i];
10404             if(dlg){
10405                 dlg.setZIndex(seed + (i*10));
10406             }
10407         }
10408     };
10409
10410     return {
10411         /**
10412          * The starting z-index for BasicDialogs (defaults to 9000)
10413          * @type Number The z-index value
10414          */
10415         zseed : 9000,
10416
10417         // private
10418         register : function(dlg){
10419             list[dlg.id] = dlg;
10420             accessList.push(dlg);
10421         },
10422
10423         // private
10424         unregister : function(dlg){
10425             delete list[dlg.id];
10426             var i=0;
10427             var len=0;
10428             if(!accessList.indexOf){
10429                 for(  i = 0, len = accessList.length; i < len; i++){
10430                     if(accessList[i] == dlg){
10431                         accessList.splice(i, 1);
10432                         return;
10433                     }
10434                 }
10435             }else{
10436                  i = accessList.indexOf(dlg);
10437                 if(i != -1){
10438                     accessList.splice(i, 1);
10439                 }
10440             }
10441         },
10442
10443         /**
10444          * Gets a registered dialog by id
10445          * @param {String/Object} id The id of the dialog or a dialog
10446          * @return {Roo.BasicDialog} this
10447          */
10448         get : function(id){
10449             return typeof id == "object" ? id : list[id];
10450         },
10451
10452         /**
10453          * Brings the specified dialog to the front
10454          * @param {String/Object} dlg The id of the dialog or a dialog
10455          * @return {Roo.BasicDialog} this
10456          */
10457         bringToFront : function(dlg){
10458             dlg = this.get(dlg);
10459             if(dlg != front){
10460                 front = dlg;
10461                 dlg._lastAccess = new Date().getTime();
10462                 orderDialogs();
10463             }
10464             return dlg;
10465         },
10466
10467         /**
10468          * Sends the specified dialog to the back
10469          * @param {String/Object} dlg The id of the dialog or a dialog
10470          * @return {Roo.BasicDialog} this
10471          */
10472         sendToBack : function(dlg){
10473             dlg = this.get(dlg);
10474             dlg._lastAccess = -(new Date().getTime());
10475             orderDialogs();
10476             return dlg;
10477         },
10478
10479         /**
10480          * Hides all dialogs
10481          */
10482         hideAll : function(){
10483             for(var id in list){
10484                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10485                     list[id].hide();
10486                 }
10487             }
10488         }
10489     };
10490 }();
10491
10492 /**
10493  * @class Roo.LayoutDialog
10494  * @extends Roo.BasicDialog
10495  * Dialog which provides adjustments for working with a layout in a Dialog.
10496  * Add your necessary layout config options to the dialog's config.<br>
10497  * Example usage (including a nested layout):
10498  * <pre><code>
10499 if(!dialog){
10500     dialog = new Roo.LayoutDialog("download-dlg", {
10501         modal: true,
10502         width:600,
10503         height:450,
10504         shadow:true,
10505         minWidth:500,
10506         minHeight:350,
10507         autoTabs:true,
10508         proxyDrag:true,
10509         // layout config merges with the dialog config
10510         center:{
10511             tabPosition: "top",
10512             alwaysShowTabs: true
10513         }
10514     });
10515     dialog.addKeyListener(27, dialog.hide, dialog);
10516     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10517     dialog.addButton("Build It!", this.getDownload, this);
10518
10519     // we can even add nested layouts
10520     var innerLayout = new Roo.BorderLayout("dl-inner", {
10521         east: {
10522             initialSize: 200,
10523             autoScroll:true,
10524             split:true
10525         },
10526         center: {
10527             autoScroll:true
10528         }
10529     });
10530     innerLayout.beginUpdate();
10531     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10532     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10533     innerLayout.endUpdate(true);
10534
10535     var layout = dialog.getLayout();
10536     layout.beginUpdate();
10537     layout.add("center", new Roo.ContentPanel("standard-panel",
10538                         {title: "Download the Source", fitToFrame:true}));
10539     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10540                {title: "Build your own roo.js"}));
10541     layout.getRegion("center").showPanel(sp);
10542     layout.endUpdate();
10543 }
10544 </code></pre>
10545     * @constructor
10546     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10547     * @param {Object} config configuration options
10548   */
10549 Roo.LayoutDialog = function(el, cfg){
10550     
10551     var config=  cfg;
10552     if (typeof(cfg) == 'undefined') {
10553         config = Roo.apply({}, el);
10554         // not sure why we use documentElement here.. - it should always be body.
10555         // IE7 borks horribly if we use documentElement.
10556         // webkit also does not like documentElement - it creates a body element...
10557         el = Roo.get( document.body || document.documentElement ).createChild();
10558         //config.autoCreate = true;
10559     }
10560     
10561     
10562     config.autoTabs = false;
10563     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10564     this.body.setStyle({overflow:"hidden", position:"relative"});
10565     this.layout = new Roo.BorderLayout(this.body.dom, config);
10566     this.layout.monitorWindowResize = false;
10567     this.el.addClass("x-dlg-auto-layout");
10568     // fix case when center region overwrites center function
10569     this.center = Roo.BasicDialog.prototype.center;
10570     this.on("show", this.layout.layout, this.layout, true);
10571     if (config.items) {
10572         var xitems = config.items;
10573         delete config.items;
10574         Roo.each(xitems, this.addxtype, this);
10575     }
10576     
10577     
10578 };
10579 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10580     /**
10581      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10582      * @deprecated
10583      */
10584     endUpdate : function(){
10585         this.layout.endUpdate();
10586     },
10587
10588     /**
10589      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10590      *  @deprecated
10591      */
10592     beginUpdate : function(){
10593         this.layout.beginUpdate();
10594     },
10595
10596     /**
10597      * Get the BorderLayout for this dialog
10598      * @return {Roo.BorderLayout}
10599      */
10600     getLayout : function(){
10601         return this.layout;
10602     },
10603
10604     showEl : function(){
10605         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10606         if(Roo.isIE7){
10607             this.layout.layout();
10608         }
10609     },
10610
10611     // private
10612     // Use the syncHeightBeforeShow config option to control this automatically
10613     syncBodyHeight : function(){
10614         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10615         if(this.layout){this.layout.layout();}
10616     },
10617     
10618       /**
10619      * Add an xtype element (actually adds to the layout.)
10620      * @return {Object} xdata xtype object data.
10621      */
10622     
10623     addxtype : function(c) {
10624         return this.layout.addxtype(c);
10625     }
10626 });/*
10627  * Based on:
10628  * Ext JS Library 1.1.1
10629  * Copyright(c) 2006-2007, Ext JS, LLC.
10630  *
10631  * Originally Released Under LGPL - original licence link has changed is not relivant.
10632  *
10633  * Fork - LGPL
10634  * <script type="text/javascript">
10635  */
10636  
10637 /**
10638  * @class Roo.MessageBox
10639  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10640  * Example usage:
10641  *<pre><code>
10642 // Basic alert:
10643 Roo.Msg.alert('Status', 'Changes saved successfully.');
10644
10645 // Prompt for user data:
10646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10647     if (btn == 'ok'){
10648         // process text value...
10649     }
10650 });
10651
10652 // Show a dialog using config options:
10653 Roo.Msg.show({
10654    title:'Save Changes?',
10655    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10656    buttons: Roo.Msg.YESNOCANCEL,
10657    fn: processResult,
10658    animEl: 'elId'
10659 });
10660 </code></pre>
10661  * @singleton
10662  */
10663 Roo.MessageBox = function(){
10664     var dlg, opt, mask, waitTimer;
10665     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10666     var buttons, activeTextEl, bwidth;
10667
10668     // private
10669     var handleButton = function(button){
10670         dlg.hide();
10671         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10672     };
10673
10674     // private
10675     var handleHide = function(){
10676         if(opt && opt.cls){
10677             dlg.el.removeClass(opt.cls);
10678         }
10679         if(waitTimer){
10680             Roo.TaskMgr.stop(waitTimer);
10681             waitTimer = null;
10682         }
10683     };
10684
10685     // private
10686     var updateButtons = function(b){
10687         var width = 0;
10688         if(!b){
10689             buttons["ok"].hide();
10690             buttons["cancel"].hide();
10691             buttons["yes"].hide();
10692             buttons["no"].hide();
10693             dlg.footer.dom.style.display = 'none';
10694             return width;
10695         }
10696         dlg.footer.dom.style.display = '';
10697         for(var k in buttons){
10698             if(typeof buttons[k] != "function"){
10699                 if(b[k]){
10700                     buttons[k].show();
10701                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10702                     width += buttons[k].el.getWidth()+15;
10703                 }else{
10704                     buttons[k].hide();
10705                 }
10706             }
10707         }
10708         return width;
10709     };
10710
10711     // private
10712     var handleEsc = function(d, k, e){
10713         if(opt && opt.closable !== false){
10714             dlg.hide();
10715         }
10716         if(e){
10717             e.stopEvent();
10718         }
10719     };
10720
10721     return {
10722         /**
10723          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10724          * @return {Roo.BasicDialog} The BasicDialog element
10725          */
10726         getDialog : function(){
10727            if(!dlg){
10728                 dlg = new Roo.BasicDialog("x-msg-box", {
10729                     autoCreate : true,
10730                     shadow: true,
10731                     draggable: true,
10732                     resizable:false,
10733                     constraintoviewport:false,
10734                     fixedcenter:true,
10735                     collapsible : false,
10736                     shim:true,
10737                     modal: true,
10738                     width:400, height:100,
10739                     buttonAlign:"center",
10740                     closeClick : function(){
10741                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10742                             handleButton("no");
10743                         }else{
10744                             handleButton("cancel");
10745                         }
10746                     }
10747                 });
10748                 dlg.on("hide", handleHide);
10749                 mask = dlg.mask;
10750                 dlg.addKeyListener(27, handleEsc);
10751                 buttons = {};
10752                 var bt = this.buttonText;
10753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10757                 bodyEl = dlg.body.createChild({
10758
10759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
10760                 });
10761                 msgEl = bodyEl.dom.firstChild;
10762                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10763                 textboxEl.enableDisplayMode();
10764                 textboxEl.addKeyListener([10,13], function(){
10765                     if(dlg.isVisible() && opt && opt.buttons){
10766                         if(opt.buttons.ok){
10767                             handleButton("ok");
10768                         }else if(opt.buttons.yes){
10769                             handleButton("yes");
10770                         }
10771                     }
10772                 });
10773                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10774                 textareaEl.enableDisplayMode();
10775                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10776                 progressEl.enableDisplayMode();
10777                 var pf = progressEl.dom.firstChild;
10778                 if (pf) {
10779                     pp = Roo.get(pf.firstChild);
10780                     pp.setHeight(pf.offsetHeight);
10781                 }
10782                 
10783             }
10784             return dlg;
10785         },
10786
10787         /**
10788          * Updates the message box body text
10789          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10790          * the XHTML-compliant non-breaking space character '&amp;#160;')
10791          * @return {Roo.MessageBox} This message box
10792          */
10793         updateText : function(text){
10794             if(!dlg.isVisible() && !opt.width){
10795                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10796             }
10797             msgEl.innerHTML = text || '&#160;';
10798       
10799             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10800             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10801             var w = Math.max(
10802                     Math.min(opt.width || cw , this.maxWidth), 
10803                     Math.max(opt.minWidth || this.minWidth, bwidth)
10804             );
10805             if(opt.prompt){
10806                 activeTextEl.setWidth(w);
10807             }
10808             if(dlg.isVisible()){
10809                 dlg.fixedcenter = false;
10810             }
10811             // to big, make it scroll. = But as usual stupid IE does not support
10812             // !important..
10813             
10814             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10815                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10816                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10817             } else {
10818                 bodyEl.dom.style.height = '';
10819                 bodyEl.dom.style.overflowY = '';
10820             }
10821             if (cw > w) {
10822                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10823             } else {
10824                 bodyEl.dom.style.overflowX = '';
10825             }
10826             
10827             dlg.setContentSize(w, bodyEl.getHeight());
10828             if(dlg.isVisible()){
10829                 dlg.fixedcenter = true;
10830             }
10831             return this;
10832         },
10833
10834         /**
10835          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10836          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10837          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10838          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10839          * @return {Roo.MessageBox} This message box
10840          */
10841         updateProgress : function(value, text){
10842             if(text){
10843                 this.updateText(text);
10844             }
10845             if (pp) { // weird bug on my firefox - for some reason this is not defined
10846                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10847             }
10848             return this;
10849         },        
10850
10851         /**
10852          * Returns true if the message box is currently displayed
10853          * @return {Boolean} True if the message box is visible, else false
10854          */
10855         isVisible : function(){
10856             return dlg && dlg.isVisible();  
10857         },
10858
10859         /**
10860          * Hides the message box if it is displayed
10861          */
10862         hide : function(){
10863             if(this.isVisible()){
10864                 dlg.hide();
10865             }  
10866         },
10867
10868         /**
10869          * Displays a new message box, or reinitializes an existing message box, based on the config options
10870          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10871          * The following config object properties are supported:
10872          * <pre>
10873 Property    Type             Description
10874 ----------  ---------------  ------------------------------------------------------------------------------------
10875 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10876                                    closes (defaults to undefined)
10877 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10878                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10879 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10880                                    progress and wait dialogs will ignore this property and always hide the
10881                                    close button as they can only be closed programmatically.
10882 cls               String           A custom CSS class to apply to the message box element
10883 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10884                                    displayed (defaults to 75)
10885 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10886                                    function will be btn (the name of the button that was clicked, if applicable,
10887                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10888                                    Progress and wait dialogs will ignore this option since they do not respond to
10889                                    user actions and can only be closed programmatically, so any required function
10890                                    should be called by the same code after it closes the dialog.
10891 icon              String           A CSS class that provides a background image to be used as an icon for
10892                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10893 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10894 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10895 modal             Boolean          False to allow user interaction with the page while the message box is
10896                                    displayed (defaults to true)
10897 msg               String           A string that will replace the existing message box body text (defaults
10898                                    to the XHTML-compliant non-breaking space character '&#160;')
10899 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10900 progress          Boolean          True to display a progress bar (defaults to false)
10901 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10902 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10903 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10904 title             String           The title text
10905 value             String           The string value to set into the active textbox element if displayed
10906 wait              Boolean          True to display a progress bar (defaults to false)
10907 width             Number           The width of the dialog in pixels
10908 </pre>
10909          *
10910          * Example usage:
10911          * <pre><code>
10912 Roo.Msg.show({
10913    title: 'Address',
10914    msg: 'Please enter your address:',
10915    width: 300,
10916    buttons: Roo.MessageBox.OKCANCEL,
10917    multiline: true,
10918    fn: saveAddress,
10919    animEl: 'addAddressBtn'
10920 });
10921 </code></pre>
10922          * @param {Object} config Configuration options
10923          * @return {Roo.MessageBox} This message box
10924          */
10925         show : function(options)
10926         {
10927             
10928             // this causes nightmares if you show one dialog after another
10929             // especially on callbacks..
10930              
10931             if(this.isVisible()){
10932                 
10933                 this.hide();
10934                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10935                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10936                 Roo.log("New Dialog Message:" +  options.msg )
10937                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10938                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10939                 
10940             }
10941             var d = this.getDialog();
10942             opt = options;
10943             d.setTitle(opt.title || "&#160;");
10944             d.close.setDisplayed(opt.closable !== false);
10945             activeTextEl = textboxEl;
10946             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10947             if(opt.prompt){
10948                 if(opt.multiline){
10949                     textboxEl.hide();
10950                     textareaEl.show();
10951                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10952                         opt.multiline : this.defaultTextHeight);
10953                     activeTextEl = textareaEl;
10954                 }else{
10955                     textboxEl.show();
10956                     textareaEl.hide();
10957                 }
10958             }else{
10959                 textboxEl.hide();
10960                 textareaEl.hide();
10961             }
10962             progressEl.setDisplayed(opt.progress === true);
10963             this.updateProgress(0);
10964             activeTextEl.dom.value = opt.value || "";
10965             if(opt.prompt){
10966                 dlg.setDefaultButton(activeTextEl);
10967             }else{
10968                 var bs = opt.buttons;
10969                 var db = null;
10970                 if(bs && bs.ok){
10971                     db = buttons["ok"];
10972                 }else if(bs && bs.yes){
10973                     db = buttons["yes"];
10974                 }
10975                 dlg.setDefaultButton(db);
10976             }
10977             bwidth = updateButtons(opt.buttons);
10978             this.updateText(opt.msg);
10979             if(opt.cls){
10980                 d.el.addClass(opt.cls);
10981             }
10982             d.proxyDrag = opt.proxyDrag === true;
10983             d.modal = opt.modal !== false;
10984             d.mask = opt.modal !== false ? mask : false;
10985             if(!d.isVisible()){
10986                 // force it to the end of the z-index stack so it gets a cursor in FF
10987                 document.body.appendChild(dlg.el.dom);
10988                 d.animateTarget = null;
10989                 d.show(options.animEl);
10990             }
10991             return this;
10992         },
10993
10994         /**
10995          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10996          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10997          * and closing the message box when the process is complete.
10998          * @param {String} title The title bar text
10999          * @param {String} msg The message box body text
11000          * @return {Roo.MessageBox} This message box
11001          */
11002         progress : function(title, msg){
11003             this.show({
11004                 title : title,
11005                 msg : msg,
11006                 buttons: false,
11007                 progress:true,
11008                 closable:false,
11009                 minWidth: this.minProgressWidth,
11010                 modal : true
11011             });
11012             return this;
11013         },
11014
11015         /**
11016          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11017          * If a callback function is passed it will be called after the user clicks the button, and the
11018          * id of the button that was clicked will be passed as the only parameter to the callback
11019          * (could also be the top-right close button).
11020          * @param {String} title The title bar text
11021          * @param {String} msg The message box body text
11022          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11023          * @param {Object} scope (optional) The scope of the callback function
11024          * @return {Roo.MessageBox} This message box
11025          */
11026         alert : function(title, msg, fn, scope){
11027             this.show({
11028                 title : title,
11029                 msg : msg,
11030                 buttons: this.OK,
11031                 fn: fn,
11032                 scope : scope,
11033                 modal : true
11034             });
11035             return this;
11036         },
11037
11038         /**
11039          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11040          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11041          * You are responsible for closing the message box when the process is complete.
11042          * @param {String} msg The message box body text
11043          * @param {String} title (optional) The title bar text
11044          * @return {Roo.MessageBox} This message box
11045          */
11046         wait : function(msg, title){
11047             this.show({
11048                 title : title,
11049                 msg : msg,
11050                 buttons: false,
11051                 closable:false,
11052                 progress:true,
11053                 modal:true,
11054                 width:300,
11055                 wait:true
11056             });
11057             waitTimer = Roo.TaskMgr.start({
11058                 run: function(i){
11059                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11060                 },
11061                 interval: 1000
11062             });
11063             return this;
11064         },
11065
11066         /**
11067          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11068          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11069          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11070          * @param {String} title The title bar text
11071          * @param {String} msg The message box body text
11072          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11073          * @param {Object} scope (optional) The scope of the callback function
11074          * @return {Roo.MessageBox} This message box
11075          */
11076         confirm : function(title, msg, fn, scope){
11077             this.show({
11078                 title : title,
11079                 msg : msg,
11080                 buttons: this.YESNO,
11081                 fn: fn,
11082                 scope : scope,
11083                 modal : true
11084             });
11085             return this;
11086         },
11087
11088         /**
11089          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11090          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11091          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11092          * (could also be the top-right close button) and the text that was entered will be passed as the two
11093          * parameters to the callback.
11094          * @param {String} title The title bar text
11095          * @param {String} msg The message box body text
11096          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11097          * @param {Object} scope (optional) The scope of the callback function
11098          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11099          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11100          * @return {Roo.MessageBox} This message box
11101          */
11102         prompt : function(title, msg, fn, scope, multiline){
11103             this.show({
11104                 title : title,
11105                 msg : msg,
11106                 buttons: this.OKCANCEL,
11107                 fn: fn,
11108                 minWidth:250,
11109                 scope : scope,
11110                 prompt:true,
11111                 multiline: multiline,
11112                 modal : true
11113             });
11114             return this;
11115         },
11116
11117         /**
11118          * Button config that displays a single OK button
11119          * @type Object
11120          */
11121         OK : {ok:true},
11122         /**
11123          * Button config that displays Yes and No buttons
11124          * @type Object
11125          */
11126         YESNO : {yes:true, no:true},
11127         /**
11128          * Button config that displays OK and Cancel buttons
11129          * @type Object
11130          */
11131         OKCANCEL : {ok:true, cancel:true},
11132         /**
11133          * Button config that displays Yes, No and Cancel buttons
11134          * @type Object
11135          */
11136         YESNOCANCEL : {yes:true, no:true, cancel:true},
11137
11138         /**
11139          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11140          * @type Number
11141          */
11142         defaultTextHeight : 75,
11143         /**
11144          * The maximum width in pixels of the message box (defaults to 600)
11145          * @type Number
11146          */
11147         maxWidth : 600,
11148         /**
11149          * The minimum width in pixels of the message box (defaults to 100)
11150          * @type Number
11151          */
11152         minWidth : 100,
11153         /**
11154          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11155          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11156          * @type Number
11157          */
11158         minProgressWidth : 250,
11159         /**
11160          * An object containing the default button text strings that can be overriden for localized language support.
11161          * Supported properties are: ok, cancel, yes and no.
11162          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11163          * @type Object
11164          */
11165         buttonText : {
11166             ok : "OK",
11167             cancel : "Cancel",
11168             yes : "Yes",
11169             no : "No"
11170         }
11171     };
11172 }();
11173
11174 /**
11175  * Shorthand for {@link Roo.MessageBox}
11176  */
11177 Roo.Msg = Roo.MessageBox;/*
11178  * Based on:
11179  * Ext JS Library 1.1.1
11180  * Copyright(c) 2006-2007, Ext JS, LLC.
11181  *
11182  * Originally Released Under LGPL - original licence link has changed is not relivant.
11183  *
11184  * Fork - LGPL
11185  * <script type="text/javascript">
11186  */
11187 /**
11188  * @class Roo.QuickTips
11189  * Provides attractive and customizable tooltips for any element.
11190  * @singleton
11191  */
11192 Roo.QuickTips = function(){
11193     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11194     var ce, bd, xy, dd;
11195     var visible = false, disabled = true, inited = false;
11196     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11197     
11198     var onOver = function(e){
11199         if(disabled){
11200             return;
11201         }
11202         var t = e.getTarget();
11203         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11204             return;
11205         }
11206         if(ce && t == ce.el){
11207             clearTimeout(hideProc);
11208             return;
11209         }
11210         if(t && tagEls[t.id]){
11211             tagEls[t.id].el = t;
11212             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11213             return;
11214         }
11215         var ttp, et = Roo.fly(t);
11216         var ns = cfg.namespace;
11217         if(tm.interceptTitles && t.title){
11218             ttp = t.title;
11219             t.qtip = ttp;
11220             t.removeAttribute("title");
11221             e.preventDefault();
11222         }else{
11223             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11224         }
11225         if(ttp){
11226             showProc = show.defer(tm.showDelay, tm, [{
11227                 el: t, 
11228                 text: ttp, 
11229                 width: et.getAttributeNS(ns, cfg.width),
11230                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11231                 title: et.getAttributeNS(ns, cfg.title),
11232                     cls: et.getAttributeNS(ns, cfg.cls)
11233             }]);
11234         }
11235     };
11236     
11237     var onOut = function(e){
11238         clearTimeout(showProc);
11239         var t = e.getTarget();
11240         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11241             hideProc = setTimeout(hide, tm.hideDelay);
11242         }
11243     };
11244     
11245     var onMove = function(e){
11246         if(disabled){
11247             return;
11248         }
11249         xy = e.getXY();
11250         xy[1] += 18;
11251         if(tm.trackMouse && ce){
11252             el.setXY(xy);
11253         }
11254     };
11255     
11256     var onDown = function(e){
11257         clearTimeout(showProc);
11258         clearTimeout(hideProc);
11259         if(!e.within(el)){
11260             if(tm.hideOnClick){
11261                 hide();
11262                 tm.disable();
11263                 tm.enable.defer(100, tm);
11264             }
11265         }
11266     };
11267     
11268     var getPad = function(){
11269         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11270     };
11271
11272     var show = function(o){
11273         if(disabled){
11274             return;
11275         }
11276         clearTimeout(dismissProc);
11277         ce = o;
11278         if(removeCls){ // in case manually hidden
11279             el.removeClass(removeCls);
11280             removeCls = null;
11281         }
11282         if(ce.cls){
11283             el.addClass(ce.cls);
11284             removeCls = ce.cls;
11285         }
11286         if(ce.title){
11287             tipTitle.update(ce.title);
11288             tipTitle.show();
11289         }else{
11290             tipTitle.update('');
11291             tipTitle.hide();
11292         }
11293         el.dom.style.width  = tm.maxWidth+'px';
11294         //tipBody.dom.style.width = '';
11295         tipBodyText.update(o.text);
11296         var p = getPad(), w = ce.width;
11297         if(!w){
11298             var td = tipBodyText.dom;
11299             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11300             if(aw > tm.maxWidth){
11301                 w = tm.maxWidth;
11302             }else if(aw < tm.minWidth){
11303                 w = tm.minWidth;
11304             }else{
11305                 w = aw;
11306             }
11307         }
11308         //tipBody.setWidth(w);
11309         el.setWidth(parseInt(w, 10) + p);
11310         if(ce.autoHide === false){
11311             close.setDisplayed(true);
11312             if(dd){
11313                 dd.unlock();
11314             }
11315         }else{
11316             close.setDisplayed(false);
11317             if(dd){
11318                 dd.lock();
11319             }
11320         }
11321         if(xy){
11322             el.avoidY = xy[1]-18;
11323             el.setXY(xy);
11324         }
11325         if(tm.animate){
11326             el.setOpacity(.1);
11327             el.setStyle("visibility", "visible");
11328             el.fadeIn({callback: afterShow});
11329         }else{
11330             afterShow();
11331         }
11332     };
11333     
11334     var afterShow = function(){
11335         if(ce){
11336             el.show();
11337             esc.enable();
11338             if(tm.autoDismiss && ce.autoHide !== false){
11339                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11340             }
11341         }
11342     };
11343     
11344     var hide = function(noanim){
11345         clearTimeout(dismissProc);
11346         clearTimeout(hideProc);
11347         ce = null;
11348         if(el.isVisible()){
11349             esc.disable();
11350             if(noanim !== true && tm.animate){
11351                 el.fadeOut({callback: afterHide});
11352             }else{
11353                 afterHide();
11354             } 
11355         }
11356     };
11357     
11358     var afterHide = function(){
11359         el.hide();
11360         if(removeCls){
11361             el.removeClass(removeCls);
11362             removeCls = null;
11363         }
11364     };
11365     
11366     return {
11367         /**
11368         * @cfg {Number} minWidth
11369         * The minimum width of the quick tip (defaults to 40)
11370         */
11371        minWidth : 40,
11372         /**
11373         * @cfg {Number} maxWidth
11374         * The maximum width of the quick tip (defaults to 300)
11375         */
11376        maxWidth : 300,
11377         /**
11378         * @cfg {Boolean} interceptTitles
11379         * True to automatically use the element's DOM title value if available (defaults to false)
11380         */
11381        interceptTitles : false,
11382         /**
11383         * @cfg {Boolean} trackMouse
11384         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11385         */
11386        trackMouse : false,
11387         /**
11388         * @cfg {Boolean} hideOnClick
11389         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11390         */
11391        hideOnClick : true,
11392         /**
11393         * @cfg {Number} showDelay
11394         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11395         */
11396        showDelay : 500,
11397         /**
11398         * @cfg {Number} hideDelay
11399         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11400         */
11401        hideDelay : 200,
11402         /**
11403         * @cfg {Boolean} autoHide
11404         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11405         * Used in conjunction with hideDelay.
11406         */
11407        autoHide : true,
11408         /**
11409         * @cfg {Boolean}
11410         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11411         * (defaults to true).  Used in conjunction with autoDismissDelay.
11412         */
11413        autoDismiss : true,
11414         /**
11415         * @cfg {Number}
11416         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11417         */
11418        autoDismissDelay : 5000,
11419        /**
11420         * @cfg {Boolean} animate
11421         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11422         */
11423        animate : false,
11424
11425        /**
11426         * @cfg {String} title
11427         * Title text to display (defaults to '').  This can be any valid HTML markup.
11428         */
11429         title: '',
11430        /**
11431         * @cfg {String} text
11432         * Body text to display (defaults to '').  This can be any valid HTML markup.
11433         */
11434         text : '',
11435        /**
11436         * @cfg {String} cls
11437         * A CSS class to apply to the base quick tip element (defaults to '').
11438         */
11439         cls : '',
11440        /**
11441         * @cfg {Number} width
11442         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11443         * minWidth or maxWidth.
11444         */
11445         width : null,
11446
11447     /**
11448      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11449      * or display QuickTips in a page.
11450      */
11451        init : function(){
11452           tm = Roo.QuickTips;
11453           cfg = tm.tagConfig;
11454           if(!inited){
11455               if(!Roo.isReady){ // allow calling of init() before onReady
11456                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11457                   return;
11458               }
11459               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11460               el.fxDefaults = {stopFx: true};
11461               // maximum custom styling
11462               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11463               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
11464               tipTitle = el.child('h3');
11465               tipTitle.enableDisplayMode("block");
11466               tipBody = el.child('div.x-tip-bd');
11467               tipBodyText = el.child('div.x-tip-bd-inner');
11468               //bdLeft = el.child('div.x-tip-bd-left');
11469               //bdRight = el.child('div.x-tip-bd-right');
11470               close = el.child('div.x-tip-close');
11471               close.enableDisplayMode("block");
11472               close.on("click", hide);
11473               var d = Roo.get(document);
11474               d.on("mousedown", onDown);
11475               d.on("mouseover", onOver);
11476               d.on("mouseout", onOut);
11477               d.on("mousemove", onMove);
11478               esc = d.addKeyListener(27, hide);
11479               esc.disable();
11480               if(Roo.dd.DD){
11481                   dd = el.initDD("default", null, {
11482                       onDrag : function(){
11483                           el.sync();  
11484                       }
11485                   });
11486                   dd.setHandleElId(tipTitle.id);
11487                   dd.lock();
11488               }
11489               inited = true;
11490           }
11491           this.enable(); 
11492        },
11493
11494     /**
11495      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11496      * are supported:
11497      * <pre>
11498 Property    Type                   Description
11499 ----------  ---------------------  ------------------------------------------------------------------------
11500 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11501      * </ul>
11502      * @param {Object} config The config object
11503      */
11504        register : function(config){
11505            var cs = config instanceof Array ? config : arguments;
11506            for(var i = 0, len = cs.length; i < len; i++) {
11507                var c = cs[i];
11508                var target = c.target;
11509                if(target){
11510                    if(target instanceof Array){
11511                        for(var j = 0, jlen = target.length; j < jlen; j++){
11512                            tagEls[target[j]] = c;
11513                        }
11514                    }else{
11515                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11516                    }
11517                }
11518            }
11519        },
11520
11521     /**
11522      * Removes this quick tip from its element and destroys it.
11523      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11524      */
11525        unregister : function(el){
11526            delete tagEls[Roo.id(el)];
11527        },
11528
11529     /**
11530      * Enable this quick tip.
11531      */
11532        enable : function(){
11533            if(inited && disabled){
11534                locks.pop();
11535                if(locks.length < 1){
11536                    disabled = false;
11537                }
11538            }
11539        },
11540
11541     /**
11542      * Disable this quick tip.
11543      */
11544        disable : function(){
11545           disabled = true;
11546           clearTimeout(showProc);
11547           clearTimeout(hideProc);
11548           clearTimeout(dismissProc);
11549           if(ce){
11550               hide(true);
11551           }
11552           locks.push(1);
11553        },
11554
11555     /**
11556      * Returns true if the quick tip is enabled, else false.
11557      */
11558        isEnabled : function(){
11559             return !disabled;
11560        },
11561
11562         // private
11563        tagConfig : {
11564            namespace : "roo", // was ext?? this may break..
11565            alt_namespace : "ext",
11566            attribute : "qtip",
11567            width : "width",
11568            target : "target",
11569            title : "qtitle",
11570            hide : "hide",
11571            cls : "qclass"
11572        }
11573    };
11574 }();
11575
11576 // backwards compat
11577 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11578  * Based on:
11579  * Ext JS Library 1.1.1
11580  * Copyright(c) 2006-2007, Ext JS, LLC.
11581  *
11582  * Originally Released Under LGPL - original licence link has changed is not relivant.
11583  *
11584  * Fork - LGPL
11585  * <script type="text/javascript">
11586  */
11587  
11588
11589 /**
11590  * @class Roo.tree.TreePanel
11591  * @extends Roo.data.Tree
11592
11593  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11594  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11595  * @cfg {Boolean} enableDD true to enable drag and drop
11596  * @cfg {Boolean} enableDrag true to enable just drag
11597  * @cfg {Boolean} enableDrop true to enable just drop
11598  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11599  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11600  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11601  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11602  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11603  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11604  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11605  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11606  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11607  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11608  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11609  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11610  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11611  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11612  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11613  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11614  * 
11615  * @constructor
11616  * @param {String/HTMLElement/Element} el The container element
11617  * @param {Object} config
11618  */
11619 Roo.tree.TreePanel = function(el, config){
11620     var root = false;
11621     var loader = false;
11622     if (config.root) {
11623         root = config.root;
11624         delete config.root;
11625     }
11626     if (config.loader) {
11627         loader = config.loader;
11628         delete config.loader;
11629     }
11630     
11631     Roo.apply(this, config);
11632     Roo.tree.TreePanel.superclass.constructor.call(this);
11633     this.el = Roo.get(el);
11634     this.el.addClass('x-tree');
11635     //console.log(root);
11636     if (root) {
11637         this.setRootNode( Roo.factory(root, Roo.tree));
11638     }
11639     if (loader) {
11640         this.loader = Roo.factory(loader, Roo.tree);
11641     }
11642    /**
11643     * Read-only. The id of the container element becomes this TreePanel's id.
11644     */
11645     this.id = this.el.id;
11646     this.addEvents({
11647         /**
11648         * @event beforeload
11649         * Fires before a node is loaded, return false to cancel
11650         * @param {Node} node The node being loaded
11651         */
11652         "beforeload" : true,
11653         /**
11654         * @event load
11655         * Fires when a node is loaded
11656         * @param {Node} node The node that was loaded
11657         */
11658         "load" : true,
11659         /**
11660         * @event textchange
11661         * Fires when the text for a node is changed
11662         * @param {Node} node The node
11663         * @param {String} text The new text
11664         * @param {String} oldText The old text
11665         */
11666         "textchange" : true,
11667         /**
11668         * @event beforeexpand
11669         * Fires before a node is expanded, return false to cancel.
11670         * @param {Node} node The node
11671         * @param {Boolean} deep
11672         * @param {Boolean} anim
11673         */
11674         "beforeexpand" : true,
11675         /**
11676         * @event beforecollapse
11677         * Fires before a node is collapsed, return false to cancel.
11678         * @param {Node} node The node
11679         * @param {Boolean} deep
11680         * @param {Boolean} anim
11681         */
11682         "beforecollapse" : true,
11683         /**
11684         * @event expand
11685         * Fires when a node is expanded
11686         * @param {Node} node The node
11687         */
11688         "expand" : true,
11689         /**
11690         * @event disabledchange
11691         * Fires when the disabled status of a node changes
11692         * @param {Node} node The node
11693         * @param {Boolean} disabled
11694         */
11695         "disabledchange" : true,
11696         /**
11697         * @event collapse
11698         * Fires when a node is collapsed
11699         * @param {Node} node The node
11700         */
11701         "collapse" : true,
11702         /**
11703         * @event beforeclick
11704         * Fires before click processing on a node. Return false to cancel the default action.
11705         * @param {Node} node The node
11706         * @param {Roo.EventObject} e The event object
11707         */
11708         "beforeclick":true,
11709         /**
11710         * @event checkchange
11711         * Fires when a node with a checkbox's checked property changes
11712         * @param {Node} this This node
11713         * @param {Boolean} checked
11714         */
11715         "checkchange":true,
11716         /**
11717         * @event click
11718         * Fires when a node is clicked
11719         * @param {Node} node The node
11720         * @param {Roo.EventObject} e The event object
11721         */
11722         "click":true,
11723         /**
11724         * @event dblclick
11725         * Fires when a node is double clicked
11726         * @param {Node} node The node
11727         * @param {Roo.EventObject} e The event object
11728         */
11729         "dblclick":true,
11730         /**
11731         * @event contextmenu
11732         * Fires when a node is right clicked
11733         * @param {Node} node The node
11734         * @param {Roo.EventObject} e The event object
11735         */
11736         "contextmenu":true,
11737         /**
11738         * @event beforechildrenrendered
11739         * Fires right before the child nodes for a node are rendered
11740         * @param {Node} node The node
11741         */
11742         "beforechildrenrendered":true,
11743         /**
11744         * @event startdrag
11745         * Fires when a node starts being dragged
11746         * @param {Roo.tree.TreePanel} this
11747         * @param {Roo.tree.TreeNode} node
11748         * @param {event} e The raw browser event
11749         */ 
11750        "startdrag" : true,
11751        /**
11752         * @event enddrag
11753         * Fires when a drag operation is complete
11754         * @param {Roo.tree.TreePanel} this
11755         * @param {Roo.tree.TreeNode} node
11756         * @param {event} e The raw browser event
11757         */
11758        "enddrag" : true,
11759        /**
11760         * @event dragdrop
11761         * Fires when a dragged node is dropped on a valid DD target
11762         * @param {Roo.tree.TreePanel} this
11763         * @param {Roo.tree.TreeNode} node
11764         * @param {DD} dd The dd it was dropped on
11765         * @param {event} e The raw browser event
11766         */
11767        "dragdrop" : true,
11768        /**
11769         * @event beforenodedrop
11770         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11771         * passed to handlers has the following properties:<br />
11772         * <ul style="padding:5px;padding-left:16px;">
11773         * <li>tree - The TreePanel</li>
11774         * <li>target - The node being targeted for the drop</li>
11775         * <li>data - The drag data from the drag source</li>
11776         * <li>point - The point of the drop - append, above or below</li>
11777         * <li>source - The drag source</li>
11778         * <li>rawEvent - Raw mouse event</li>
11779         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11780         * to be inserted by setting them on this object.</li>
11781         * <li>cancel - Set this to true to cancel the drop.</li>
11782         * </ul>
11783         * @param {Object} dropEvent
11784         */
11785        "beforenodedrop" : true,
11786        /**
11787         * @event nodedrop
11788         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11789         * passed to handlers has the following properties:<br />
11790         * <ul style="padding:5px;padding-left:16px;">
11791         * <li>tree - The TreePanel</li>
11792         * <li>target - The node being targeted for the drop</li>
11793         * <li>data - The drag data from the drag source</li>
11794         * <li>point - The point of the drop - append, above or below</li>
11795         * <li>source - The drag source</li>
11796         * <li>rawEvent - Raw mouse event</li>
11797         * <li>dropNode - Dropped node(s).</li>
11798         * </ul>
11799         * @param {Object} dropEvent
11800         */
11801        "nodedrop" : true,
11802         /**
11803         * @event nodedragover
11804         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11805         * passed to handlers has the following properties:<br />
11806         * <ul style="padding:5px;padding-left:16px;">
11807         * <li>tree - The TreePanel</li>
11808         * <li>target - The node being targeted for the drop</li>
11809         * <li>data - The drag data from the drag source</li>
11810         * <li>point - The point of the drop - append, above or below</li>
11811         * <li>source - The drag source</li>
11812         * <li>rawEvent - Raw mouse event</li>
11813         * <li>dropNode - Drop node(s) provided by the source.</li>
11814         * <li>cancel - Set this to true to signal drop not allowed.</li>
11815         * </ul>
11816         * @param {Object} dragOverEvent
11817         */
11818        "nodedragover" : true
11819         
11820     });
11821     if(this.singleExpand){
11822        this.on("beforeexpand", this.restrictExpand, this);
11823     }
11824     if (this.editor) {
11825         this.editor.tree = this;
11826         this.editor = Roo.factory(this.editor, Roo.tree);
11827     }
11828     
11829     if (this.selModel) {
11830         this.selModel = Roo.factory(this.selModel, Roo.tree);
11831     }
11832    
11833 };
11834 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11835     rootVisible : true,
11836     animate: Roo.enableFx,
11837     lines : true,
11838     enableDD : false,
11839     hlDrop : Roo.enableFx,
11840   
11841     renderer: false,
11842     
11843     rendererTip: false,
11844     // private
11845     restrictExpand : function(node){
11846         var p = node.parentNode;
11847         if(p){
11848             if(p.expandedChild && p.expandedChild.parentNode == p){
11849                 p.expandedChild.collapse();
11850             }
11851             p.expandedChild = node;
11852         }
11853     },
11854
11855     // private override
11856     setRootNode : function(node){
11857         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11858         if(!this.rootVisible){
11859             node.ui = new Roo.tree.RootTreeNodeUI(node);
11860         }
11861         return node;
11862     },
11863
11864     /**
11865      * Returns the container element for this TreePanel
11866      */
11867     getEl : function(){
11868         return this.el;
11869     },
11870
11871     /**
11872      * Returns the default TreeLoader for this TreePanel
11873      */
11874     getLoader : function(){
11875         return this.loader;
11876     },
11877
11878     /**
11879      * Expand all nodes
11880      */
11881     expandAll : function(){
11882         this.root.expand(true);
11883     },
11884
11885     /**
11886      * Collapse all nodes
11887      */
11888     collapseAll : function(){
11889         this.root.collapse(true);
11890     },
11891
11892     /**
11893      * Returns the selection model used by this TreePanel
11894      */
11895     getSelectionModel : function(){
11896         if(!this.selModel){
11897             this.selModel = new Roo.tree.DefaultSelectionModel();
11898         }
11899         return this.selModel;
11900     },
11901
11902     /**
11903      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11904      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11905      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11906      * @return {Array}
11907      */
11908     getChecked : function(a, startNode){
11909         startNode = startNode || this.root;
11910         var r = [];
11911         var f = function(){
11912             if(this.attributes.checked){
11913                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11914             }
11915         }
11916         startNode.cascade(f);
11917         return r;
11918     },
11919
11920     /**
11921      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11922      * @param {String} path
11923      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11924      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11925      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11926      */
11927     expandPath : function(path, attr, callback){
11928         attr = attr || "id";
11929         var keys = path.split(this.pathSeparator);
11930         var curNode = this.root;
11931         if(curNode.attributes[attr] != keys[1]){ // invalid root
11932             if(callback){
11933                 callback(false, null);
11934             }
11935             return;
11936         }
11937         var index = 1;
11938         var f = function(){
11939             if(++index == keys.length){
11940                 if(callback){
11941                     callback(true, curNode);
11942                 }
11943                 return;
11944             }
11945             var c = curNode.findChild(attr, keys[index]);
11946             if(!c){
11947                 if(callback){
11948                     callback(false, curNode);
11949                 }
11950                 return;
11951             }
11952             curNode = c;
11953             c.expand(false, false, f);
11954         };
11955         curNode.expand(false, false, f);
11956     },
11957
11958     /**
11959      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11960      * @param {String} path
11961      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11962      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11963      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11964      */
11965     selectPath : function(path, attr, callback){
11966         attr = attr || "id";
11967         var keys = path.split(this.pathSeparator);
11968         var v = keys.pop();
11969         if(keys.length > 0){
11970             var f = function(success, node){
11971                 if(success && node){
11972                     var n = node.findChild(attr, v);
11973                     if(n){
11974                         n.select();
11975                         if(callback){
11976                             callback(true, n);
11977                         }
11978                     }else if(callback){
11979                         callback(false, n);
11980                     }
11981                 }else{
11982                     if(callback){
11983                         callback(false, n);
11984                     }
11985                 }
11986             };
11987             this.expandPath(keys.join(this.pathSeparator), attr, f);
11988         }else{
11989             this.root.select();
11990             if(callback){
11991                 callback(true, this.root);
11992             }
11993         }
11994     },
11995
11996     getTreeEl : function(){
11997         return this.el;
11998     },
11999
12000     /**
12001      * Trigger rendering of this TreePanel
12002      */
12003     render : function(){
12004         if (this.innerCt) {
12005             return this; // stop it rendering more than once!!
12006         }
12007         
12008         this.innerCt = this.el.createChild({tag:"ul",
12009                cls:"x-tree-root-ct " +
12010                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12011
12012         if(this.containerScroll){
12013             Roo.dd.ScrollManager.register(this.el);
12014         }
12015         if((this.enableDD || this.enableDrop) && !this.dropZone){
12016            /**
12017             * The dropZone used by this tree if drop is enabled
12018             * @type Roo.tree.TreeDropZone
12019             */
12020              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12021                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12022            });
12023         }
12024         if((this.enableDD || this.enableDrag) && !this.dragZone){
12025            /**
12026             * The dragZone used by this tree if drag is enabled
12027             * @type Roo.tree.TreeDragZone
12028             */
12029             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12030                ddGroup: this.ddGroup || "TreeDD",
12031                scroll: this.ddScroll
12032            });
12033         }
12034         this.getSelectionModel().init(this);
12035         if (!this.root) {
12036             Roo.log("ROOT not set in tree");
12037             return this;
12038         }
12039         this.root.render();
12040         if(!this.rootVisible){
12041             this.root.renderChildren();
12042         }
12043         return this;
12044     }
12045 });/*
12046  * Based on:
12047  * Ext JS Library 1.1.1
12048  * Copyright(c) 2006-2007, Ext JS, LLC.
12049  *
12050  * Originally Released Under LGPL - original licence link has changed is not relivant.
12051  *
12052  * Fork - LGPL
12053  * <script type="text/javascript">
12054  */
12055  
12056
12057 /**
12058  * @class Roo.tree.DefaultSelectionModel
12059  * @extends Roo.util.Observable
12060  * The default single selection for a TreePanel.
12061  * @param {Object} cfg Configuration
12062  */
12063 Roo.tree.DefaultSelectionModel = function(cfg){
12064    this.selNode = null;
12065    
12066    
12067    
12068    this.addEvents({
12069        /**
12070         * @event selectionchange
12071         * Fires when the selected node changes
12072         * @param {DefaultSelectionModel} this
12073         * @param {TreeNode} node the new selection
12074         */
12075        "selectionchange" : true,
12076
12077        /**
12078         * @event beforeselect
12079         * Fires before the selected node changes, return false to cancel the change
12080         * @param {DefaultSelectionModel} this
12081         * @param {TreeNode} node the new selection
12082         * @param {TreeNode} node the old selection
12083         */
12084        "beforeselect" : true
12085    });
12086    
12087     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12088 };
12089
12090 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12091     init : function(tree){
12092         this.tree = tree;
12093         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12094         tree.on("click", this.onNodeClick, this);
12095     },
12096     
12097     onNodeClick : function(node, e){
12098         if (e.ctrlKey && this.selNode == node)  {
12099             this.unselect(node);
12100             return;
12101         }
12102         this.select(node);
12103     },
12104     
12105     /**
12106      * Select a node.
12107      * @param {TreeNode} node The node to select
12108      * @return {TreeNode} The selected node
12109      */
12110     select : function(node){
12111         var last = this.selNode;
12112         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12113             if(last){
12114                 last.ui.onSelectedChange(false);
12115             }
12116             this.selNode = node;
12117             node.ui.onSelectedChange(true);
12118             this.fireEvent("selectionchange", this, node, last);
12119         }
12120         return node;
12121     },
12122     
12123     /**
12124      * Deselect a node.
12125      * @param {TreeNode} node The node to unselect
12126      */
12127     unselect : function(node){
12128         if(this.selNode == node){
12129             this.clearSelections();
12130         }    
12131     },
12132     
12133     /**
12134      * Clear all selections
12135      */
12136     clearSelections : function(){
12137         var n = this.selNode;
12138         if(n){
12139             n.ui.onSelectedChange(false);
12140             this.selNode = null;
12141             this.fireEvent("selectionchange", this, null);
12142         }
12143         return n;
12144     },
12145     
12146     /**
12147      * Get the selected node
12148      * @return {TreeNode} The selected node
12149      */
12150     getSelectedNode : function(){
12151         return this.selNode;    
12152     },
12153     
12154     /**
12155      * Returns true if the node is selected
12156      * @param {TreeNode} node The node to check
12157      * @return {Boolean}
12158      */
12159     isSelected : function(node){
12160         return this.selNode == node;  
12161     },
12162
12163     /**
12164      * Selects the node above the selected node in the tree, intelligently walking the nodes
12165      * @return TreeNode The new selection
12166      */
12167     selectPrevious : function(){
12168         var s = this.selNode || this.lastSelNode;
12169         if(!s){
12170             return null;
12171         }
12172         var ps = s.previousSibling;
12173         if(ps){
12174             if(!ps.isExpanded() || ps.childNodes.length < 1){
12175                 return this.select(ps);
12176             } else{
12177                 var lc = ps.lastChild;
12178                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12179                     lc = lc.lastChild;
12180                 }
12181                 return this.select(lc);
12182             }
12183         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12184             return this.select(s.parentNode);
12185         }
12186         return null;
12187     },
12188
12189     /**
12190      * Selects the node above the selected node in the tree, intelligently walking the nodes
12191      * @return TreeNode The new selection
12192      */
12193     selectNext : function(){
12194         var s = this.selNode || this.lastSelNode;
12195         if(!s){
12196             return null;
12197         }
12198         if(s.firstChild && s.isExpanded()){
12199              return this.select(s.firstChild);
12200          }else if(s.nextSibling){
12201              return this.select(s.nextSibling);
12202          }else if(s.parentNode){
12203             var newS = null;
12204             s.parentNode.bubble(function(){
12205                 if(this.nextSibling){
12206                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12207                     return false;
12208                 }
12209             });
12210             return newS;
12211          }
12212         return null;
12213     },
12214
12215     onKeyDown : function(e){
12216         var s = this.selNode || this.lastSelNode;
12217         // undesirable, but required
12218         var sm = this;
12219         if(!s){
12220             return;
12221         }
12222         var k = e.getKey();
12223         switch(k){
12224              case e.DOWN:
12225                  e.stopEvent();
12226                  this.selectNext();
12227              break;
12228              case e.UP:
12229                  e.stopEvent();
12230                  this.selectPrevious();
12231              break;
12232              case e.RIGHT:
12233                  e.preventDefault();
12234                  if(s.hasChildNodes()){
12235                      if(!s.isExpanded()){
12236                          s.expand();
12237                      }else if(s.firstChild){
12238                          this.select(s.firstChild, e);
12239                      }
12240                  }
12241              break;
12242              case e.LEFT:
12243                  e.preventDefault();
12244                  if(s.hasChildNodes() && s.isExpanded()){
12245                      s.collapse();
12246                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12247                      this.select(s.parentNode, e);
12248                  }
12249              break;
12250         };
12251     }
12252 });
12253
12254 /**
12255  * @class Roo.tree.MultiSelectionModel
12256  * @extends Roo.util.Observable
12257  * Multi selection for a TreePanel.
12258  * @param {Object} cfg Configuration
12259  */
12260 Roo.tree.MultiSelectionModel = function(){
12261    this.selNodes = [];
12262    this.selMap = {};
12263    this.addEvents({
12264        /**
12265         * @event selectionchange
12266         * Fires when the selected nodes change
12267         * @param {MultiSelectionModel} this
12268         * @param {Array} nodes Array of the selected nodes
12269         */
12270        "selectionchange" : true
12271    });
12272    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12273    
12274 };
12275
12276 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12277     init : function(tree){
12278         this.tree = tree;
12279         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12280         tree.on("click", this.onNodeClick, this);
12281     },
12282     
12283     onNodeClick : function(node, e){
12284         this.select(node, e, e.ctrlKey);
12285     },
12286     
12287     /**
12288      * Select a node.
12289      * @param {TreeNode} node The node to select
12290      * @param {EventObject} e (optional) An event associated with the selection
12291      * @param {Boolean} keepExisting True to retain existing selections
12292      * @return {TreeNode} The selected node
12293      */
12294     select : function(node, e, keepExisting){
12295         if(keepExisting !== true){
12296             this.clearSelections(true);
12297         }
12298         if(this.isSelected(node)){
12299             this.lastSelNode = node;
12300             return node;
12301         }
12302         this.selNodes.push(node);
12303         this.selMap[node.id] = node;
12304         this.lastSelNode = node;
12305         node.ui.onSelectedChange(true);
12306         this.fireEvent("selectionchange", this, this.selNodes);
12307         return node;
12308     },
12309     
12310     /**
12311      * Deselect a node.
12312      * @param {TreeNode} node The node to unselect
12313      */
12314     unselect : function(node){
12315         if(this.selMap[node.id]){
12316             node.ui.onSelectedChange(false);
12317             var sn = this.selNodes;
12318             var index = -1;
12319             if(sn.indexOf){
12320                 index = sn.indexOf(node);
12321             }else{
12322                 for(var i = 0, len = sn.length; i < len; i++){
12323                     if(sn[i] == node){
12324                         index = i;
12325                         break;
12326                     }
12327                 }
12328             }
12329             if(index != -1){
12330                 this.selNodes.splice(index, 1);
12331             }
12332             delete this.selMap[node.id];
12333             this.fireEvent("selectionchange", this, this.selNodes);
12334         }
12335     },
12336     
12337     /**
12338      * Clear all selections
12339      */
12340     clearSelections : function(suppressEvent){
12341         var sn = this.selNodes;
12342         if(sn.length > 0){
12343             for(var i = 0, len = sn.length; i < len; i++){
12344                 sn[i].ui.onSelectedChange(false);
12345             }
12346             this.selNodes = [];
12347             this.selMap = {};
12348             if(suppressEvent !== true){
12349                 this.fireEvent("selectionchange", this, this.selNodes);
12350             }
12351         }
12352     },
12353     
12354     /**
12355      * Returns true if the node is selected
12356      * @param {TreeNode} node The node to check
12357      * @return {Boolean}
12358      */
12359     isSelected : function(node){
12360         return this.selMap[node.id] ? true : false;  
12361     },
12362     
12363     /**
12364      * Returns an array of the selected nodes
12365      * @return {Array}
12366      */
12367     getSelectedNodes : function(){
12368         return this.selNodes;    
12369     },
12370
12371     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12372
12373     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12374
12375     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12376 });/*
12377  * Based on:
12378  * Ext JS Library 1.1.1
12379  * Copyright(c) 2006-2007, Ext JS, LLC.
12380  *
12381  * Originally Released Under LGPL - original licence link has changed is not relivant.
12382  *
12383  * Fork - LGPL
12384  * <script type="text/javascript">
12385  */
12386  
12387 /**
12388  * @class Roo.tree.TreeNode
12389  * @extends Roo.data.Node
12390  * @cfg {String} text The text for this node
12391  * @cfg {Boolean} expanded true to start the node expanded
12392  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12393  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12394  * @cfg {Boolean} disabled true to start the node disabled
12395  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12396  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12397  * @cfg {String} cls A css class to be added to the node
12398  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12399  * @cfg {String} href URL of the link used for the node (defaults to #)
12400  * @cfg {String} hrefTarget target frame for the link
12401  * @cfg {String} qtip An Ext QuickTip for the node
12402  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12403  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12404  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12405  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12406  * (defaults to undefined with no checkbox rendered)
12407  * @constructor
12408  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12409  */
12410 Roo.tree.TreeNode = function(attributes){
12411     attributes = attributes || {};
12412     if(typeof attributes == "string"){
12413         attributes = {text: attributes};
12414     }
12415     this.childrenRendered = false;
12416     this.rendered = false;
12417     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12418     this.expanded = attributes.expanded === true;
12419     this.isTarget = attributes.isTarget !== false;
12420     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12421     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12422
12423     /**
12424      * Read-only. The text for this node. To change it use setText().
12425      * @type String
12426      */
12427     this.text = attributes.text;
12428     /**
12429      * True if this node is disabled.
12430      * @type Boolean
12431      */
12432     this.disabled = attributes.disabled === true;
12433
12434     this.addEvents({
12435         /**
12436         * @event textchange
12437         * Fires when the text for this node is changed
12438         * @param {Node} this This node
12439         * @param {String} text The new text
12440         * @param {String} oldText The old text
12441         */
12442         "textchange" : true,
12443         /**
12444         * @event beforeexpand
12445         * Fires before this node is expanded, return false to cancel.
12446         * @param {Node} this This node
12447         * @param {Boolean} deep
12448         * @param {Boolean} anim
12449         */
12450         "beforeexpand" : true,
12451         /**
12452         * @event beforecollapse
12453         * Fires before this node is collapsed, return false to cancel.
12454         * @param {Node} this This node
12455         * @param {Boolean} deep
12456         * @param {Boolean} anim
12457         */
12458         "beforecollapse" : true,
12459         /**
12460         * @event expand
12461         * Fires when this node is expanded
12462         * @param {Node} this This node
12463         */
12464         "expand" : true,
12465         /**
12466         * @event disabledchange
12467         * Fires when the disabled status of this node changes
12468         * @param {Node} this This node
12469         * @param {Boolean} disabled
12470         */
12471         "disabledchange" : true,
12472         /**
12473         * @event collapse
12474         * Fires when this node is collapsed
12475         * @param {Node} this This node
12476         */
12477         "collapse" : true,
12478         /**
12479         * @event beforeclick
12480         * Fires before click processing. Return false to cancel the default action.
12481         * @param {Node} this This node
12482         * @param {Roo.EventObject} e The event object
12483         */
12484         "beforeclick":true,
12485         /**
12486         * @event checkchange
12487         * Fires when a node with a checkbox's checked property changes
12488         * @param {Node} this This node
12489         * @param {Boolean} checked
12490         */
12491         "checkchange":true,
12492         /**
12493         * @event click
12494         * Fires when this node is clicked
12495         * @param {Node} this This node
12496         * @param {Roo.EventObject} e The event object
12497         */
12498         "click":true,
12499         /**
12500         * @event dblclick
12501         * Fires when this node is double clicked
12502         * @param {Node} this This node
12503         * @param {Roo.EventObject} e The event object
12504         */
12505         "dblclick":true,
12506         /**
12507         * @event contextmenu
12508         * Fires when this node is right clicked
12509         * @param {Node} this This node
12510         * @param {Roo.EventObject} e The event object
12511         */
12512         "contextmenu":true,
12513         /**
12514         * @event beforechildrenrendered
12515         * Fires right before the child nodes for this node are rendered
12516         * @param {Node} this This node
12517         */
12518         "beforechildrenrendered":true
12519     });
12520
12521     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12522
12523     /**
12524      * Read-only. The UI for this node
12525      * @type TreeNodeUI
12526      */
12527     this.ui = new uiClass(this);
12528     
12529     // finally support items[]
12530     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12531         return;
12532     }
12533     
12534     
12535     Roo.each(this.attributes.items, function(c) {
12536         this.appendChild(Roo.factory(c,Roo.Tree));
12537     }, this);
12538     delete this.attributes.items;
12539     
12540     
12541     
12542 };
12543 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12544     preventHScroll: true,
12545     /**
12546      * Returns true if this node is expanded
12547      * @return {Boolean}
12548      */
12549     isExpanded : function(){
12550         return this.expanded;
12551     },
12552
12553     /**
12554      * Returns the UI object for this node
12555      * @return {TreeNodeUI}
12556      */
12557     getUI : function(){
12558         return this.ui;
12559     },
12560
12561     // private override
12562     setFirstChild : function(node){
12563         var of = this.firstChild;
12564         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12565         if(this.childrenRendered && of && node != of){
12566             of.renderIndent(true, true);
12567         }
12568         if(this.rendered){
12569             this.renderIndent(true, true);
12570         }
12571     },
12572
12573     // private override
12574     setLastChild : function(node){
12575         var ol = this.lastChild;
12576         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12577         if(this.childrenRendered && ol && node != ol){
12578             ol.renderIndent(true, true);
12579         }
12580         if(this.rendered){
12581             this.renderIndent(true, true);
12582         }
12583     },
12584
12585     // these methods are overridden to provide lazy rendering support
12586     // private override
12587     appendChild : function()
12588     {
12589         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12590         if(node && this.childrenRendered){
12591             node.render();
12592         }
12593         this.ui.updateExpandIcon();
12594         return node;
12595     },
12596
12597     // private override
12598     removeChild : function(node){
12599         this.ownerTree.getSelectionModel().unselect(node);
12600         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12601         // if it's been rendered remove dom node
12602         if(this.childrenRendered){
12603             node.ui.remove();
12604         }
12605         if(this.childNodes.length < 1){
12606             this.collapse(false, false);
12607         }else{
12608             this.ui.updateExpandIcon();
12609         }
12610         if(!this.firstChild) {
12611             this.childrenRendered = false;
12612         }
12613         return node;
12614     },
12615
12616     // private override
12617     insertBefore : function(node, refNode){
12618         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12619         if(newNode && refNode && this.childrenRendered){
12620             node.render();
12621         }
12622         this.ui.updateExpandIcon();
12623         return newNode;
12624     },
12625
12626     /**
12627      * Sets the text for this node
12628      * @param {String} text
12629      */
12630     setText : function(text){
12631         var oldText = this.text;
12632         this.text = text;
12633         this.attributes.text = text;
12634         if(this.rendered){ // event without subscribing
12635             this.ui.onTextChange(this, text, oldText);
12636         }
12637         this.fireEvent("textchange", this, text, oldText);
12638     },
12639
12640     /**
12641      * Triggers selection of this node
12642      */
12643     select : function(){
12644         this.getOwnerTree().getSelectionModel().select(this);
12645     },
12646
12647     /**
12648      * Triggers deselection of this node
12649      */
12650     unselect : function(){
12651         this.getOwnerTree().getSelectionModel().unselect(this);
12652     },
12653
12654     /**
12655      * Returns true if this node is selected
12656      * @return {Boolean}
12657      */
12658     isSelected : function(){
12659         return this.getOwnerTree().getSelectionModel().isSelected(this);
12660     },
12661
12662     /**
12663      * Expand this node.
12664      * @param {Boolean} deep (optional) True to expand all children as well
12665      * @param {Boolean} anim (optional) false to cancel the default animation
12666      * @param {Function} callback (optional) A callback to be called when
12667      * expanding this node completes (does not wait for deep expand to complete).
12668      * Called with 1 parameter, this node.
12669      */
12670     expand : function(deep, anim, callback){
12671         if(!this.expanded){
12672             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12673                 return;
12674             }
12675             if(!this.childrenRendered){
12676                 this.renderChildren();
12677             }
12678             this.expanded = true;
12679             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12680                 this.ui.animExpand(function(){
12681                     this.fireEvent("expand", this);
12682                     if(typeof callback == "function"){
12683                         callback(this);
12684                     }
12685                     if(deep === true){
12686                         this.expandChildNodes(true);
12687                     }
12688                 }.createDelegate(this));
12689                 return;
12690             }else{
12691                 this.ui.expand();
12692                 this.fireEvent("expand", this);
12693                 if(typeof callback == "function"){
12694                     callback(this);
12695                 }
12696             }
12697         }else{
12698            if(typeof callback == "function"){
12699                callback(this);
12700            }
12701         }
12702         if(deep === true){
12703             this.expandChildNodes(true);
12704         }
12705     },
12706
12707     isHiddenRoot : function(){
12708         return this.isRoot && !this.getOwnerTree().rootVisible;
12709     },
12710
12711     /**
12712      * Collapse this node.
12713      * @param {Boolean} deep (optional) True to collapse all children as well
12714      * @param {Boolean} anim (optional) false to cancel the default animation
12715      */
12716     collapse : function(deep, anim){
12717         if(this.expanded && !this.isHiddenRoot()){
12718             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12719                 return;
12720             }
12721             this.expanded = false;
12722             if((this.getOwnerTree().animate && anim !== false) || anim){
12723                 this.ui.animCollapse(function(){
12724                     this.fireEvent("collapse", this);
12725                     if(deep === true){
12726                         this.collapseChildNodes(true);
12727                     }
12728                 }.createDelegate(this));
12729                 return;
12730             }else{
12731                 this.ui.collapse();
12732                 this.fireEvent("collapse", this);
12733             }
12734         }
12735         if(deep === true){
12736             var cs = this.childNodes;
12737             for(var i = 0, len = cs.length; i < len; i++) {
12738                 cs[i].collapse(true, false);
12739             }
12740         }
12741     },
12742
12743     // private
12744     delayedExpand : function(delay){
12745         if(!this.expandProcId){
12746             this.expandProcId = this.expand.defer(delay, this);
12747         }
12748     },
12749
12750     // private
12751     cancelExpand : function(){
12752         if(this.expandProcId){
12753             clearTimeout(this.expandProcId);
12754         }
12755         this.expandProcId = false;
12756     },
12757
12758     /**
12759      * Toggles expanded/collapsed state of the node
12760      */
12761     toggle : function(){
12762         if(this.expanded){
12763             this.collapse();
12764         }else{
12765             this.expand();
12766         }
12767     },
12768
12769     /**
12770      * Ensures all parent nodes are expanded
12771      */
12772     ensureVisible : function(callback){
12773         var tree = this.getOwnerTree();
12774         tree.expandPath(this.parentNode.getPath(), false, function(){
12775             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12776             Roo.callback(callback);
12777         }.createDelegate(this));
12778     },
12779
12780     /**
12781      * Expand all child nodes
12782      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12783      */
12784     expandChildNodes : function(deep){
12785         var cs = this.childNodes;
12786         for(var i = 0, len = cs.length; i < len; i++) {
12787                 cs[i].expand(deep);
12788         }
12789     },
12790
12791     /**
12792      * Collapse all child nodes
12793      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12794      */
12795     collapseChildNodes : function(deep){
12796         var cs = this.childNodes;
12797         for(var i = 0, len = cs.length; i < len; i++) {
12798                 cs[i].collapse(deep);
12799         }
12800     },
12801
12802     /**
12803      * Disables this node
12804      */
12805     disable : function(){
12806         this.disabled = true;
12807         this.unselect();
12808         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12809             this.ui.onDisableChange(this, true);
12810         }
12811         this.fireEvent("disabledchange", this, true);
12812     },
12813
12814     /**
12815      * Enables this node
12816      */
12817     enable : function(){
12818         this.disabled = false;
12819         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12820             this.ui.onDisableChange(this, false);
12821         }
12822         this.fireEvent("disabledchange", this, false);
12823     },
12824
12825     // private
12826     renderChildren : function(suppressEvent){
12827         if(suppressEvent !== false){
12828             this.fireEvent("beforechildrenrendered", this);
12829         }
12830         var cs = this.childNodes;
12831         for(var i = 0, len = cs.length; i < len; i++){
12832             cs[i].render(true);
12833         }
12834         this.childrenRendered = true;
12835     },
12836
12837     // private
12838     sort : function(fn, scope){
12839         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12840         if(this.childrenRendered){
12841             var cs = this.childNodes;
12842             for(var i = 0, len = cs.length; i < len; i++){
12843                 cs[i].render(true);
12844             }
12845         }
12846     },
12847
12848     // private
12849     render : function(bulkRender){
12850         this.ui.render(bulkRender);
12851         if(!this.rendered){
12852             this.rendered = true;
12853             if(this.expanded){
12854                 this.expanded = false;
12855                 this.expand(false, false);
12856             }
12857         }
12858     },
12859
12860     // private
12861     renderIndent : function(deep, refresh){
12862         if(refresh){
12863             this.ui.childIndent = null;
12864         }
12865         this.ui.renderIndent();
12866         if(deep === true && this.childrenRendered){
12867             var cs = this.childNodes;
12868             for(var i = 0, len = cs.length; i < len; i++){
12869                 cs[i].renderIndent(true, refresh);
12870             }
12871         }
12872     }
12873 });/*
12874  * Based on:
12875  * Ext JS Library 1.1.1
12876  * Copyright(c) 2006-2007, Ext JS, LLC.
12877  *
12878  * Originally Released Under LGPL - original licence link has changed is not relivant.
12879  *
12880  * Fork - LGPL
12881  * <script type="text/javascript">
12882  */
12883  
12884 /**
12885  * @class Roo.tree.AsyncTreeNode
12886  * @extends Roo.tree.TreeNode
12887  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12888  * @constructor
12889  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12890  */
12891  Roo.tree.AsyncTreeNode = function(config){
12892     this.loaded = false;
12893     this.loading = false;
12894     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12895     /**
12896     * @event beforeload
12897     * Fires before this node is loaded, return false to cancel
12898     * @param {Node} this This node
12899     */
12900     this.addEvents({'beforeload':true, 'load': true});
12901     /**
12902     * @event load
12903     * Fires when this node is loaded
12904     * @param {Node} this This node
12905     */
12906     /**
12907      * The loader used by this node (defaults to using the tree's defined loader)
12908      * @type TreeLoader
12909      * @property loader
12910      */
12911 };
12912 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12913     expand : function(deep, anim, callback){
12914         if(this.loading){ // if an async load is already running, waiting til it's done
12915             var timer;
12916             var f = function(){
12917                 if(!this.loading){ // done loading
12918                     clearInterval(timer);
12919                     this.expand(deep, anim, callback);
12920                 }
12921             }.createDelegate(this);
12922             timer = setInterval(f, 200);
12923             return;
12924         }
12925         if(!this.loaded){
12926             if(this.fireEvent("beforeload", this) === false){
12927                 return;
12928             }
12929             this.loading = true;
12930             this.ui.beforeLoad(this);
12931             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12932             if(loader){
12933                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12934                 return;
12935             }
12936         }
12937         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12938     },
12939     
12940     /**
12941      * Returns true if this node is currently loading
12942      * @return {Boolean}
12943      */
12944     isLoading : function(){
12945         return this.loading;  
12946     },
12947     
12948     loadComplete : function(deep, anim, callback){
12949         this.loading = false;
12950         this.loaded = true;
12951         this.ui.afterLoad(this);
12952         this.fireEvent("load", this);
12953         this.expand(deep, anim, callback);
12954     },
12955     
12956     /**
12957      * Returns true if this node has been loaded
12958      * @return {Boolean}
12959      */
12960     isLoaded : function(){
12961         return this.loaded;
12962     },
12963     
12964     hasChildNodes : function(){
12965         if(!this.isLeaf() && !this.loaded){
12966             return true;
12967         }else{
12968             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12969         }
12970     },
12971
12972     /**
12973      * Trigger a reload for this node
12974      * @param {Function} callback
12975      */
12976     reload : function(callback){
12977         this.collapse(false, false);
12978         while(this.firstChild){
12979             this.removeChild(this.firstChild);
12980         }
12981         this.childrenRendered = false;
12982         this.loaded = false;
12983         if(this.isHiddenRoot()){
12984             this.expanded = false;
12985         }
12986         this.expand(false, false, callback);
12987     }
12988 });/*
12989  * Based on:
12990  * Ext JS Library 1.1.1
12991  * Copyright(c) 2006-2007, Ext JS, LLC.
12992  *
12993  * Originally Released Under LGPL - original licence link has changed is not relivant.
12994  *
12995  * Fork - LGPL
12996  * <script type="text/javascript">
12997  */
12998  
12999 /**
13000  * @class Roo.tree.TreeNodeUI
13001  * @constructor
13002  * @param {Object} node The node to render
13003  * The TreeNode UI implementation is separate from the
13004  * tree implementation. Unless you are customizing the tree UI,
13005  * you should never have to use this directly.
13006  */
13007 Roo.tree.TreeNodeUI = function(node){
13008     this.node = node;
13009     this.rendered = false;
13010     this.animating = false;
13011     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13012 };
13013
13014 Roo.tree.TreeNodeUI.prototype = {
13015     removeChild : function(node){
13016         if(this.rendered){
13017             this.ctNode.removeChild(node.ui.getEl());
13018         }
13019     },
13020
13021     beforeLoad : function(){
13022          this.addClass("x-tree-node-loading");
13023     },
13024
13025     afterLoad : function(){
13026          this.removeClass("x-tree-node-loading");
13027     },
13028
13029     onTextChange : function(node, text, oldText){
13030         if(this.rendered){
13031             this.textNode.innerHTML = text;
13032         }
13033     },
13034
13035     onDisableChange : function(node, state){
13036         this.disabled = state;
13037         if(state){
13038             this.addClass("x-tree-node-disabled");
13039         }else{
13040             this.removeClass("x-tree-node-disabled");
13041         }
13042     },
13043
13044     onSelectedChange : function(state){
13045         if(state){
13046             this.focus();
13047             this.addClass("x-tree-selected");
13048         }else{
13049             //this.blur();
13050             this.removeClass("x-tree-selected");
13051         }
13052     },
13053
13054     onMove : function(tree, node, oldParent, newParent, index, refNode){
13055         this.childIndent = null;
13056         if(this.rendered){
13057             var targetNode = newParent.ui.getContainer();
13058             if(!targetNode){//target not rendered
13059                 this.holder = document.createElement("div");
13060                 this.holder.appendChild(this.wrap);
13061                 return;
13062             }
13063             var insertBefore = refNode ? refNode.ui.getEl() : null;
13064             if(insertBefore){
13065                 targetNode.insertBefore(this.wrap, insertBefore);
13066             }else{
13067                 targetNode.appendChild(this.wrap);
13068             }
13069             this.node.renderIndent(true);
13070         }
13071     },
13072
13073     addClass : function(cls){
13074         if(this.elNode){
13075             Roo.fly(this.elNode).addClass(cls);
13076         }
13077     },
13078
13079     removeClass : function(cls){
13080         if(this.elNode){
13081             Roo.fly(this.elNode).removeClass(cls);
13082         }
13083     },
13084
13085     remove : function(){
13086         if(this.rendered){
13087             this.holder = document.createElement("div");
13088             this.holder.appendChild(this.wrap);
13089         }
13090     },
13091
13092     fireEvent : function(){
13093         return this.node.fireEvent.apply(this.node, arguments);
13094     },
13095
13096     initEvents : function(){
13097         this.node.on("move", this.onMove, this);
13098         var E = Roo.EventManager;
13099         var a = this.anchor;
13100
13101         var el = Roo.fly(a, '_treeui');
13102
13103         if(Roo.isOpera){ // opera render bug ignores the CSS
13104             el.setStyle("text-decoration", "none");
13105         }
13106
13107         el.on("click", this.onClick, this);
13108         el.on("dblclick", this.onDblClick, this);
13109
13110         if(this.checkbox){
13111             Roo.EventManager.on(this.checkbox,
13112                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13113         }
13114
13115         el.on("contextmenu", this.onContextMenu, this);
13116
13117         var icon = Roo.fly(this.iconNode);
13118         icon.on("click", this.onClick, this);
13119         icon.on("dblclick", this.onDblClick, this);
13120         icon.on("contextmenu", this.onContextMenu, this);
13121         E.on(this.ecNode, "click", this.ecClick, this, true);
13122
13123         if(this.node.disabled){
13124             this.addClass("x-tree-node-disabled");
13125         }
13126         if(this.node.hidden){
13127             this.addClass("x-tree-node-disabled");
13128         }
13129         var ot = this.node.getOwnerTree();
13130         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13131         if(dd && (!this.node.isRoot || ot.rootVisible)){
13132             Roo.dd.Registry.register(this.elNode, {
13133                 node: this.node,
13134                 handles: this.getDDHandles(),
13135                 isHandle: false
13136             });
13137         }
13138     },
13139
13140     getDDHandles : function(){
13141         return [this.iconNode, this.textNode];
13142     },
13143
13144     hide : function(){
13145         if(this.rendered){
13146             this.wrap.style.display = "none";
13147         }
13148     },
13149
13150     show : function(){
13151         if(this.rendered){
13152             this.wrap.style.display = "";
13153         }
13154     },
13155
13156     onContextMenu : function(e){
13157         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13158             e.preventDefault();
13159             this.focus();
13160             this.fireEvent("contextmenu", this.node, e);
13161         }
13162     },
13163
13164     onClick : function(e){
13165         if(this.dropping){
13166             e.stopEvent();
13167             return;
13168         }
13169         if(this.fireEvent("beforeclick", this.node, e) !== false){
13170             if(!this.disabled && this.node.attributes.href){
13171                 this.fireEvent("click", this.node, e);
13172                 return;
13173             }
13174             e.preventDefault();
13175             if(this.disabled){
13176                 return;
13177             }
13178
13179             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13180                 this.node.toggle();
13181             }
13182
13183             this.fireEvent("click", this.node, e);
13184         }else{
13185             e.stopEvent();
13186         }
13187     },
13188
13189     onDblClick : function(e){
13190         e.preventDefault();
13191         if(this.disabled){
13192             return;
13193         }
13194         if(this.checkbox){
13195             this.toggleCheck();
13196         }
13197         if(!this.animating && this.node.hasChildNodes()){
13198             this.node.toggle();
13199         }
13200         this.fireEvent("dblclick", this.node, e);
13201     },
13202
13203     onCheckChange : function(){
13204         var checked = this.checkbox.checked;
13205         this.node.attributes.checked = checked;
13206         this.fireEvent('checkchange', this.node, checked);
13207     },
13208
13209     ecClick : function(e){
13210         if(!this.animating && this.node.hasChildNodes()){
13211             this.node.toggle();
13212         }
13213     },
13214
13215     startDrop : function(){
13216         this.dropping = true;
13217     },
13218
13219     // delayed drop so the click event doesn't get fired on a drop
13220     endDrop : function(){
13221        setTimeout(function(){
13222            this.dropping = false;
13223        }.createDelegate(this), 50);
13224     },
13225
13226     expand : function(){
13227         this.updateExpandIcon();
13228         this.ctNode.style.display = "";
13229     },
13230
13231     focus : function(){
13232         if(!this.node.preventHScroll){
13233             try{this.anchor.focus();
13234             }catch(e){}
13235         }else if(!Roo.isIE){
13236             try{
13237                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13238                 var l = noscroll.scrollLeft;
13239                 this.anchor.focus();
13240                 noscroll.scrollLeft = l;
13241             }catch(e){}
13242         }
13243     },
13244
13245     toggleCheck : function(value){
13246         var cb = this.checkbox;
13247         if(cb){
13248             cb.checked = (value === undefined ? !cb.checked : value);
13249         }
13250     },
13251
13252     blur : function(){
13253         try{
13254             this.anchor.blur();
13255         }catch(e){}
13256     },
13257
13258     animExpand : function(callback){
13259         var ct = Roo.get(this.ctNode);
13260         ct.stopFx();
13261         if(!this.node.hasChildNodes()){
13262             this.updateExpandIcon();
13263             this.ctNode.style.display = "";
13264             Roo.callback(callback);
13265             return;
13266         }
13267         this.animating = true;
13268         this.updateExpandIcon();
13269
13270         ct.slideIn('t', {
13271            callback : function(){
13272                this.animating = false;
13273                Roo.callback(callback);
13274             },
13275             scope: this,
13276             duration: this.node.ownerTree.duration || .25
13277         });
13278     },
13279
13280     highlight : function(){
13281         var tree = this.node.getOwnerTree();
13282         Roo.fly(this.wrap).highlight(
13283             tree.hlColor || "C3DAF9",
13284             {endColor: tree.hlBaseColor}
13285         );
13286     },
13287
13288     collapse : function(){
13289         this.updateExpandIcon();
13290         this.ctNode.style.display = "none";
13291     },
13292
13293     animCollapse : function(callback){
13294         var ct = Roo.get(this.ctNode);
13295         ct.enableDisplayMode('block');
13296         ct.stopFx();
13297
13298         this.animating = true;
13299         this.updateExpandIcon();
13300
13301         ct.slideOut('t', {
13302             callback : function(){
13303                this.animating = false;
13304                Roo.callback(callback);
13305             },
13306             scope: this,
13307             duration: this.node.ownerTree.duration || .25
13308         });
13309     },
13310
13311     getContainer : function(){
13312         return this.ctNode;
13313     },
13314
13315     getEl : function(){
13316         return this.wrap;
13317     },
13318
13319     appendDDGhost : function(ghostNode){
13320         ghostNode.appendChild(this.elNode.cloneNode(true));
13321     },
13322
13323     getDDRepairXY : function(){
13324         return Roo.lib.Dom.getXY(this.iconNode);
13325     },
13326
13327     onRender : function(){
13328         this.render();
13329     },
13330
13331     render : function(bulkRender){
13332         var n = this.node, a = n.attributes;
13333         var targetNode = n.parentNode ?
13334               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13335
13336         if(!this.rendered){
13337             this.rendered = true;
13338
13339             this.renderElements(n, a, targetNode, bulkRender);
13340
13341             if(a.qtip){
13342                if(this.textNode.setAttributeNS){
13343                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13344                    if(a.qtipTitle){
13345                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13346                    }
13347                }else{
13348                    this.textNode.setAttribute("ext:qtip", a.qtip);
13349                    if(a.qtipTitle){
13350                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13351                    }
13352                }
13353             }else if(a.qtipCfg){
13354                 a.qtipCfg.target = Roo.id(this.textNode);
13355                 Roo.QuickTips.register(a.qtipCfg);
13356             }
13357             this.initEvents();
13358             if(!this.node.expanded){
13359                 this.updateExpandIcon();
13360             }
13361         }else{
13362             if(bulkRender === true) {
13363                 targetNode.appendChild(this.wrap);
13364             }
13365         }
13366     },
13367
13368     renderElements : function(n, a, targetNode, bulkRender)
13369     {
13370         // add some indent caching, this helps performance when rendering a large tree
13371         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13372         var t = n.getOwnerTree();
13373         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13374         if (typeof(n.attributes.html) != 'undefined') {
13375             txt = n.attributes.html;
13376         }
13377         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13378         var cb = typeof a.checked == 'boolean';
13379         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13380         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13381             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13382             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13383             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13384             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13385             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13386              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13387                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13388             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13389             "</li>"];
13390
13391         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13392             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13393                                 n.nextSibling.ui.getEl(), buf.join(""));
13394         }else{
13395             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13396         }
13397
13398         this.elNode = this.wrap.childNodes[0];
13399         this.ctNode = this.wrap.childNodes[1];
13400         var cs = this.elNode.childNodes;
13401         this.indentNode = cs[0];
13402         this.ecNode = cs[1];
13403         this.iconNode = cs[2];
13404         var index = 3;
13405         if(cb){
13406             this.checkbox = cs[3];
13407             index++;
13408         }
13409         this.anchor = cs[index];
13410         this.textNode = cs[index].firstChild;
13411     },
13412
13413     getAnchor : function(){
13414         return this.anchor;
13415     },
13416
13417     getTextEl : function(){
13418         return this.textNode;
13419     },
13420
13421     getIconEl : function(){
13422         return this.iconNode;
13423     },
13424
13425     isChecked : function(){
13426         return this.checkbox ? this.checkbox.checked : false;
13427     },
13428
13429     updateExpandIcon : function(){
13430         if(this.rendered){
13431             var n = this.node, c1, c2;
13432             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13433             var hasChild = n.hasChildNodes();
13434             if(hasChild){
13435                 if(n.expanded){
13436                     cls += "-minus";
13437                     c1 = "x-tree-node-collapsed";
13438                     c2 = "x-tree-node-expanded";
13439                 }else{
13440                     cls += "-plus";
13441                     c1 = "x-tree-node-expanded";
13442                     c2 = "x-tree-node-collapsed";
13443                 }
13444                 if(this.wasLeaf){
13445                     this.removeClass("x-tree-node-leaf");
13446                     this.wasLeaf = false;
13447                 }
13448                 if(this.c1 != c1 || this.c2 != c2){
13449                     Roo.fly(this.elNode).replaceClass(c1, c2);
13450                     this.c1 = c1; this.c2 = c2;
13451                 }
13452             }else{
13453                 // this changes non-leafs into leafs if they have no children.
13454                 // it's not very rational behaviour..
13455                 
13456                 if(!this.wasLeaf && this.node.leaf){
13457                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13458                     delete this.c1;
13459                     delete this.c2;
13460                     this.wasLeaf = true;
13461                 }
13462             }
13463             var ecc = "x-tree-ec-icon "+cls;
13464             if(this.ecc != ecc){
13465                 this.ecNode.className = ecc;
13466                 this.ecc = ecc;
13467             }
13468         }
13469     },
13470
13471     getChildIndent : function(){
13472         if(!this.childIndent){
13473             var buf = [];
13474             var p = this.node;
13475             while(p){
13476                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13477                     if(!p.isLast()) {
13478                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13479                     } else {
13480                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13481                     }
13482                 }
13483                 p = p.parentNode;
13484             }
13485             this.childIndent = buf.join("");
13486         }
13487         return this.childIndent;
13488     },
13489
13490     renderIndent : function(){
13491         if(this.rendered){
13492             var indent = "";
13493             var p = this.node.parentNode;
13494             if(p){
13495                 indent = p.ui.getChildIndent();
13496             }
13497             if(this.indentMarkup != indent){ // don't rerender if not required
13498                 this.indentNode.innerHTML = indent;
13499                 this.indentMarkup = indent;
13500             }
13501             this.updateExpandIcon();
13502         }
13503     }
13504 };
13505
13506 Roo.tree.RootTreeNodeUI = function(){
13507     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13508 };
13509 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13510     render : function(){
13511         if(!this.rendered){
13512             var targetNode = this.node.ownerTree.innerCt.dom;
13513             this.node.expanded = true;
13514             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13515             this.wrap = this.ctNode = targetNode.firstChild;
13516         }
13517     },
13518     collapse : function(){
13519     },
13520     expand : function(){
13521     }
13522 });/*
13523  * Based on:
13524  * Ext JS Library 1.1.1
13525  * Copyright(c) 2006-2007, Ext JS, LLC.
13526  *
13527  * Originally Released Under LGPL - original licence link has changed is not relivant.
13528  *
13529  * Fork - LGPL
13530  * <script type="text/javascript">
13531  */
13532 /**
13533  * @class Roo.tree.TreeLoader
13534  * @extends Roo.util.Observable
13535  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13536  * nodes from a specified URL. The response must be a javascript Array definition
13537  * who's elements are node definition objects. eg:
13538  * <pre><code>
13539 {  success : true,
13540    data :      [
13541    
13542     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13543     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13544     ]
13545 }
13546
13547
13548 </code></pre>
13549  * <br><br>
13550  * The old style respose with just an array is still supported, but not recommended.
13551  * <br><br>
13552  *
13553  * A server request is sent, and child nodes are loaded only when a node is expanded.
13554  * The loading node's id is passed to the server under the parameter name "node" to
13555  * enable the server to produce the correct child nodes.
13556  * <br><br>
13557  * To pass extra parameters, an event handler may be attached to the "beforeload"
13558  * event, and the parameters specified in the TreeLoader's baseParams property:
13559  * <pre><code>
13560     myTreeLoader.on("beforeload", function(treeLoader, node) {
13561         this.baseParams.category = node.attributes.category;
13562     }, this);
13563 </code></pre><
13564  * This would pass an HTTP parameter called "category" to the server containing
13565  * the value of the Node's "category" attribute.
13566  * @constructor
13567  * Creates a new Treeloader.
13568  * @param {Object} config A config object containing config properties.
13569  */
13570 Roo.tree.TreeLoader = function(config){
13571     this.baseParams = {};
13572     this.requestMethod = "POST";
13573     Roo.apply(this, config);
13574
13575     this.addEvents({
13576     
13577         /**
13578          * @event beforeload
13579          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13580          * @param {Object} This TreeLoader object.
13581          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13582          * @param {Object} callback The callback function specified in the {@link #load} call.
13583          */
13584         beforeload : true,
13585         /**
13586          * @event load
13587          * Fires when the node has been successfuly loaded.
13588          * @param {Object} This TreeLoader object.
13589          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13590          * @param {Object} response The response object containing the data from the server.
13591          */
13592         load : true,
13593         /**
13594          * @event loadexception
13595          * Fires if the network request failed.
13596          * @param {Object} This TreeLoader object.
13597          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13598          * @param {Object} response The response object containing the data from the server.
13599          */
13600         loadexception : true,
13601         /**
13602          * @event create
13603          * Fires before a node is created, enabling you to return custom Node types 
13604          * @param {Object} This TreeLoader object.
13605          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13606          */
13607         create : true
13608     });
13609
13610     Roo.tree.TreeLoader.superclass.constructor.call(this);
13611 };
13612
13613 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13614     /**
13615     * @cfg {String} dataUrl The URL from which to request a Json string which
13616     * specifies an array of node definition object representing the child nodes
13617     * to be loaded.
13618     */
13619     /**
13620     * @cfg {String} requestMethod either GET or POST
13621     * defaults to POST (due to BC)
13622     * to be loaded.
13623     */
13624     /**
13625     * @cfg {Object} baseParams (optional) An object containing properties which
13626     * specify HTTP parameters to be passed to each request for child nodes.
13627     */
13628     /**
13629     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13630     * created by this loader. If the attributes sent by the server have an attribute in this object,
13631     * they take priority.
13632     */
13633     /**
13634     * @cfg {Object} uiProviders (optional) An object containing properties which
13635     * 
13636     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13637     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13638     * <i>uiProvider</i> attribute of a returned child node is a string rather
13639     * than a reference to a TreeNodeUI implementation, this that string value
13640     * is used as a property name in the uiProviders object. You can define the provider named
13641     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13642     */
13643     uiProviders : {},
13644
13645     /**
13646     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13647     * child nodes before loading.
13648     */
13649     clearOnLoad : true,
13650
13651     /**
13652     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13653     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13654     * Grid query { data : [ .....] }
13655     */
13656     
13657     root : false,
13658      /**
13659     * @cfg {String} queryParam (optional) 
13660     * Name of the query as it will be passed on the querystring (defaults to 'node')
13661     * eg. the request will be ?node=[id]
13662     */
13663     
13664     
13665     queryParam: false,
13666     
13667     /**
13668      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13669      * This is called automatically when a node is expanded, but may be used to reload
13670      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13671      * @param {Roo.tree.TreeNode} node
13672      * @param {Function} callback
13673      */
13674     load : function(node, callback){
13675         if(this.clearOnLoad){
13676             while(node.firstChild){
13677                 node.removeChild(node.firstChild);
13678             }
13679         }
13680         if(node.attributes.children){ // preloaded json children
13681             var cs = node.attributes.children;
13682             for(var i = 0, len = cs.length; i < len; i++){
13683                 node.appendChild(this.createNode(cs[i]));
13684             }
13685             if(typeof callback == "function"){
13686                 callback();
13687             }
13688         }else if(this.dataUrl){
13689             this.requestData(node, callback);
13690         }
13691     },
13692
13693     getParams: function(node){
13694         var buf = [], bp = this.baseParams;
13695         for(var key in bp){
13696             if(typeof bp[key] != "function"){
13697                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13698             }
13699         }
13700         var n = this.queryParam === false ? 'node' : this.queryParam;
13701         buf.push(n + "=", encodeURIComponent(node.id));
13702         return buf.join("");
13703     },
13704
13705     requestData : function(node, callback){
13706         if(this.fireEvent("beforeload", this, node, callback) !== false){
13707             this.transId = Roo.Ajax.request({
13708                 method:this.requestMethod,
13709                 url: this.dataUrl||this.url,
13710                 success: this.handleResponse,
13711                 failure: this.handleFailure,
13712                 scope: this,
13713                 argument: {callback: callback, node: node},
13714                 params: this.getParams(node)
13715             });
13716         }else{
13717             // if the load is cancelled, make sure we notify
13718             // the node that we are done
13719             if(typeof callback == "function"){
13720                 callback();
13721             }
13722         }
13723     },
13724
13725     isLoading : function(){
13726         return this.transId ? true : false;
13727     },
13728
13729     abort : function(){
13730         if(this.isLoading()){
13731             Roo.Ajax.abort(this.transId);
13732         }
13733     },
13734
13735     // private
13736     createNode : function(attr)
13737     {
13738         // apply baseAttrs, nice idea Corey!
13739         if(this.baseAttrs){
13740             Roo.applyIf(attr, this.baseAttrs);
13741         }
13742         if(this.applyLoader !== false){
13743             attr.loader = this;
13744         }
13745         // uiProvider = depreciated..
13746         
13747         if(typeof(attr.uiProvider) == 'string'){
13748            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13749                 /**  eval:var:attr */ eval(attr.uiProvider);
13750         }
13751         if(typeof(this.uiProviders['default']) != 'undefined') {
13752             attr.uiProvider = this.uiProviders['default'];
13753         }
13754         
13755         this.fireEvent('create', this, attr);
13756         
13757         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13758         return(attr.leaf ?
13759                         new Roo.tree.TreeNode(attr) :
13760                         new Roo.tree.AsyncTreeNode(attr));
13761     },
13762
13763     processResponse : function(response, node, callback)
13764     {
13765         var json = response.responseText;
13766         try {
13767             
13768             var o = Roo.decode(json);
13769             
13770             if (this.root === false && typeof(o.success) != undefined) {
13771                 this.root = 'data'; // the default behaviour for list like data..
13772                 }
13773                 
13774             if (this.root !== false &&  !o.success) {
13775                 // it's a failure condition.
13776                 var a = response.argument;
13777                 this.fireEvent("loadexception", this, a.node, response);
13778                 Roo.log("Load failed - should have a handler really");
13779                 return;
13780             }
13781             
13782             
13783             
13784             if (this.root !== false) {
13785                  o = o[this.root];
13786             }
13787             
13788             for(var i = 0, len = o.length; i < len; i++){
13789                 var n = this.createNode(o[i]);
13790                 if(n){
13791                     node.appendChild(n);
13792                 }
13793             }
13794             if(typeof callback == "function"){
13795                 callback(this, node);
13796             }
13797         }catch(e){
13798             this.handleFailure(response);
13799         }
13800     },
13801
13802     handleResponse : function(response){
13803         this.transId = false;
13804         var a = response.argument;
13805         this.processResponse(response, a.node, a.callback);
13806         this.fireEvent("load", this, a.node, response);
13807     },
13808
13809     handleFailure : function(response)
13810     {
13811         // should handle failure better..
13812         this.transId = false;
13813         var a = response.argument;
13814         this.fireEvent("loadexception", this, a.node, response);
13815         if(typeof a.callback == "function"){
13816             a.callback(this, a.node);
13817         }
13818     }
13819 });/*
13820  * Based on:
13821  * Ext JS Library 1.1.1
13822  * Copyright(c) 2006-2007, Ext JS, LLC.
13823  *
13824  * Originally Released Under LGPL - original licence link has changed is not relivant.
13825  *
13826  * Fork - LGPL
13827  * <script type="text/javascript">
13828  */
13829
13830 /**
13831 * @class Roo.tree.TreeFilter
13832 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13833 * @param {TreePanel} tree
13834 * @param {Object} config (optional)
13835  */
13836 Roo.tree.TreeFilter = function(tree, config){
13837     this.tree = tree;
13838     this.filtered = {};
13839     Roo.apply(this, config);
13840 };
13841
13842 Roo.tree.TreeFilter.prototype = {
13843     clearBlank:false,
13844     reverse:false,
13845     autoClear:false,
13846     remove:false,
13847
13848      /**
13849      * Filter the data by a specific attribute.
13850      * @param {String/RegExp} value Either string that the attribute value
13851      * should start with or a RegExp to test against the attribute
13852      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13853      * @param {TreeNode} startNode (optional) The node to start the filter at.
13854      */
13855     filter : function(value, attr, startNode){
13856         attr = attr || "text";
13857         var f;
13858         if(typeof value == "string"){
13859             var vlen = value.length;
13860             // auto clear empty filter
13861             if(vlen == 0 && this.clearBlank){
13862                 this.clear();
13863                 return;
13864             }
13865             value = value.toLowerCase();
13866             f = function(n){
13867                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13868             };
13869         }else if(value.exec){ // regex?
13870             f = function(n){
13871                 return value.test(n.attributes[attr]);
13872             };
13873         }else{
13874             throw 'Illegal filter type, must be string or regex';
13875         }
13876         this.filterBy(f, null, startNode);
13877         },
13878
13879     /**
13880      * Filter by a function. The passed function will be called with each
13881      * node in the tree (or from the startNode). If the function returns true, the node is kept
13882      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13883      * @param {Function} fn The filter function
13884      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13885      */
13886     filterBy : function(fn, scope, startNode){
13887         startNode = startNode || this.tree.root;
13888         if(this.autoClear){
13889             this.clear();
13890         }
13891         var af = this.filtered, rv = this.reverse;
13892         var f = function(n){
13893             if(n == startNode){
13894                 return true;
13895             }
13896             if(af[n.id]){
13897                 return false;
13898             }
13899             var m = fn.call(scope || n, n);
13900             if(!m || rv){
13901                 af[n.id] = n;
13902                 n.ui.hide();
13903                 return false;
13904             }
13905             return true;
13906         };
13907         startNode.cascade(f);
13908         if(this.remove){
13909            for(var id in af){
13910                if(typeof id != "function"){
13911                    var n = af[id];
13912                    if(n && n.parentNode){
13913                        n.parentNode.removeChild(n);
13914                    }
13915                }
13916            }
13917         }
13918     },
13919
13920     /**
13921      * Clears the current filter. Note: with the "remove" option
13922      * set a filter cannot be cleared.
13923      */
13924     clear : function(){
13925         var t = this.tree;
13926         var af = this.filtered;
13927         for(var id in af){
13928             if(typeof id != "function"){
13929                 var n = af[id];
13930                 if(n){
13931                     n.ui.show();
13932                 }
13933             }
13934         }
13935         this.filtered = {};
13936     }
13937 };
13938 /*
13939  * Based on:
13940  * Ext JS Library 1.1.1
13941  * Copyright(c) 2006-2007, Ext JS, LLC.
13942  *
13943  * Originally Released Under LGPL - original licence link has changed is not relivant.
13944  *
13945  * Fork - LGPL
13946  * <script type="text/javascript">
13947  */
13948  
13949
13950 /**
13951  * @class Roo.tree.TreeSorter
13952  * Provides sorting of nodes in a TreePanel
13953  * 
13954  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13955  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13956  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13957  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13958  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13959  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13960  * @constructor
13961  * @param {TreePanel} tree
13962  * @param {Object} config
13963  */
13964 Roo.tree.TreeSorter = function(tree, config){
13965     Roo.apply(this, config);
13966     tree.on("beforechildrenrendered", this.doSort, this);
13967     tree.on("append", this.updateSort, this);
13968     tree.on("insert", this.updateSort, this);
13969     
13970     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13971     var p = this.property || "text";
13972     var sortType = this.sortType;
13973     var fs = this.folderSort;
13974     var cs = this.caseSensitive === true;
13975     var leafAttr = this.leafAttr || 'leaf';
13976
13977     this.sortFn = function(n1, n2){
13978         if(fs){
13979             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13980                 return 1;
13981             }
13982             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13983                 return -1;
13984             }
13985         }
13986         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13987         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13988         if(v1 < v2){
13989                         return dsc ? +1 : -1;
13990                 }else if(v1 > v2){
13991                         return dsc ? -1 : +1;
13992         }else{
13993                 return 0;
13994         }
13995     };
13996 };
13997
13998 Roo.tree.TreeSorter.prototype = {
13999     doSort : function(node){
14000         node.sort(this.sortFn);
14001     },
14002     
14003     compareNodes : function(n1, n2){
14004         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14005     },
14006     
14007     updateSort : function(tree, node){
14008         if(node.childrenRendered){
14009             this.doSort.defer(1, this, [node]);
14010         }
14011     }
14012 };/*
14013  * Based on:
14014  * Ext JS Library 1.1.1
14015  * Copyright(c) 2006-2007, Ext JS, LLC.
14016  *
14017  * Originally Released Under LGPL - original licence link has changed is not relivant.
14018  *
14019  * Fork - LGPL
14020  * <script type="text/javascript">
14021  */
14022
14023 if(Roo.dd.DropZone){
14024     
14025 Roo.tree.TreeDropZone = function(tree, config){
14026     this.allowParentInsert = false;
14027     this.allowContainerDrop = false;
14028     this.appendOnly = false;
14029     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14030     this.tree = tree;
14031     this.lastInsertClass = "x-tree-no-status";
14032     this.dragOverData = {};
14033 };
14034
14035 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14036     ddGroup : "TreeDD",
14037     scroll:  true,
14038     
14039     expandDelay : 1000,
14040     
14041     expandNode : function(node){
14042         if(node.hasChildNodes() && !node.isExpanded()){
14043             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14044         }
14045     },
14046     
14047     queueExpand : function(node){
14048         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14049     },
14050     
14051     cancelExpand : function(){
14052         if(this.expandProcId){
14053             clearTimeout(this.expandProcId);
14054             this.expandProcId = false;
14055         }
14056     },
14057     
14058     isValidDropPoint : function(n, pt, dd, e, data){
14059         if(!n || !data){ return false; }
14060         var targetNode = n.node;
14061         var dropNode = data.node;
14062         // default drop rules
14063         if(!(targetNode && targetNode.isTarget && pt)){
14064             return false;
14065         }
14066         if(pt == "append" && targetNode.allowChildren === false){
14067             return false;
14068         }
14069         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14070             return false;
14071         }
14072         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14073             return false;
14074         }
14075         // reuse the object
14076         var overEvent = this.dragOverData;
14077         overEvent.tree = this.tree;
14078         overEvent.target = targetNode;
14079         overEvent.data = data;
14080         overEvent.point = pt;
14081         overEvent.source = dd;
14082         overEvent.rawEvent = e;
14083         overEvent.dropNode = dropNode;
14084         overEvent.cancel = false;  
14085         var result = this.tree.fireEvent("nodedragover", overEvent);
14086         return overEvent.cancel === false && result !== false;
14087     },
14088     
14089     getDropPoint : function(e, n, dd)
14090     {
14091         var tn = n.node;
14092         if(tn.isRoot){
14093             return tn.allowChildren !== false ? "append" : false; // always append for root
14094         }
14095         var dragEl = n.ddel;
14096         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14097         var y = Roo.lib.Event.getPageY(e);
14098         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14099         
14100         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14101         var noAppend = tn.allowChildren === false;
14102         if(this.appendOnly || tn.parentNode.allowChildren === false){
14103             return noAppend ? false : "append";
14104         }
14105         var noBelow = false;
14106         if(!this.allowParentInsert){
14107             noBelow = tn.hasChildNodes() && tn.isExpanded();
14108         }
14109         var q = (b - t) / (noAppend ? 2 : 3);
14110         if(y >= t && y < (t + q)){
14111             return "above";
14112         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14113             return "below";
14114         }else{
14115             return "append";
14116         }
14117     },
14118     
14119     onNodeEnter : function(n, dd, e, data)
14120     {
14121         this.cancelExpand();
14122     },
14123     
14124     onNodeOver : function(n, dd, e, data)
14125     {
14126        
14127         var pt = this.getDropPoint(e, n, dd);
14128         var node = n.node;
14129         
14130         // auto node expand check
14131         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14132             this.queueExpand(node);
14133         }else if(pt != "append"){
14134             this.cancelExpand();
14135         }
14136         
14137         // set the insert point style on the target node
14138         var returnCls = this.dropNotAllowed;
14139         if(this.isValidDropPoint(n, pt, dd, e, data)){
14140            if(pt){
14141                var el = n.ddel;
14142                var cls;
14143                if(pt == "above"){
14144                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14145                    cls = "x-tree-drag-insert-above";
14146                }else if(pt == "below"){
14147                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14148                    cls = "x-tree-drag-insert-below";
14149                }else{
14150                    returnCls = "x-tree-drop-ok-append";
14151                    cls = "x-tree-drag-append";
14152                }
14153                if(this.lastInsertClass != cls){
14154                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14155                    this.lastInsertClass = cls;
14156                }
14157            }
14158        }
14159        return returnCls;
14160     },
14161     
14162     onNodeOut : function(n, dd, e, data){
14163         
14164         this.cancelExpand();
14165         this.removeDropIndicators(n);
14166     },
14167     
14168     onNodeDrop : function(n, dd, e, data){
14169         var point = this.getDropPoint(e, n, dd);
14170         var targetNode = n.node;
14171         targetNode.ui.startDrop();
14172         if(!this.isValidDropPoint(n, point, dd, e, data)){
14173             targetNode.ui.endDrop();
14174             return false;
14175         }
14176         // first try to find the drop node
14177         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14178         var dropEvent = {
14179             tree : this.tree,
14180             target: targetNode,
14181             data: data,
14182             point: point,
14183             source: dd,
14184             rawEvent: e,
14185             dropNode: dropNode,
14186             cancel: !dropNode   
14187         };
14188         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14189         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14190             targetNode.ui.endDrop();
14191             return false;
14192         }
14193         // allow target changing
14194         targetNode = dropEvent.target;
14195         if(point == "append" && !targetNode.isExpanded()){
14196             targetNode.expand(false, null, function(){
14197                 this.completeDrop(dropEvent);
14198             }.createDelegate(this));
14199         }else{
14200             this.completeDrop(dropEvent);
14201         }
14202         return true;
14203     },
14204     
14205     completeDrop : function(de){
14206         var ns = de.dropNode, p = de.point, t = de.target;
14207         if(!(ns instanceof Array)){
14208             ns = [ns];
14209         }
14210         var n;
14211         for(var i = 0, len = ns.length; i < len; i++){
14212             n = ns[i];
14213             if(p == "above"){
14214                 t.parentNode.insertBefore(n, t);
14215             }else if(p == "below"){
14216                 t.parentNode.insertBefore(n, t.nextSibling);
14217             }else{
14218                 t.appendChild(n);
14219             }
14220         }
14221         n.ui.focus();
14222         if(this.tree.hlDrop){
14223             n.ui.highlight();
14224         }
14225         t.ui.endDrop();
14226         this.tree.fireEvent("nodedrop", de);
14227     },
14228     
14229     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14230         if(this.tree.hlDrop){
14231             dropNode.ui.focus();
14232             dropNode.ui.highlight();
14233         }
14234         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14235     },
14236     
14237     getTree : function(){
14238         return this.tree;
14239     },
14240     
14241     removeDropIndicators : function(n){
14242         if(n && n.ddel){
14243             var el = n.ddel;
14244             Roo.fly(el).removeClass([
14245                     "x-tree-drag-insert-above",
14246                     "x-tree-drag-insert-below",
14247                     "x-tree-drag-append"]);
14248             this.lastInsertClass = "_noclass";
14249         }
14250     },
14251     
14252     beforeDragDrop : function(target, e, id){
14253         this.cancelExpand();
14254         return true;
14255     },
14256     
14257     afterRepair : function(data){
14258         if(data && Roo.enableFx){
14259             data.node.ui.highlight();
14260         }
14261         this.hideProxy();
14262     } 
14263     
14264 });
14265
14266 }
14267 /*
14268  * Based on:
14269  * Ext JS Library 1.1.1
14270  * Copyright(c) 2006-2007, Ext JS, LLC.
14271  *
14272  * Originally Released Under LGPL - original licence link has changed is not relivant.
14273  *
14274  * Fork - LGPL
14275  * <script type="text/javascript">
14276  */
14277  
14278
14279 if(Roo.dd.DragZone){
14280 Roo.tree.TreeDragZone = function(tree, config){
14281     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14282     this.tree = tree;
14283 };
14284
14285 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14286     ddGroup : "TreeDD",
14287    
14288     onBeforeDrag : function(data, e){
14289         var n = data.node;
14290         return n && n.draggable && !n.disabled;
14291     },
14292      
14293     
14294     onInitDrag : function(e){
14295         var data = this.dragData;
14296         this.tree.getSelectionModel().select(data.node);
14297         this.proxy.update("");
14298         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14299         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14300     },
14301     
14302     getRepairXY : function(e, data){
14303         return data.node.ui.getDDRepairXY();
14304     },
14305     
14306     onEndDrag : function(data, e){
14307         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14308         
14309         
14310     },
14311     
14312     onValidDrop : function(dd, e, id){
14313         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14314         this.hideProxy();
14315     },
14316     
14317     beforeInvalidDrop : function(e, id){
14318         // this scrolls the original position back into view
14319         var sm = this.tree.getSelectionModel();
14320         sm.clearSelections();
14321         sm.select(this.dragData.node);
14322     }
14323 });
14324 }/*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.tree.TreeEditor
14336  * @extends Roo.Editor
14337  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14338  * as the editor field.
14339  * @constructor
14340  * @param {Object} config (used to be the tree panel.)
14341  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14342  * 
14343  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14344  * @cfg {Roo.form.TextField|Object} field The field configuration
14345  *
14346  * 
14347  */
14348 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14349     var tree = config;
14350     var field;
14351     if (oldconfig) { // old style..
14352         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14353     } else {
14354         // new style..
14355         tree = config.tree;
14356         config.field = config.field  || {};
14357         config.field.xtype = 'TextField';
14358         field = Roo.factory(config.field, Roo.form);
14359     }
14360     config = config || {};
14361     
14362     
14363     this.addEvents({
14364         /**
14365          * @event beforenodeedit
14366          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14367          * false from the handler of this event.
14368          * @param {Editor} this
14369          * @param {Roo.tree.Node} node 
14370          */
14371         "beforenodeedit" : true
14372     });
14373     
14374     //Roo.log(config);
14375     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14376
14377     this.tree = tree;
14378
14379     tree.on('beforeclick', this.beforeNodeClick, this);
14380     tree.getTreeEl().on('mousedown', this.hide, this);
14381     this.on('complete', this.updateNode, this);
14382     this.on('beforestartedit', this.fitToTree, this);
14383     this.on('startedit', this.bindScroll, this, {delay:10});
14384     this.on('specialkey', this.onSpecialKey, this);
14385 };
14386
14387 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14388     /**
14389      * @cfg {String} alignment
14390      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14391      */
14392     alignment: "l-l",
14393     // inherit
14394     autoSize: false,
14395     /**
14396      * @cfg {Boolean} hideEl
14397      * True to hide the bound element while the editor is displayed (defaults to false)
14398      */
14399     hideEl : false,
14400     /**
14401      * @cfg {String} cls
14402      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14403      */
14404     cls: "x-small-editor x-tree-editor",
14405     /**
14406      * @cfg {Boolean} shim
14407      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14408      */
14409     shim:false,
14410     // inherit
14411     shadow:"frame",
14412     /**
14413      * @cfg {Number} maxWidth
14414      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14415      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14416      * scroll and client offsets into account prior to each edit.
14417      */
14418     maxWidth: 250,
14419
14420     editDelay : 350,
14421
14422     // private
14423     fitToTree : function(ed, el){
14424         var td = this.tree.getTreeEl().dom, nd = el.dom;
14425         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14426             td.scrollLeft = nd.offsetLeft;
14427         }
14428         var w = Math.min(
14429                 this.maxWidth,
14430                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14431         this.setSize(w, '');
14432         
14433         return this.fireEvent('beforenodeedit', this, this.editNode);
14434         
14435     },
14436
14437     // private
14438     triggerEdit : function(node){
14439         this.completeEdit();
14440         this.editNode = node;
14441         this.startEdit(node.ui.textNode, node.text);
14442     },
14443
14444     // private
14445     bindScroll : function(){
14446         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14447     },
14448
14449     // private
14450     beforeNodeClick : function(node, e){
14451         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14452         this.lastClick = new Date();
14453         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14454             e.stopEvent();
14455             this.triggerEdit(node);
14456             return false;
14457         }
14458         return true;
14459     },
14460
14461     // private
14462     updateNode : function(ed, value){
14463         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14464         this.editNode.setText(value);
14465     },
14466
14467     // private
14468     onHide : function(){
14469         Roo.tree.TreeEditor.superclass.onHide.call(this);
14470         if(this.editNode){
14471             this.editNode.ui.focus();
14472         }
14473     },
14474
14475     // private
14476     onSpecialKey : function(field, e){
14477         var k = e.getKey();
14478         if(k == e.ESC){
14479             e.stopEvent();
14480             this.cancelEdit();
14481         }else if(k == e.ENTER && !e.hasModifier()){
14482             e.stopEvent();
14483             this.completeEdit();
14484         }
14485     }
14486 });//<Script type="text/javascript">
14487 /*
14488  * Based on:
14489  * Ext JS Library 1.1.1
14490  * Copyright(c) 2006-2007, Ext JS, LLC.
14491  *
14492  * Originally Released Under LGPL - original licence link has changed is not relivant.
14493  *
14494  * Fork - LGPL
14495  * <script type="text/javascript">
14496  */
14497  
14498 /**
14499  * Not documented??? - probably should be...
14500  */
14501
14502 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14503     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14504     
14505     renderElements : function(n, a, targetNode, bulkRender){
14506         //consel.log("renderElements?");
14507         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14508
14509         var t = n.getOwnerTree();
14510         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14511         
14512         var cols = t.columns;
14513         var bw = t.borderWidth;
14514         var c = cols[0];
14515         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14516          var cb = typeof a.checked == "boolean";
14517         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14518         var colcls = 'x-t-' + tid + '-c0';
14519         var buf = [
14520             '<li class="x-tree-node">',
14521             
14522                 
14523                 '<div class="x-tree-node-el ', a.cls,'">',
14524                     // extran...
14525                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14526                 
14527                 
14528                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14529                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14530                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14531                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14532                            (a.iconCls ? ' '+a.iconCls : ''),
14533                            '" unselectable="on" />',
14534                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14535                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14536                              
14537                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14538                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14539                             '<span unselectable="on" qtip="' + tx + '">',
14540                              tx,
14541                              '</span></a>' ,
14542                     '</div>',
14543                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14544                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14545                  ];
14546         for(var i = 1, len = cols.length; i < len; i++){
14547             c = cols[i];
14548             colcls = 'x-t-' + tid + '-c' +i;
14549             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14550             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14551                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14552                       "</div>");
14553          }
14554          
14555          buf.push(
14556             '</a>',
14557             '<div class="x-clear"></div></div>',
14558             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14559             "</li>");
14560         
14561         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14562             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14563                                 n.nextSibling.ui.getEl(), buf.join(""));
14564         }else{
14565             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14566         }
14567         var el = this.wrap.firstChild;
14568         this.elRow = el;
14569         this.elNode = el.firstChild;
14570         this.ranchor = el.childNodes[1];
14571         this.ctNode = this.wrap.childNodes[1];
14572         var cs = el.firstChild.childNodes;
14573         this.indentNode = cs[0];
14574         this.ecNode = cs[1];
14575         this.iconNode = cs[2];
14576         var index = 3;
14577         if(cb){
14578             this.checkbox = cs[3];
14579             index++;
14580         }
14581         this.anchor = cs[index];
14582         
14583         this.textNode = cs[index].firstChild;
14584         
14585         //el.on("click", this.onClick, this);
14586         //el.on("dblclick", this.onDblClick, this);
14587         
14588         
14589        // console.log(this);
14590     },
14591     initEvents : function(){
14592         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14593         
14594             
14595         var a = this.ranchor;
14596
14597         var el = Roo.get(a);
14598
14599         if(Roo.isOpera){ // opera render bug ignores the CSS
14600             el.setStyle("text-decoration", "none");
14601         }
14602
14603         el.on("click", this.onClick, this);
14604         el.on("dblclick", this.onDblClick, this);
14605         el.on("contextmenu", this.onContextMenu, this);
14606         
14607     },
14608     
14609     /*onSelectedChange : function(state){
14610         if(state){
14611             this.focus();
14612             this.addClass("x-tree-selected");
14613         }else{
14614             //this.blur();
14615             this.removeClass("x-tree-selected");
14616         }
14617     },*/
14618     addClass : function(cls){
14619         if(this.elRow){
14620             Roo.fly(this.elRow).addClass(cls);
14621         }
14622         
14623     },
14624     
14625     
14626     removeClass : function(cls){
14627         if(this.elRow){
14628             Roo.fly(this.elRow).removeClass(cls);
14629         }
14630     }
14631
14632     
14633     
14634 });//<Script type="text/javascript">
14635
14636 /*
14637  * Based on:
14638  * Ext JS Library 1.1.1
14639  * Copyright(c) 2006-2007, Ext JS, LLC.
14640  *
14641  * Originally Released Under LGPL - original licence link has changed is not relivant.
14642  *
14643  * Fork - LGPL
14644  * <script type="text/javascript">
14645  */
14646  
14647
14648 /**
14649  * @class Roo.tree.ColumnTree
14650  * @extends Roo.data.TreePanel
14651  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14652  * @cfg {int} borderWidth  compined right/left border allowance
14653  * @constructor
14654  * @param {String/HTMLElement/Element} el The container element
14655  * @param {Object} config
14656  */
14657 Roo.tree.ColumnTree =  function(el, config)
14658 {
14659    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14660    this.addEvents({
14661         /**
14662         * @event resize
14663         * Fire this event on a container when it resizes
14664         * @param {int} w Width
14665         * @param {int} h Height
14666         */
14667        "resize" : true
14668     });
14669     this.on('resize', this.onResize, this);
14670 };
14671
14672 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14673     //lines:false,
14674     
14675     
14676     borderWidth: Roo.isBorderBox ? 0 : 2, 
14677     headEls : false,
14678     
14679     render : function(){
14680         // add the header.....
14681        
14682         Roo.tree.ColumnTree.superclass.render.apply(this);
14683         
14684         this.el.addClass('x-column-tree');
14685         
14686         this.headers = this.el.createChild(
14687             {cls:'x-tree-headers'},this.innerCt.dom);
14688    
14689         var cols = this.columns, c;
14690         var totalWidth = 0;
14691         this.headEls = [];
14692         var  len = cols.length;
14693         for(var i = 0; i < len; i++){
14694              c = cols[i];
14695              totalWidth += c.width;
14696             this.headEls.push(this.headers.createChild({
14697                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14698                  cn: {
14699                      cls:'x-tree-hd-text',
14700                      html: c.header
14701                  },
14702                  style:'width:'+(c.width-this.borderWidth)+'px;'
14703              }));
14704         }
14705         this.headers.createChild({cls:'x-clear'});
14706         // prevent floats from wrapping when clipped
14707         this.headers.setWidth(totalWidth);
14708         //this.innerCt.setWidth(totalWidth);
14709         this.innerCt.setStyle({ overflow: 'auto' });
14710         this.onResize(this.width, this.height);
14711              
14712         
14713     },
14714     onResize : function(w,h)
14715     {
14716         this.height = h;
14717         this.width = w;
14718         // resize cols..
14719         this.innerCt.setWidth(this.width);
14720         this.innerCt.setHeight(this.height-20);
14721         
14722         // headers...
14723         var cols = this.columns, c;
14724         var totalWidth = 0;
14725         var expEl = false;
14726         var len = cols.length;
14727         for(var i = 0; i < len; i++){
14728             c = cols[i];
14729             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14730                 // it's the expander..
14731                 expEl  = this.headEls[i];
14732                 continue;
14733             }
14734             totalWidth += c.width;
14735             
14736         }
14737         if (expEl) {
14738             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14739         }
14740         this.headers.setWidth(w-20);
14741
14742         
14743         
14744         
14745     }
14746 });
14747 /*
14748  * Based on:
14749  * Ext JS Library 1.1.1
14750  * Copyright(c) 2006-2007, Ext JS, LLC.
14751  *
14752  * Originally Released Under LGPL - original licence link has changed is not relivant.
14753  *
14754  * Fork - LGPL
14755  * <script type="text/javascript">
14756  */
14757  
14758 /**
14759  * @class Roo.menu.Menu
14760  * @extends Roo.util.Observable
14761  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14762  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14763  * @constructor
14764  * Creates a new Menu
14765  * @param {Object} config Configuration options
14766  */
14767 Roo.menu.Menu = function(config){
14768     Roo.apply(this, config);
14769     this.id = this.id || Roo.id();
14770     this.addEvents({
14771         /**
14772          * @event beforeshow
14773          * Fires before this menu is displayed
14774          * @param {Roo.menu.Menu} this
14775          */
14776         beforeshow : true,
14777         /**
14778          * @event beforehide
14779          * Fires before this menu is hidden
14780          * @param {Roo.menu.Menu} this
14781          */
14782         beforehide : true,
14783         /**
14784          * @event show
14785          * Fires after this menu is displayed
14786          * @param {Roo.menu.Menu} this
14787          */
14788         show : true,
14789         /**
14790          * @event hide
14791          * Fires after this menu is hidden
14792          * @param {Roo.menu.Menu} this
14793          */
14794         hide : true,
14795         /**
14796          * @event click
14797          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14798          * @param {Roo.menu.Menu} this
14799          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14800          * @param {Roo.EventObject} e
14801          */
14802         click : true,
14803         /**
14804          * @event mouseover
14805          * Fires when the mouse is hovering over this menu
14806          * @param {Roo.menu.Menu} this
14807          * @param {Roo.EventObject} e
14808          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14809          */
14810         mouseover : true,
14811         /**
14812          * @event mouseout
14813          * Fires when the mouse exits this menu
14814          * @param {Roo.menu.Menu} this
14815          * @param {Roo.EventObject} e
14816          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14817          */
14818         mouseout : true,
14819         /**
14820          * @event itemclick
14821          * Fires when a menu item contained in this menu is clicked
14822          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14823          * @param {Roo.EventObject} e
14824          */
14825         itemclick: true
14826     });
14827     if (this.registerMenu) {
14828         Roo.menu.MenuMgr.register(this);
14829     }
14830     
14831     var mis = this.items;
14832     this.items = new Roo.util.MixedCollection();
14833     if(mis){
14834         this.add.apply(this, mis);
14835     }
14836 };
14837
14838 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14839     /**
14840      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14841      */
14842     minWidth : 120,
14843     /**
14844      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14845      * for bottom-right shadow (defaults to "sides")
14846      */
14847     shadow : "sides",
14848     /**
14849      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14850      * this menu (defaults to "tl-tr?")
14851      */
14852     subMenuAlign : "tl-tr?",
14853     /**
14854      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14855      * relative to its element of origin (defaults to "tl-bl?")
14856      */
14857     defaultAlign : "tl-bl?",
14858     /**
14859      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14860      */
14861     allowOtherMenus : false,
14862     /**
14863      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14864      */
14865     registerMenu : true,
14866
14867     hidden:true,
14868
14869     // private
14870     render : function(){
14871         if(this.el){
14872             return;
14873         }
14874         var el = this.el = new Roo.Layer({
14875             cls: "x-menu",
14876             shadow:this.shadow,
14877             constrain: false,
14878             parentEl: this.parentEl || document.body,
14879             zindex:15000
14880         });
14881
14882         this.keyNav = new Roo.menu.MenuNav(this);
14883
14884         if(this.plain){
14885             el.addClass("x-menu-plain");
14886         }
14887         if(this.cls){
14888             el.addClass(this.cls);
14889         }
14890         // generic focus element
14891         this.focusEl = el.createChild({
14892             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14893         });
14894         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14895         //disabling touch- as it's causing issues ..
14896         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14897         ul.on('click'   , this.onClick, this);
14898         
14899         
14900         ul.on("mouseover", this.onMouseOver, this);
14901         ul.on("mouseout", this.onMouseOut, this);
14902         this.items.each(function(item){
14903             if (item.hidden) {
14904                 return;
14905             }
14906             
14907             var li = document.createElement("li");
14908             li.className = "x-menu-list-item";
14909             ul.dom.appendChild(li);
14910             item.render(li, this);
14911         }, this);
14912         this.ul = ul;
14913         this.autoWidth();
14914     },
14915
14916     // private
14917     autoWidth : function(){
14918         var el = this.el, ul = this.ul;
14919         if(!el){
14920             return;
14921         }
14922         var w = this.width;
14923         if(w){
14924             el.setWidth(w);
14925         }else if(Roo.isIE){
14926             el.setWidth(this.minWidth);
14927             var t = el.dom.offsetWidth; // force recalc
14928             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14929         }
14930     },
14931
14932     // private
14933     delayAutoWidth : function(){
14934         if(this.rendered){
14935             if(!this.awTask){
14936                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14937             }
14938             this.awTask.delay(20);
14939         }
14940     },
14941
14942     // private
14943     findTargetItem : function(e){
14944         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14945         if(t && t.menuItemId){
14946             return this.items.get(t.menuItemId);
14947         }
14948     },
14949
14950     // private
14951     onClick : function(e){
14952         Roo.log("menu.onClick");
14953         var t = this.findTargetItem(e);
14954         if(!t){
14955             return;
14956         }
14957         Roo.log(e);
14958         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14959             if(t == this.activeItem && t.shouldDeactivate(e)){
14960                 this.activeItem.deactivate();
14961                 delete this.activeItem;
14962                 return;
14963             }
14964             if(t.canActivate){
14965                 this.setActiveItem(t, true);
14966             }
14967             return;
14968             
14969             
14970         }
14971         
14972         t.onClick(e);
14973         this.fireEvent("click", this, t, e);
14974     },
14975
14976     // private
14977     setActiveItem : function(item, autoExpand){
14978         if(item != this.activeItem){
14979             if(this.activeItem){
14980                 this.activeItem.deactivate();
14981             }
14982             this.activeItem = item;
14983             item.activate(autoExpand);
14984         }else if(autoExpand){
14985             item.expandMenu();
14986         }
14987     },
14988
14989     // private
14990     tryActivate : function(start, step){
14991         var items = this.items;
14992         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14993             var item = items.get(i);
14994             if(!item.disabled && item.canActivate){
14995                 this.setActiveItem(item, false);
14996                 return item;
14997             }
14998         }
14999         return false;
15000     },
15001
15002     // private
15003     onMouseOver : function(e){
15004         var t;
15005         if(t = this.findTargetItem(e)){
15006             if(t.canActivate && !t.disabled){
15007                 this.setActiveItem(t, true);
15008             }
15009         }
15010         this.fireEvent("mouseover", this, e, t);
15011     },
15012
15013     // private
15014     onMouseOut : function(e){
15015         var t;
15016         if(t = this.findTargetItem(e)){
15017             if(t == this.activeItem && t.shouldDeactivate(e)){
15018                 this.activeItem.deactivate();
15019                 delete this.activeItem;
15020             }
15021         }
15022         this.fireEvent("mouseout", this, e, t);
15023     },
15024
15025     /**
15026      * Read-only.  Returns true if the menu is currently displayed, else false.
15027      * @type Boolean
15028      */
15029     isVisible : function(){
15030         return this.el && !this.hidden;
15031     },
15032
15033     /**
15034      * Displays this menu relative to another element
15035      * @param {String/HTMLElement/Roo.Element} element The element to align to
15036      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15037      * the element (defaults to this.defaultAlign)
15038      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15039      */
15040     show : function(el, pos, parentMenu){
15041         this.parentMenu = parentMenu;
15042         if(!this.el){
15043             this.render();
15044         }
15045         this.fireEvent("beforeshow", this);
15046         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15047     },
15048
15049     /**
15050      * Displays this menu at a specific xy position
15051      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15052      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15053      */
15054     showAt : function(xy, parentMenu, /* private: */_e){
15055         this.parentMenu = parentMenu;
15056         if(!this.el){
15057             this.render();
15058         }
15059         if(_e !== false){
15060             this.fireEvent("beforeshow", this);
15061             xy = this.el.adjustForConstraints(xy);
15062         }
15063         this.el.setXY(xy);
15064         this.el.show();
15065         this.hidden = false;
15066         this.focus();
15067         this.fireEvent("show", this);
15068     },
15069
15070     focus : function(){
15071         if(!this.hidden){
15072             this.doFocus.defer(50, this);
15073         }
15074     },
15075
15076     doFocus : function(){
15077         if(!this.hidden){
15078             this.focusEl.focus();
15079         }
15080     },
15081
15082     /**
15083      * Hides this menu and optionally all parent menus
15084      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15085      */
15086     hide : function(deep){
15087         if(this.el && this.isVisible()){
15088             this.fireEvent("beforehide", this);
15089             if(this.activeItem){
15090                 this.activeItem.deactivate();
15091                 this.activeItem = null;
15092             }
15093             this.el.hide();
15094             this.hidden = true;
15095             this.fireEvent("hide", this);
15096         }
15097         if(deep === true && this.parentMenu){
15098             this.parentMenu.hide(true);
15099         }
15100     },
15101
15102     /**
15103      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15104      * Any of the following are valid:
15105      * <ul>
15106      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15107      * <li>An HTMLElement object which will be converted to a menu item</li>
15108      * <li>A menu item config object that will be created as a new menu item</li>
15109      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15110      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15111      * </ul>
15112      * Usage:
15113      * <pre><code>
15114 // Create the menu
15115 var menu = new Roo.menu.Menu();
15116
15117 // Create a menu item to add by reference
15118 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15119
15120 // Add a bunch of items at once using different methods.
15121 // Only the last item added will be returned.
15122 var item = menu.add(
15123     menuItem,                // add existing item by ref
15124     'Dynamic Item',          // new TextItem
15125     '-',                     // new separator
15126     { text: 'Config Item' }  // new item by config
15127 );
15128 </code></pre>
15129      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15130      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15131      */
15132     add : function(){
15133         var a = arguments, l = a.length, item;
15134         for(var i = 0; i < l; i++){
15135             var el = a[i];
15136             if ((typeof(el) == "object") && el.xtype && el.xns) {
15137                 el = Roo.factory(el, Roo.menu);
15138             }
15139             
15140             if(el.render){ // some kind of Item
15141                 item = this.addItem(el);
15142             }else if(typeof el == "string"){ // string
15143                 if(el == "separator" || el == "-"){
15144                     item = this.addSeparator();
15145                 }else{
15146                     item = this.addText(el);
15147                 }
15148             }else if(el.tagName || el.el){ // element
15149                 item = this.addElement(el);
15150             }else if(typeof el == "object"){ // must be menu item config?
15151                 item = this.addMenuItem(el);
15152             }
15153         }
15154         return item;
15155     },
15156
15157     /**
15158      * Returns this menu's underlying {@link Roo.Element} object
15159      * @return {Roo.Element} The element
15160      */
15161     getEl : function(){
15162         if(!this.el){
15163             this.render();
15164         }
15165         return this.el;
15166     },
15167
15168     /**
15169      * Adds a separator bar to the menu
15170      * @return {Roo.menu.Item} The menu item that was added
15171      */
15172     addSeparator : function(){
15173         return this.addItem(new Roo.menu.Separator());
15174     },
15175
15176     /**
15177      * Adds an {@link Roo.Element} object to the menu
15178      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15179      * @return {Roo.menu.Item} The menu item that was added
15180      */
15181     addElement : function(el){
15182         return this.addItem(new Roo.menu.BaseItem(el));
15183     },
15184
15185     /**
15186      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15187      * @param {Roo.menu.Item} item The menu item to add
15188      * @return {Roo.menu.Item} The menu item that was added
15189      */
15190     addItem : function(item){
15191         this.items.add(item);
15192         if(this.ul){
15193             var li = document.createElement("li");
15194             li.className = "x-menu-list-item";
15195             this.ul.dom.appendChild(li);
15196             item.render(li, this);
15197             this.delayAutoWidth();
15198         }
15199         return item;
15200     },
15201
15202     /**
15203      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15204      * @param {Object} config A MenuItem config object
15205      * @return {Roo.menu.Item} The menu item that was added
15206      */
15207     addMenuItem : function(config){
15208         if(!(config instanceof Roo.menu.Item)){
15209             if(typeof config.checked == "boolean"){ // must be check menu item config?
15210                 config = new Roo.menu.CheckItem(config);
15211             }else{
15212                 config = new Roo.menu.Item(config);
15213             }
15214         }
15215         return this.addItem(config);
15216     },
15217
15218     /**
15219      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15220      * @param {String} text The text to display in the menu item
15221      * @return {Roo.menu.Item} The menu item that was added
15222      */
15223     addText : function(text){
15224         return this.addItem(new Roo.menu.TextItem({ text : text }));
15225     },
15226
15227     /**
15228      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15229      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15230      * @param {Roo.menu.Item} item The menu item to add
15231      * @return {Roo.menu.Item} The menu item that was added
15232      */
15233     insert : function(index, item){
15234         this.items.insert(index, item);
15235         if(this.ul){
15236             var li = document.createElement("li");
15237             li.className = "x-menu-list-item";
15238             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15239             item.render(li, this);
15240             this.delayAutoWidth();
15241         }
15242         return item;
15243     },
15244
15245     /**
15246      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15247      * @param {Roo.menu.Item} item The menu item to remove
15248      */
15249     remove : function(item){
15250         this.items.removeKey(item.id);
15251         item.destroy();
15252     },
15253
15254     /**
15255      * Removes and destroys all items in the menu
15256      */
15257     removeAll : function(){
15258         var f;
15259         while(f = this.items.first()){
15260             this.remove(f);
15261         }
15262     }
15263 });
15264
15265 // MenuNav is a private utility class used internally by the Menu
15266 Roo.menu.MenuNav = function(menu){
15267     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15268     this.scope = this.menu = menu;
15269 };
15270
15271 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15272     doRelay : function(e, h){
15273         var k = e.getKey();
15274         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15275             this.menu.tryActivate(0, 1);
15276             return false;
15277         }
15278         return h.call(this.scope || this, e, this.menu);
15279     },
15280
15281     up : function(e, m){
15282         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15283             m.tryActivate(m.items.length-1, -1);
15284         }
15285     },
15286
15287     down : function(e, m){
15288         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15289             m.tryActivate(0, 1);
15290         }
15291     },
15292
15293     right : function(e, m){
15294         if(m.activeItem){
15295             m.activeItem.expandMenu(true);
15296         }
15297     },
15298
15299     left : function(e, m){
15300         m.hide();
15301         if(m.parentMenu && m.parentMenu.activeItem){
15302             m.parentMenu.activeItem.activate();
15303         }
15304     },
15305
15306     enter : function(e, m){
15307         if(m.activeItem){
15308             e.stopPropagation();
15309             m.activeItem.onClick(e);
15310             m.fireEvent("click", this, m.activeItem);
15311             return true;
15312         }
15313     }
15314 });/*
15315  * Based on:
15316  * Ext JS Library 1.1.1
15317  * Copyright(c) 2006-2007, Ext JS, LLC.
15318  *
15319  * Originally Released Under LGPL - original licence link has changed is not relivant.
15320  *
15321  * Fork - LGPL
15322  * <script type="text/javascript">
15323  */
15324  
15325 /**
15326  * @class Roo.menu.MenuMgr
15327  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15328  * @singleton
15329  */
15330 Roo.menu.MenuMgr = function(){
15331    var menus, active, groups = {}, attached = false, lastShow = new Date();
15332
15333    // private - called when first menu is created
15334    function init(){
15335        menus = {};
15336        active = new Roo.util.MixedCollection();
15337        Roo.get(document).addKeyListener(27, function(){
15338            if(active.length > 0){
15339                hideAll();
15340            }
15341        });
15342    }
15343
15344    // private
15345    function hideAll(){
15346        if(active && active.length > 0){
15347            var c = active.clone();
15348            c.each(function(m){
15349                m.hide();
15350            });
15351        }
15352    }
15353
15354    // private
15355    function onHide(m){
15356        active.remove(m);
15357        if(active.length < 1){
15358            Roo.get(document).un("mousedown", onMouseDown);
15359            attached = false;
15360        }
15361    }
15362
15363    // private
15364    function onShow(m){
15365        var last = active.last();
15366        lastShow = new Date();
15367        active.add(m);
15368        if(!attached){
15369            Roo.get(document).on("mousedown", onMouseDown);
15370            attached = true;
15371        }
15372        if(m.parentMenu){
15373           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15374           m.parentMenu.activeChild = m;
15375        }else if(last && last.isVisible()){
15376           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15377        }
15378    }
15379
15380    // private
15381    function onBeforeHide(m){
15382        if(m.activeChild){
15383            m.activeChild.hide();
15384        }
15385        if(m.autoHideTimer){
15386            clearTimeout(m.autoHideTimer);
15387            delete m.autoHideTimer;
15388        }
15389    }
15390
15391    // private
15392    function onBeforeShow(m){
15393        var pm = m.parentMenu;
15394        if(!pm && !m.allowOtherMenus){
15395            hideAll();
15396        }else if(pm && pm.activeChild && active != m){
15397            pm.activeChild.hide();
15398        }
15399    }
15400
15401    // private
15402    function onMouseDown(e){
15403        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15404            hideAll();
15405        }
15406    }
15407
15408    // private
15409    function onBeforeCheck(mi, state){
15410        if(state){
15411            var g = groups[mi.group];
15412            for(var i = 0, l = g.length; i < l; i++){
15413                if(g[i] != mi){
15414                    g[i].setChecked(false);
15415                }
15416            }
15417        }
15418    }
15419
15420    return {
15421
15422        /**
15423         * Hides all menus that are currently visible
15424         */
15425        hideAll : function(){
15426             hideAll();  
15427        },
15428
15429        // private
15430        register : function(menu){
15431            if(!menus){
15432                init();
15433            }
15434            menus[menu.id] = menu;
15435            menu.on("beforehide", onBeforeHide);
15436            menu.on("hide", onHide);
15437            menu.on("beforeshow", onBeforeShow);
15438            menu.on("show", onShow);
15439            var g = menu.group;
15440            if(g && menu.events["checkchange"]){
15441                if(!groups[g]){
15442                    groups[g] = [];
15443                }
15444                groups[g].push(menu);
15445                menu.on("checkchange", onCheck);
15446            }
15447        },
15448
15449         /**
15450          * Returns a {@link Roo.menu.Menu} object
15451          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15452          * be used to generate and return a new Menu instance.
15453          */
15454        get : function(menu){
15455            if(typeof menu == "string"){ // menu id
15456                return menus[menu];
15457            }else if(menu.events){  // menu instance
15458                return menu;
15459            }else if(typeof menu.length == 'number'){ // array of menu items?
15460                return new Roo.menu.Menu({items:menu});
15461            }else{ // otherwise, must be a config
15462                return new Roo.menu.Menu(menu);
15463            }
15464        },
15465
15466        // private
15467        unregister : function(menu){
15468            delete menus[menu.id];
15469            menu.un("beforehide", onBeforeHide);
15470            menu.un("hide", onHide);
15471            menu.un("beforeshow", onBeforeShow);
15472            menu.un("show", onShow);
15473            var g = menu.group;
15474            if(g && menu.events["checkchange"]){
15475                groups[g].remove(menu);
15476                menu.un("checkchange", onCheck);
15477            }
15478        },
15479
15480        // private
15481        registerCheckable : function(menuItem){
15482            var g = menuItem.group;
15483            if(g){
15484                if(!groups[g]){
15485                    groups[g] = [];
15486                }
15487                groups[g].push(menuItem);
15488                menuItem.on("beforecheckchange", onBeforeCheck);
15489            }
15490        },
15491
15492        // private
15493        unregisterCheckable : function(menuItem){
15494            var g = menuItem.group;
15495            if(g){
15496                groups[g].remove(menuItem);
15497                menuItem.un("beforecheckchange", onBeforeCheck);
15498            }
15499        }
15500    };
15501 }();/*
15502  * Based on:
15503  * Ext JS Library 1.1.1
15504  * Copyright(c) 2006-2007, Ext JS, LLC.
15505  *
15506  * Originally Released Under LGPL - original licence link has changed is not relivant.
15507  *
15508  * Fork - LGPL
15509  * <script type="text/javascript">
15510  */
15511  
15512
15513 /**
15514  * @class Roo.menu.BaseItem
15515  * @extends Roo.Component
15516  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15517  * management and base configuration options shared by all menu components.
15518  * @constructor
15519  * Creates a new BaseItem
15520  * @param {Object} config Configuration options
15521  */
15522 Roo.menu.BaseItem = function(config){
15523     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15524
15525     this.addEvents({
15526         /**
15527          * @event click
15528          * Fires when this item is clicked
15529          * @param {Roo.menu.BaseItem} this
15530          * @param {Roo.EventObject} e
15531          */
15532         click: true,
15533         /**
15534          * @event activate
15535          * Fires when this item is activated
15536          * @param {Roo.menu.BaseItem} this
15537          */
15538         activate : true,
15539         /**
15540          * @event deactivate
15541          * Fires when this item is deactivated
15542          * @param {Roo.menu.BaseItem} this
15543          */
15544         deactivate : true
15545     });
15546
15547     if(this.handler){
15548         this.on("click", this.handler, this.scope, true);
15549     }
15550 };
15551
15552 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15553     /**
15554      * @cfg {Function} handler
15555      * A function that will handle the click event of this menu item (defaults to undefined)
15556      */
15557     /**
15558      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15559      */
15560     canActivate : false,
15561     
15562      /**
15563      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15564      */
15565     hidden: false,
15566     
15567     /**
15568      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15569      */
15570     activeClass : "x-menu-item-active",
15571     /**
15572      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15573      */
15574     hideOnClick : true,
15575     /**
15576      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15577      */
15578     hideDelay : 100,
15579
15580     // private
15581     ctype: "Roo.menu.BaseItem",
15582
15583     // private
15584     actionMode : "container",
15585
15586     // private
15587     render : function(container, parentMenu){
15588         this.parentMenu = parentMenu;
15589         Roo.menu.BaseItem.superclass.render.call(this, container);
15590         this.container.menuItemId = this.id;
15591     },
15592
15593     // private
15594     onRender : function(container, position){
15595         this.el = Roo.get(this.el);
15596         container.dom.appendChild(this.el.dom);
15597     },
15598
15599     // private
15600     onClick : function(e){
15601         if(!this.disabled && this.fireEvent("click", this, e) !== false
15602                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15603             this.handleClick(e);
15604         }else{
15605             e.stopEvent();
15606         }
15607     },
15608
15609     // private
15610     activate : function(){
15611         if(this.disabled){
15612             return false;
15613         }
15614         var li = this.container;
15615         li.addClass(this.activeClass);
15616         this.region = li.getRegion().adjust(2, 2, -2, -2);
15617         this.fireEvent("activate", this);
15618         return true;
15619     },
15620
15621     // private
15622     deactivate : function(){
15623         this.container.removeClass(this.activeClass);
15624         this.fireEvent("deactivate", this);
15625     },
15626
15627     // private
15628     shouldDeactivate : function(e){
15629         return !this.region || !this.region.contains(e.getPoint());
15630     },
15631
15632     // private
15633     handleClick : function(e){
15634         if(this.hideOnClick){
15635             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15636         }
15637     },
15638
15639     // private
15640     expandMenu : function(autoActivate){
15641         // do nothing
15642     },
15643
15644     // private
15645     hideMenu : function(){
15646         // do nothing
15647     }
15648 });/*
15649  * Based on:
15650  * Ext JS Library 1.1.1
15651  * Copyright(c) 2006-2007, Ext JS, LLC.
15652  *
15653  * Originally Released Under LGPL - original licence link has changed is not relivant.
15654  *
15655  * Fork - LGPL
15656  * <script type="text/javascript">
15657  */
15658  
15659 /**
15660  * @class Roo.menu.Adapter
15661  * @extends Roo.menu.BaseItem
15662  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15663  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15664  * @constructor
15665  * Creates a new Adapter
15666  * @param {Object} config Configuration options
15667  */
15668 Roo.menu.Adapter = function(component, config){
15669     Roo.menu.Adapter.superclass.constructor.call(this, config);
15670     this.component = component;
15671 };
15672 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15673     // private
15674     canActivate : true,
15675
15676     // private
15677     onRender : function(container, position){
15678         this.component.render(container);
15679         this.el = this.component.getEl();
15680     },
15681
15682     // private
15683     activate : function(){
15684         if(this.disabled){
15685             return false;
15686         }
15687         this.component.focus();
15688         this.fireEvent("activate", this);
15689         return true;
15690     },
15691
15692     // private
15693     deactivate : function(){
15694         this.fireEvent("deactivate", this);
15695     },
15696
15697     // private
15698     disable : function(){
15699         this.component.disable();
15700         Roo.menu.Adapter.superclass.disable.call(this);
15701     },
15702
15703     // private
15704     enable : function(){
15705         this.component.enable();
15706         Roo.menu.Adapter.superclass.enable.call(this);
15707     }
15708 });/*
15709  * Based on:
15710  * Ext JS Library 1.1.1
15711  * Copyright(c) 2006-2007, Ext JS, LLC.
15712  *
15713  * Originally Released Under LGPL - original licence link has changed is not relivant.
15714  *
15715  * Fork - LGPL
15716  * <script type="text/javascript">
15717  */
15718
15719 /**
15720  * @class Roo.menu.TextItem
15721  * @extends Roo.menu.BaseItem
15722  * Adds a static text string to a menu, usually used as either a heading or group separator.
15723  * Note: old style constructor with text is still supported.
15724  * 
15725  * @constructor
15726  * Creates a new TextItem
15727  * @param {Object} cfg Configuration
15728  */
15729 Roo.menu.TextItem = function(cfg){
15730     if (typeof(cfg) == 'string') {
15731         this.text = cfg;
15732     } else {
15733         Roo.apply(this,cfg);
15734     }
15735     
15736     Roo.menu.TextItem.superclass.constructor.call(this);
15737 };
15738
15739 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15740     /**
15741      * @cfg {Boolean} text Text to show on item.
15742      */
15743     text : '',
15744     
15745     /**
15746      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15747      */
15748     hideOnClick : false,
15749     /**
15750      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15751      */
15752     itemCls : "x-menu-text",
15753
15754     // private
15755     onRender : function(){
15756         var s = document.createElement("span");
15757         s.className = this.itemCls;
15758         s.innerHTML = this.text;
15759         this.el = s;
15760         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15761     }
15762 });/*
15763  * Based on:
15764  * Ext JS Library 1.1.1
15765  * Copyright(c) 2006-2007, Ext JS, LLC.
15766  *
15767  * Originally Released Under LGPL - original licence link has changed is not relivant.
15768  *
15769  * Fork - LGPL
15770  * <script type="text/javascript">
15771  */
15772
15773 /**
15774  * @class Roo.menu.Separator
15775  * @extends Roo.menu.BaseItem
15776  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15777  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15778  * @constructor
15779  * @param {Object} config Configuration options
15780  */
15781 Roo.menu.Separator = function(config){
15782     Roo.menu.Separator.superclass.constructor.call(this, config);
15783 };
15784
15785 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15786     /**
15787      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15788      */
15789     itemCls : "x-menu-sep",
15790     /**
15791      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15792      */
15793     hideOnClick : false,
15794
15795     // private
15796     onRender : function(li){
15797         var s = document.createElement("span");
15798         s.className = this.itemCls;
15799         s.innerHTML = "&#160;";
15800         this.el = s;
15801         li.addClass("x-menu-sep-li");
15802         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15803     }
15804 });/*
15805  * Based on:
15806  * Ext JS Library 1.1.1
15807  * Copyright(c) 2006-2007, Ext JS, LLC.
15808  *
15809  * Originally Released Under LGPL - original licence link has changed is not relivant.
15810  *
15811  * Fork - LGPL
15812  * <script type="text/javascript">
15813  */
15814 /**
15815  * @class Roo.menu.Item
15816  * @extends Roo.menu.BaseItem
15817  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15818  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15819  * activation and click handling.
15820  * @constructor
15821  * Creates a new Item
15822  * @param {Object} config Configuration options
15823  */
15824 Roo.menu.Item = function(config){
15825     Roo.menu.Item.superclass.constructor.call(this, config);
15826     if(this.menu){
15827         this.menu = Roo.menu.MenuMgr.get(this.menu);
15828     }
15829 };
15830 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15831     
15832     /**
15833      * @cfg {String} text
15834      * The text to show on the menu item.
15835      */
15836     text: '',
15837      /**
15838      * @cfg {String} HTML to render in menu
15839      * The text to show on the menu item (HTML version).
15840      */
15841     html: '',
15842     /**
15843      * @cfg {String} icon
15844      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15845      */
15846     icon: undefined,
15847     /**
15848      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15849      */
15850     itemCls : "x-menu-item",
15851     /**
15852      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15853      */
15854     canActivate : true,
15855     /**
15856      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15857      */
15858     showDelay: 200,
15859     // doc'd in BaseItem
15860     hideDelay: 200,
15861
15862     // private
15863     ctype: "Roo.menu.Item",
15864     
15865     // private
15866     onRender : function(container, position){
15867         var el = document.createElement("a");
15868         el.hideFocus = true;
15869         el.unselectable = "on";
15870         el.href = this.href || "#";
15871         if(this.hrefTarget){
15872             el.target = this.hrefTarget;
15873         }
15874         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15875         
15876         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15877         
15878         el.innerHTML = String.format(
15879                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15880                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15881         this.el = el;
15882         Roo.menu.Item.superclass.onRender.call(this, container, position);
15883     },
15884
15885     /**
15886      * Sets the text to display in this menu item
15887      * @param {String} text The text to display
15888      * @param {Boolean} isHTML true to indicate text is pure html.
15889      */
15890     setText : function(text, isHTML){
15891         if (isHTML) {
15892             this.html = text;
15893         } else {
15894             this.text = text;
15895             this.html = '';
15896         }
15897         if(this.rendered){
15898             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15899      
15900             this.el.update(String.format(
15901                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15902                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15903             this.parentMenu.autoWidth();
15904         }
15905     },
15906
15907     // private
15908     handleClick : function(e){
15909         if(!this.href){ // if no link defined, stop the event automatically
15910             e.stopEvent();
15911         }
15912         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15913     },
15914
15915     // private
15916     activate : function(autoExpand){
15917         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15918             this.focus();
15919             if(autoExpand){
15920                 this.expandMenu();
15921             }
15922         }
15923         return true;
15924     },
15925
15926     // private
15927     shouldDeactivate : function(e){
15928         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15929             if(this.menu && this.menu.isVisible()){
15930                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15931             }
15932             return true;
15933         }
15934         return false;
15935     },
15936
15937     // private
15938     deactivate : function(){
15939         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15940         this.hideMenu();
15941     },
15942
15943     // private
15944     expandMenu : function(autoActivate){
15945         if(!this.disabled && this.menu){
15946             clearTimeout(this.hideTimer);
15947             delete this.hideTimer;
15948             if(!this.menu.isVisible() && !this.showTimer){
15949                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15950             }else if (this.menu.isVisible() && autoActivate){
15951                 this.menu.tryActivate(0, 1);
15952             }
15953         }
15954     },
15955
15956     // private
15957     deferExpand : function(autoActivate){
15958         delete this.showTimer;
15959         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15960         if(autoActivate){
15961             this.menu.tryActivate(0, 1);
15962         }
15963     },
15964
15965     // private
15966     hideMenu : function(){
15967         clearTimeout(this.showTimer);
15968         delete this.showTimer;
15969         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15970             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15971         }
15972     },
15973
15974     // private
15975     deferHide : function(){
15976         delete this.hideTimer;
15977         this.menu.hide();
15978     }
15979 });/*
15980  * Based on:
15981  * Ext JS Library 1.1.1
15982  * Copyright(c) 2006-2007, Ext JS, LLC.
15983  *
15984  * Originally Released Under LGPL - original licence link has changed is not relivant.
15985  *
15986  * Fork - LGPL
15987  * <script type="text/javascript">
15988  */
15989  
15990 /**
15991  * @class Roo.menu.CheckItem
15992  * @extends Roo.menu.Item
15993  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15994  * @constructor
15995  * Creates a new CheckItem
15996  * @param {Object} config Configuration options
15997  */
15998 Roo.menu.CheckItem = function(config){
15999     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16000     this.addEvents({
16001         /**
16002          * @event beforecheckchange
16003          * Fires before the checked value is set, providing an opportunity to cancel if needed
16004          * @param {Roo.menu.CheckItem} this
16005          * @param {Boolean} checked The new checked value that will be set
16006          */
16007         "beforecheckchange" : true,
16008         /**
16009          * @event checkchange
16010          * Fires after the checked value has been set
16011          * @param {Roo.menu.CheckItem} this
16012          * @param {Boolean} checked The checked value that was set
16013          */
16014         "checkchange" : true
16015     });
16016     if(this.checkHandler){
16017         this.on('checkchange', this.checkHandler, this.scope);
16018     }
16019 };
16020 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16021     /**
16022      * @cfg {String} group
16023      * All check items with the same group name will automatically be grouped into a single-select
16024      * radio button group (defaults to '')
16025      */
16026     /**
16027      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16028      */
16029     itemCls : "x-menu-item x-menu-check-item",
16030     /**
16031      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16032      */
16033     groupClass : "x-menu-group-item",
16034
16035     /**
16036      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16037      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16038      * initialized with checked = true will be rendered as checked.
16039      */
16040     checked: false,
16041
16042     // private
16043     ctype: "Roo.menu.CheckItem",
16044
16045     // private
16046     onRender : function(c){
16047         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16048         if(this.group){
16049             this.el.addClass(this.groupClass);
16050         }
16051         Roo.menu.MenuMgr.registerCheckable(this);
16052         if(this.checked){
16053             this.checked = false;
16054             this.setChecked(true, true);
16055         }
16056     },
16057
16058     // private
16059     destroy : function(){
16060         if(this.rendered){
16061             Roo.menu.MenuMgr.unregisterCheckable(this);
16062         }
16063         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16064     },
16065
16066     /**
16067      * Set the checked state of this item
16068      * @param {Boolean} checked The new checked value
16069      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16070      */
16071     setChecked : function(state, suppressEvent){
16072         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16073             if(this.container){
16074                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16075             }
16076             this.checked = state;
16077             if(suppressEvent !== true){
16078                 this.fireEvent("checkchange", this, state);
16079             }
16080         }
16081     },
16082
16083     // private
16084     handleClick : function(e){
16085        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16086            this.setChecked(!this.checked);
16087        }
16088        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16089     }
16090 });/*
16091  * Based on:
16092  * Ext JS Library 1.1.1
16093  * Copyright(c) 2006-2007, Ext JS, LLC.
16094  *
16095  * Originally Released Under LGPL - original licence link has changed is not relivant.
16096  *
16097  * Fork - LGPL
16098  * <script type="text/javascript">
16099  */
16100  
16101 /**
16102  * @class Roo.menu.DateItem
16103  * @extends Roo.menu.Adapter
16104  * A menu item that wraps the {@link Roo.DatPicker} component.
16105  * @constructor
16106  * Creates a new DateItem
16107  * @param {Object} config Configuration options
16108  */
16109 Roo.menu.DateItem = function(config){
16110     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16111     /** The Roo.DatePicker object @type Roo.DatePicker */
16112     this.picker = this.component;
16113     this.addEvents({select: true});
16114     
16115     this.picker.on("render", function(picker){
16116         picker.getEl().swallowEvent("click");
16117         picker.container.addClass("x-menu-date-item");
16118     });
16119
16120     this.picker.on("select", this.onSelect, this);
16121 };
16122
16123 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16124     // private
16125     onSelect : function(picker, date){
16126         this.fireEvent("select", this, date, picker);
16127         Roo.menu.DateItem.superclass.handleClick.call(this);
16128     }
16129 });/*
16130  * Based on:
16131  * Ext JS Library 1.1.1
16132  * Copyright(c) 2006-2007, Ext JS, LLC.
16133  *
16134  * Originally Released Under LGPL - original licence link has changed is not relivant.
16135  *
16136  * Fork - LGPL
16137  * <script type="text/javascript">
16138  */
16139  
16140 /**
16141  * @class Roo.menu.ColorItem
16142  * @extends Roo.menu.Adapter
16143  * A menu item that wraps the {@link Roo.ColorPalette} component.
16144  * @constructor
16145  * Creates a new ColorItem
16146  * @param {Object} config Configuration options
16147  */
16148 Roo.menu.ColorItem = function(config){
16149     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16150     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16151     this.palette = this.component;
16152     this.relayEvents(this.palette, ["select"]);
16153     if(this.selectHandler){
16154         this.on('select', this.selectHandler, this.scope);
16155     }
16156 };
16157 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16158  * Based on:
16159  * Ext JS Library 1.1.1
16160  * Copyright(c) 2006-2007, Ext JS, LLC.
16161  *
16162  * Originally Released Under LGPL - original licence link has changed is not relivant.
16163  *
16164  * Fork - LGPL
16165  * <script type="text/javascript">
16166  */
16167  
16168
16169 /**
16170  * @class Roo.menu.DateMenu
16171  * @extends Roo.menu.Menu
16172  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16173  * @constructor
16174  * Creates a new DateMenu
16175  * @param {Object} config Configuration options
16176  */
16177 Roo.menu.DateMenu = function(config){
16178     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16179     this.plain = true;
16180     var di = new Roo.menu.DateItem(config);
16181     this.add(di);
16182     /**
16183      * The {@link Roo.DatePicker} instance for this DateMenu
16184      * @type DatePicker
16185      */
16186     this.picker = di.picker;
16187     /**
16188      * @event select
16189      * @param {DatePicker} picker
16190      * @param {Date} date
16191      */
16192     this.relayEvents(di, ["select"]);
16193     this.on('beforeshow', function(){
16194         if(this.picker){
16195             this.picker.hideMonthPicker(false);
16196         }
16197     }, this);
16198 };
16199 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16200     cls:'x-date-menu'
16201 });/*
16202  * Based on:
16203  * Ext JS Library 1.1.1
16204  * Copyright(c) 2006-2007, Ext JS, LLC.
16205  *
16206  * Originally Released Under LGPL - original licence link has changed is not relivant.
16207  *
16208  * Fork - LGPL
16209  * <script type="text/javascript">
16210  */
16211  
16212
16213 /**
16214  * @class Roo.menu.ColorMenu
16215  * @extends Roo.menu.Menu
16216  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16217  * @constructor
16218  * Creates a new ColorMenu
16219  * @param {Object} config Configuration options
16220  */
16221 Roo.menu.ColorMenu = function(config){
16222     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16223     this.plain = true;
16224     var ci = new Roo.menu.ColorItem(config);
16225     this.add(ci);
16226     /**
16227      * The {@link Roo.ColorPalette} instance for this ColorMenu
16228      * @type ColorPalette
16229      */
16230     this.palette = ci.palette;
16231     /**
16232      * @event select
16233      * @param {ColorPalette} palette
16234      * @param {String} color
16235      */
16236     this.relayEvents(ci, ["select"]);
16237 };
16238 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16239  * Based on:
16240  * Ext JS Library 1.1.1
16241  * Copyright(c) 2006-2007, Ext JS, LLC.
16242  *
16243  * Originally Released Under LGPL - original licence link has changed is not relivant.
16244  *
16245  * Fork - LGPL
16246  * <script type="text/javascript">
16247  */
16248  
16249 /**
16250  * @class Roo.form.Field
16251  * @extends Roo.BoxComponent
16252  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16253  * @constructor
16254  * Creates a new Field
16255  * @param {Object} config Configuration options
16256  */
16257 Roo.form.Field = function(config){
16258     Roo.form.Field.superclass.constructor.call(this, config);
16259 };
16260
16261 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16262     /**
16263      * @cfg {String} fieldLabel Label to use when rendering a form.
16264      */
16265        /**
16266      * @cfg {String} qtip Mouse over tip
16267      */
16268      
16269     /**
16270      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16271      */
16272     invalidClass : "x-form-invalid",
16273     /**
16274      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
16275      */
16276     invalidText : "The value in this field is invalid",
16277     /**
16278      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16279      */
16280     focusClass : "x-form-focus",
16281     /**
16282      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16283       automatic validation (defaults to "keyup").
16284      */
16285     validationEvent : "keyup",
16286     /**
16287      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16288      */
16289     validateOnBlur : true,
16290     /**
16291      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16292      */
16293     validationDelay : 250,
16294     /**
16295      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16296      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16297      */
16298     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16299     /**
16300      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16301      */
16302     fieldClass : "x-form-field",
16303     /**
16304      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16305      *<pre>
16306 Value         Description
16307 -----------   ----------------------------------------------------------------------
16308 qtip          Display a quick tip when the user hovers over the field
16309 title         Display a default browser title attribute popup
16310 under         Add a block div beneath the field containing the error text
16311 side          Add an error icon to the right of the field with a popup on hover
16312 [element id]  Add the error text directly to the innerHTML of the specified element
16313 </pre>
16314      */
16315     msgTarget : 'qtip',
16316     /**
16317      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16318      */
16319     msgFx : 'normal',
16320
16321     /**
16322      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
16323      */
16324     readOnly : false,
16325
16326     /**
16327      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16328      */
16329     disabled : false,
16330
16331     /**
16332      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16333      */
16334     inputType : undefined,
16335     
16336     /**
16337      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
16338          */
16339         tabIndex : undefined,
16340         
16341     // private
16342     isFormField : true,
16343
16344     // private
16345     hasFocus : false,
16346     /**
16347      * @property {Roo.Element} fieldEl
16348      * Element Containing the rendered Field (with label etc.)
16349      */
16350     /**
16351      * @cfg {Mixed} value A value to initialize this field with.
16352      */
16353     value : undefined,
16354
16355     /**
16356      * @cfg {String} name The field's HTML name attribute.
16357      */
16358     /**
16359      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16360      */
16361     // private
16362     loadedValue : false,
16363      
16364      
16365         // private ??
16366         initComponent : function(){
16367         Roo.form.Field.superclass.initComponent.call(this);
16368         this.addEvents({
16369             /**
16370              * @event focus
16371              * Fires when this field receives input focus.
16372              * @param {Roo.form.Field} this
16373              */
16374             focus : true,
16375             /**
16376              * @event blur
16377              * Fires when this field loses input focus.
16378              * @param {Roo.form.Field} this
16379              */
16380             blur : true,
16381             /**
16382              * @event specialkey
16383              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16384              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16385              * @param {Roo.form.Field} this
16386              * @param {Roo.EventObject} e The event object
16387              */
16388             specialkey : true,
16389             /**
16390              * @event change
16391              * Fires just before the field blurs if the field value has changed.
16392              * @param {Roo.form.Field} this
16393              * @param {Mixed} newValue The new value
16394              * @param {Mixed} oldValue The original value
16395              */
16396             change : true,
16397             /**
16398              * @event invalid
16399              * Fires after the field has been marked as invalid.
16400              * @param {Roo.form.Field} this
16401              * @param {String} msg The validation message
16402              */
16403             invalid : true,
16404             /**
16405              * @event valid
16406              * Fires after the field has been validated with no errors.
16407              * @param {Roo.form.Field} this
16408              */
16409             valid : true,
16410              /**
16411              * @event keyup
16412              * Fires after the key up
16413              * @param {Roo.form.Field} this
16414              * @param {Roo.EventObject}  e The event Object
16415              */
16416             keyup : true
16417         });
16418     },
16419
16420     /**
16421      * Returns the name attribute of the field if available
16422      * @return {String} name The field name
16423      */
16424     getName: function(){
16425          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16426     },
16427
16428     // private
16429     onRender : function(ct, position){
16430         Roo.form.Field.superclass.onRender.call(this, ct, position);
16431         if(!this.el){
16432             var cfg = this.getAutoCreate();
16433             if(!cfg.name){
16434                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16435             }
16436             if (!cfg.name.length) {
16437                 delete cfg.name;
16438             }
16439             if(this.inputType){
16440                 cfg.type = this.inputType;
16441             }
16442             this.el = ct.createChild(cfg, position);
16443         }
16444         var type = this.el.dom.type;
16445         if(type){
16446             if(type == 'password'){
16447                 type = 'text';
16448             }
16449             this.el.addClass('x-form-'+type);
16450         }
16451         if(this.readOnly){
16452             this.el.dom.readOnly = true;
16453         }
16454         if(this.tabIndex !== undefined){
16455             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16456         }
16457
16458         this.el.addClass([this.fieldClass, this.cls]);
16459         this.initValue();
16460     },
16461
16462     /**
16463      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16464      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16465      * @return {Roo.form.Field} this
16466      */
16467     applyTo : function(target){
16468         this.allowDomMove = false;
16469         this.el = Roo.get(target);
16470         this.render(this.el.dom.parentNode);
16471         return this;
16472     },
16473
16474     // private
16475     initValue : function(){
16476         if(this.value !== undefined){
16477             this.setValue(this.value);
16478         }else if(this.el.dom.value.length > 0){
16479             this.setValue(this.el.dom.value);
16480         }
16481     },
16482
16483     /**
16484      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16485      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16486      */
16487     isDirty : function() {
16488         if(this.disabled) {
16489             return false;
16490         }
16491         return String(this.getValue()) !== String(this.originalValue);
16492     },
16493
16494     /**
16495      * stores the current value in loadedValue
16496      */
16497     resetHasChanged : function()
16498     {
16499         this.loadedValue = String(this.getValue());
16500     },
16501     /**
16502      * checks the current value against the 'loaded' value.
16503      * Note - will return false if 'resetHasChanged' has not been called first.
16504      */
16505     hasChanged : function()
16506     {
16507         if(this.disabled || this.readOnly) {
16508             return false;
16509         }
16510         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16511     },
16512     
16513     
16514     
16515     // private
16516     afterRender : function(){
16517         Roo.form.Field.superclass.afterRender.call(this);
16518         this.initEvents();
16519     },
16520
16521     // private
16522     fireKey : function(e){
16523         //Roo.log('field ' + e.getKey());
16524         if(e.isNavKeyPress()){
16525             this.fireEvent("specialkey", this, e);
16526         }
16527     },
16528
16529     /**
16530      * Resets the current field value to the originally loaded value and clears any validation messages
16531      */
16532     reset : function(){
16533         this.setValue(this.resetValue);
16534         this.clearInvalid();
16535     },
16536
16537     // private
16538     initEvents : function(){
16539         // safari killled keypress - so keydown is now used..
16540         this.el.on("keydown" , this.fireKey,  this);
16541         this.el.on("focus", this.onFocus,  this);
16542         this.el.on("blur", this.onBlur,  this);
16543         this.el.relayEvent('keyup', this);
16544
16545         // reference to original value for reset
16546         this.originalValue = this.getValue();
16547         this.resetValue =  this.getValue();
16548     },
16549
16550     // private
16551     onFocus : function(){
16552         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16553             this.el.addClass(this.focusClass);
16554         }
16555         if(!this.hasFocus){
16556             this.hasFocus = true;
16557             this.startValue = this.getValue();
16558             this.fireEvent("focus", this);
16559         }
16560     },
16561
16562     beforeBlur : Roo.emptyFn,
16563
16564     // private
16565     onBlur : function(){
16566         this.beforeBlur();
16567         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16568             this.el.removeClass(this.focusClass);
16569         }
16570         this.hasFocus = false;
16571         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16572             this.validate();
16573         }
16574         var v = this.getValue();
16575         if(String(v) !== String(this.startValue)){
16576             this.fireEvent('change', this, v, this.startValue);
16577         }
16578         this.fireEvent("blur", this);
16579     },
16580
16581     /**
16582      * Returns whether or not the field value is currently valid
16583      * @param {Boolean} preventMark True to disable marking the field invalid
16584      * @return {Boolean} True if the value is valid, else false
16585      */
16586     isValid : function(preventMark){
16587         if(this.disabled){
16588             return true;
16589         }
16590         var restore = this.preventMark;
16591         this.preventMark = preventMark === true;
16592         var v = this.validateValue(this.processValue(this.getRawValue()));
16593         this.preventMark = restore;
16594         return v;
16595     },
16596
16597     /**
16598      * Validates the field value
16599      * @return {Boolean} True if the value is valid, else false
16600      */
16601     validate : function(){
16602         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16603             this.clearInvalid();
16604             return true;
16605         }
16606         return false;
16607     },
16608
16609     processValue : function(value){
16610         return value;
16611     },
16612
16613     // private
16614     // Subclasses should provide the validation implementation by overriding this
16615     validateValue : function(value){
16616         return true;
16617     },
16618
16619     /**
16620      * Mark this field as invalid
16621      * @param {String} msg The validation message
16622      */
16623     markInvalid : function(msg){
16624         if(!this.rendered || this.preventMark){ // not rendered
16625             return;
16626         }
16627         
16628         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16629         
16630         obj.el.addClass(this.invalidClass);
16631         msg = msg || this.invalidText;
16632         switch(this.msgTarget){
16633             case 'qtip':
16634                 obj.el.dom.qtip = msg;
16635                 obj.el.dom.qclass = 'x-form-invalid-tip';
16636                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16637                     Roo.QuickTips.enable();
16638                 }
16639                 break;
16640             case 'title':
16641                 this.el.dom.title = msg;
16642                 break;
16643             case 'under':
16644                 if(!this.errorEl){
16645                     var elp = this.el.findParent('.x-form-element', 5, true);
16646                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16647                     this.errorEl.setWidth(elp.getWidth(true)-20);
16648                 }
16649                 this.errorEl.update(msg);
16650                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16651                 break;
16652             case 'side':
16653                 if(!this.errorIcon){
16654                     var elp = this.el.findParent('.x-form-element', 5, true);
16655                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16656                 }
16657                 this.alignErrorIcon();
16658                 this.errorIcon.dom.qtip = msg;
16659                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16660                 this.errorIcon.show();
16661                 this.on('resize', this.alignErrorIcon, this);
16662                 break;
16663             default:
16664                 var t = Roo.getDom(this.msgTarget);
16665                 t.innerHTML = msg;
16666                 t.style.display = this.msgDisplay;
16667                 break;
16668         }
16669         this.fireEvent('invalid', this, msg);
16670     },
16671
16672     // private
16673     alignErrorIcon : function(){
16674         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16675     },
16676
16677     /**
16678      * Clear any invalid styles/messages for this field
16679      */
16680     clearInvalid : function(){
16681         if(!this.rendered || this.preventMark){ // not rendered
16682             return;
16683         }
16684         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16685         
16686         obj.el.removeClass(this.invalidClass);
16687         switch(this.msgTarget){
16688             case 'qtip':
16689                 obj.el.dom.qtip = '';
16690                 break;
16691             case 'title':
16692                 this.el.dom.title = '';
16693                 break;
16694             case 'under':
16695                 if(this.errorEl){
16696                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16697                 }
16698                 break;
16699             case 'side':
16700                 if(this.errorIcon){
16701                     this.errorIcon.dom.qtip = '';
16702                     this.errorIcon.hide();
16703                     this.un('resize', this.alignErrorIcon, this);
16704                 }
16705                 break;
16706             default:
16707                 var t = Roo.getDom(this.msgTarget);
16708                 t.innerHTML = '';
16709                 t.style.display = 'none';
16710                 break;
16711         }
16712         this.fireEvent('valid', this);
16713     },
16714
16715     /**
16716      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16717      * @return {Mixed} value The field value
16718      */
16719     getRawValue : function(){
16720         var v = this.el.getValue();
16721         
16722         return v;
16723     },
16724
16725     /**
16726      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16727      * @return {Mixed} value The field value
16728      */
16729     getValue : function(){
16730         var v = this.el.getValue();
16731          
16732         return v;
16733     },
16734
16735     /**
16736      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16737      * @param {Mixed} value The value to set
16738      */
16739     setRawValue : function(v){
16740         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16741     },
16742
16743     /**
16744      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16745      * @param {Mixed} value The value to set
16746      */
16747     setValue : function(v){
16748         this.value = v;
16749         if(this.rendered){
16750             this.el.dom.value = (v === null || v === undefined ? '' : v);
16751              this.validate();
16752         }
16753     },
16754
16755     adjustSize : function(w, h){
16756         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16757         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16758         return s;
16759     },
16760
16761     adjustWidth : function(tag, w){
16762         tag = tag.toLowerCase();
16763         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16764             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16765                 if(tag == 'input'){
16766                     return w + 2;
16767                 }
16768                 if(tag == 'textarea'){
16769                     return w-2;
16770                 }
16771             }else if(Roo.isOpera){
16772                 if(tag == 'input'){
16773                     return w + 2;
16774                 }
16775                 if(tag == 'textarea'){
16776                     return w-2;
16777                 }
16778             }
16779         }
16780         return w;
16781     }
16782 });
16783
16784
16785 // anything other than normal should be considered experimental
16786 Roo.form.Field.msgFx = {
16787     normal : {
16788         show: function(msgEl, f){
16789             msgEl.setDisplayed('block');
16790         },
16791
16792         hide : function(msgEl, f){
16793             msgEl.setDisplayed(false).update('');
16794         }
16795     },
16796
16797     slide : {
16798         show: function(msgEl, f){
16799             msgEl.slideIn('t', {stopFx:true});
16800         },
16801
16802         hide : function(msgEl, f){
16803             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16804         }
16805     },
16806
16807     slideRight : {
16808         show: function(msgEl, f){
16809             msgEl.fixDisplay();
16810             msgEl.alignTo(f.el, 'tl-tr');
16811             msgEl.slideIn('l', {stopFx:true});
16812         },
16813
16814         hide : function(msgEl, f){
16815             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16816         }
16817     }
16818 };/*
16819  * Based on:
16820  * Ext JS Library 1.1.1
16821  * Copyright(c) 2006-2007, Ext JS, LLC.
16822  *
16823  * Originally Released Under LGPL - original licence link has changed is not relivant.
16824  *
16825  * Fork - LGPL
16826  * <script type="text/javascript">
16827  */
16828  
16829
16830 /**
16831  * @class Roo.form.TextField
16832  * @extends Roo.form.Field
16833  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16834  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16835  * @constructor
16836  * Creates a new TextField
16837  * @param {Object} config Configuration options
16838  */
16839 Roo.form.TextField = function(config){
16840     Roo.form.TextField.superclass.constructor.call(this, config);
16841     this.addEvents({
16842         /**
16843          * @event autosize
16844          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16845          * according to the default logic, but this event provides a hook for the developer to apply additional
16846          * logic at runtime to resize the field if needed.
16847              * @param {Roo.form.Field} this This text field
16848              * @param {Number} width The new field width
16849              */
16850         autosize : true
16851     });
16852 };
16853
16854 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16855     /**
16856      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16857      */
16858     grow : false,
16859     /**
16860      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16861      */
16862     growMin : 30,
16863     /**
16864      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16865      */
16866     growMax : 800,
16867     /**
16868      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16869      */
16870     vtype : null,
16871     /**
16872      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16873      */
16874     maskRe : null,
16875     /**
16876      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16877      */
16878     disableKeyFilter : false,
16879     /**
16880      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16881      */
16882     allowBlank : true,
16883     /**
16884      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16885      */
16886     minLength : 0,
16887     /**
16888      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16889      */
16890     maxLength : Number.MAX_VALUE,
16891     /**
16892      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16893      */
16894     minLengthText : "The minimum length for this field is {0}",
16895     /**
16896      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16897      */
16898     maxLengthText : "The maximum length for this field is {0}",
16899     /**
16900      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16901      */
16902     selectOnFocus : false,
16903     /**
16904      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16905      */
16906     blankText : "This field is required",
16907     /**
16908      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16909      * If available, this function will be called only after the basic validators all return true, and will be passed the
16910      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16911      */
16912     validator : null,
16913     /**
16914      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16915      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16916      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16917      */
16918     regex : null,
16919     /**
16920      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16921      */
16922     regexText : "",
16923     /**
16924      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16925      */
16926     emptyText : null,
16927    
16928
16929     // private
16930     initEvents : function()
16931     {
16932         if (this.emptyText) {
16933             this.el.attr('placeholder', this.emptyText);
16934         }
16935         
16936         Roo.form.TextField.superclass.initEvents.call(this);
16937         if(this.validationEvent == 'keyup'){
16938             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16939             this.el.on('keyup', this.filterValidation, this);
16940         }
16941         else if(this.validationEvent !== false){
16942             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16943         }
16944         
16945         if(this.selectOnFocus){
16946             this.on("focus", this.preFocus, this);
16947             
16948         }
16949         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16950             this.el.on("keypress", this.filterKeys, this);
16951         }
16952         if(this.grow){
16953             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16954             this.el.on("click", this.autoSize,  this);
16955         }
16956         if(this.el.is('input[type=password]') && Roo.isSafari){
16957             this.el.on('keydown', this.SafariOnKeyDown, this);
16958         }
16959     },
16960
16961     processValue : function(value){
16962         if(this.stripCharsRe){
16963             var newValue = value.replace(this.stripCharsRe, '');
16964             if(newValue !== value){
16965                 this.setRawValue(newValue);
16966                 return newValue;
16967             }
16968         }
16969         return value;
16970     },
16971
16972     filterValidation : function(e){
16973         if(!e.isNavKeyPress()){
16974             this.validationTask.delay(this.validationDelay);
16975         }
16976     },
16977
16978     // private
16979     onKeyUp : function(e){
16980         if(!e.isNavKeyPress()){
16981             this.autoSize();
16982         }
16983     },
16984
16985     /**
16986      * Resets the current field value to the originally-loaded value and clears any validation messages.
16987      *  
16988      */
16989     reset : function(){
16990         Roo.form.TextField.superclass.reset.call(this);
16991        
16992     },
16993
16994     
16995     // private
16996     preFocus : function(){
16997         
16998         if(this.selectOnFocus){
16999             this.el.dom.select();
17000         }
17001     },
17002
17003     
17004     // private
17005     filterKeys : function(e){
17006         var k = e.getKey();
17007         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17008             return;
17009         }
17010         var c = e.getCharCode(), cc = String.fromCharCode(c);
17011         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17012             return;
17013         }
17014         if(!this.maskRe.test(cc)){
17015             e.stopEvent();
17016         }
17017     },
17018
17019     setValue : function(v){
17020         
17021         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17022         
17023         this.autoSize();
17024     },
17025
17026     /**
17027      * Validates a value according to the field's validation rules and marks the field as invalid
17028      * if the validation fails
17029      * @param {Mixed} value The value to validate
17030      * @return {Boolean} True if the value is valid, else false
17031      */
17032     validateValue : function(value){
17033         if(value.length < 1)  { // if it's blank
17034              if(this.allowBlank){
17035                 this.clearInvalid();
17036                 return true;
17037              }else{
17038                 this.markInvalid(this.blankText);
17039                 return false;
17040              }
17041         }
17042         if(value.length < this.minLength){
17043             this.markInvalid(String.format(this.minLengthText, this.minLength));
17044             return false;
17045         }
17046         if(value.length > this.maxLength){
17047             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17048             return false;
17049         }
17050         if(this.vtype){
17051             var vt = Roo.form.VTypes;
17052             if(!vt[this.vtype](value, this)){
17053                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17054                 return false;
17055             }
17056         }
17057         if(typeof this.validator == "function"){
17058             var msg = this.validator(value);
17059             if(msg !== true){
17060                 this.markInvalid(msg);
17061                 return false;
17062             }
17063         }
17064         if(this.regex && !this.regex.test(value)){
17065             this.markInvalid(this.regexText);
17066             return false;
17067         }
17068         return true;
17069     },
17070
17071     /**
17072      * Selects text in this field
17073      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17074      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17075      */
17076     selectText : function(start, end){
17077         var v = this.getRawValue();
17078         if(v.length > 0){
17079             start = start === undefined ? 0 : start;
17080             end = end === undefined ? v.length : end;
17081             var d = this.el.dom;
17082             if(d.setSelectionRange){
17083                 d.setSelectionRange(start, end);
17084             }else if(d.createTextRange){
17085                 var range = d.createTextRange();
17086                 range.moveStart("character", start);
17087                 range.moveEnd("character", v.length-end);
17088                 range.select();
17089             }
17090         }
17091     },
17092
17093     /**
17094      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17095      * This only takes effect if grow = true, and fires the autosize event.
17096      */
17097     autoSize : function(){
17098         if(!this.grow || !this.rendered){
17099             return;
17100         }
17101         if(!this.metrics){
17102             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17103         }
17104         var el = this.el;
17105         var v = el.dom.value;
17106         var d = document.createElement('div');
17107         d.appendChild(document.createTextNode(v));
17108         v = d.innerHTML;
17109         d = null;
17110         v += "&#160;";
17111         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17112         this.el.setWidth(w);
17113         this.fireEvent("autosize", this, w);
17114     },
17115     
17116     // private
17117     SafariOnKeyDown : function(event)
17118     {
17119         // this is a workaround for a password hang bug on chrome/ webkit.
17120         
17121         var isSelectAll = false;
17122         
17123         if(this.el.dom.selectionEnd > 0){
17124             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17125         }
17126         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17127             event.preventDefault();
17128             this.setValue('');
17129             return;
17130         }
17131         
17132         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17133             
17134             event.preventDefault();
17135             // this is very hacky as keydown always get's upper case.
17136             
17137             var cc = String.fromCharCode(event.getCharCode());
17138             
17139             
17140             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17141             
17142         }
17143         
17144         
17145     }
17146 });/*
17147  * Based on:
17148  * Ext JS Library 1.1.1
17149  * Copyright(c) 2006-2007, Ext JS, LLC.
17150  *
17151  * Originally Released Under LGPL - original licence link has changed is not relivant.
17152  *
17153  * Fork - LGPL
17154  * <script type="text/javascript">
17155  */
17156  
17157 /**
17158  * @class Roo.form.Hidden
17159  * @extends Roo.form.TextField
17160  * Simple Hidden element used on forms 
17161  * 
17162  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17163  * 
17164  * @constructor
17165  * Creates a new Hidden form element.
17166  * @param {Object} config Configuration options
17167  */
17168
17169
17170
17171 // easy hidden field...
17172 Roo.form.Hidden = function(config){
17173     Roo.form.Hidden.superclass.constructor.call(this, config);
17174 };
17175   
17176 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17177     fieldLabel:      '',
17178     inputType:      'hidden',
17179     width:          50,
17180     allowBlank:     true,
17181     labelSeparator: '',
17182     hidden:         true,
17183     itemCls :       'x-form-item-display-none'
17184
17185
17186 });
17187
17188
17189 /*
17190  * Based on:
17191  * Ext JS Library 1.1.1
17192  * Copyright(c) 2006-2007, Ext JS, LLC.
17193  *
17194  * Originally Released Under LGPL - original licence link has changed is not relivant.
17195  *
17196  * Fork - LGPL
17197  * <script type="text/javascript">
17198  */
17199  
17200 /**
17201  * @class Roo.form.TriggerField
17202  * @extends Roo.form.TextField
17203  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17204  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17205  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17206  * for which you can provide a custom implementation.  For example:
17207  * <pre><code>
17208 var trigger = new Roo.form.TriggerField();
17209 trigger.onTriggerClick = myTriggerFn;
17210 trigger.applyTo('my-field');
17211 </code></pre>
17212  *
17213  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17214  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17215  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17216  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17217  * @constructor
17218  * Create a new TriggerField.
17219  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17220  * to the base TextField)
17221  */
17222 Roo.form.TriggerField = function(config){
17223     this.mimicing = false;
17224     Roo.form.TriggerField.superclass.constructor.call(this, config);
17225 };
17226
17227 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17228     /**
17229      * @cfg {String} triggerClass A CSS class to apply to the trigger
17230      */
17231     /**
17232      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17233      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17234      */
17235     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17236     /**
17237      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17238      */
17239     hideTrigger:false,
17240
17241     /** @cfg {Boolean} grow @hide */
17242     /** @cfg {Number} growMin @hide */
17243     /** @cfg {Number} growMax @hide */
17244
17245     /**
17246      * @hide 
17247      * @method
17248      */
17249     autoSize: Roo.emptyFn,
17250     // private
17251     monitorTab : true,
17252     // private
17253     deferHeight : true,
17254
17255     
17256     actionMode : 'wrap',
17257     // private
17258     onResize : function(w, h){
17259         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17260         if(typeof w == 'number'){
17261             var x = w - this.trigger.getWidth();
17262             this.el.setWidth(this.adjustWidth('input', x));
17263             this.trigger.setStyle('left', x+'px');
17264         }
17265     },
17266
17267     // private
17268     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17269
17270     // private
17271     getResizeEl : function(){
17272         return this.wrap;
17273     },
17274
17275     // private
17276     getPositionEl : function(){
17277         return this.wrap;
17278     },
17279
17280     // private
17281     alignErrorIcon : function(){
17282         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17283     },
17284
17285     // private
17286     onRender : function(ct, position){
17287         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17288         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17289         this.trigger = this.wrap.createChild(this.triggerConfig ||
17290                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17291         if(this.hideTrigger){
17292             this.trigger.setDisplayed(false);
17293         }
17294         this.initTrigger();
17295         if(!this.width){
17296             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17297         }
17298     },
17299
17300     // private
17301     initTrigger : function(){
17302         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17303         this.trigger.addClassOnOver('x-form-trigger-over');
17304         this.trigger.addClassOnClick('x-form-trigger-click');
17305     },
17306
17307     // private
17308     onDestroy : function(){
17309         if(this.trigger){
17310             this.trigger.removeAllListeners();
17311             this.trigger.remove();
17312         }
17313         if(this.wrap){
17314             this.wrap.remove();
17315         }
17316         Roo.form.TriggerField.superclass.onDestroy.call(this);
17317     },
17318
17319     // private
17320     onFocus : function(){
17321         Roo.form.TriggerField.superclass.onFocus.call(this);
17322         if(!this.mimicing){
17323             this.wrap.addClass('x-trigger-wrap-focus');
17324             this.mimicing = true;
17325             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17326             if(this.monitorTab){
17327                 this.el.on("keydown", this.checkTab, this);
17328             }
17329         }
17330     },
17331
17332     // private
17333     checkTab : function(e){
17334         if(e.getKey() == e.TAB){
17335             this.triggerBlur();
17336         }
17337     },
17338
17339     // private
17340     onBlur : function(){
17341         // do nothing
17342     },
17343
17344     // private
17345     mimicBlur : function(e, t){
17346         if(!this.wrap.contains(t) && this.validateBlur()){
17347             this.triggerBlur();
17348         }
17349     },
17350
17351     // private
17352     triggerBlur : function(){
17353         this.mimicing = false;
17354         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17355         if(this.monitorTab){
17356             this.el.un("keydown", this.checkTab, this);
17357         }
17358         this.wrap.removeClass('x-trigger-wrap-focus');
17359         Roo.form.TriggerField.superclass.onBlur.call(this);
17360     },
17361
17362     // private
17363     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17364     validateBlur : function(e, t){
17365         return true;
17366     },
17367
17368     // private
17369     onDisable : function(){
17370         Roo.form.TriggerField.superclass.onDisable.call(this);
17371         if(this.wrap){
17372             this.wrap.addClass('x-item-disabled');
17373         }
17374     },
17375
17376     // private
17377     onEnable : function(){
17378         Roo.form.TriggerField.superclass.onEnable.call(this);
17379         if(this.wrap){
17380             this.wrap.removeClass('x-item-disabled');
17381         }
17382     },
17383
17384     // private
17385     onShow : function(){
17386         var ae = this.getActionEl();
17387         
17388         if(ae){
17389             ae.dom.style.display = '';
17390             ae.dom.style.visibility = 'visible';
17391         }
17392     },
17393
17394     // private
17395     
17396     onHide : function(){
17397         var ae = this.getActionEl();
17398         ae.dom.style.display = 'none';
17399     },
17400
17401     /**
17402      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17403      * by an implementing function.
17404      * @method
17405      * @param {EventObject} e
17406      */
17407     onTriggerClick : Roo.emptyFn
17408 });
17409
17410 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17411 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17412 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17413 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17414     initComponent : function(){
17415         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17416
17417         this.triggerConfig = {
17418             tag:'span', cls:'x-form-twin-triggers', cn:[
17419             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17420             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17421         ]};
17422     },
17423
17424     getTrigger : function(index){
17425         return this.triggers[index];
17426     },
17427
17428     initTrigger : function(){
17429         var ts = this.trigger.select('.x-form-trigger', true);
17430         this.wrap.setStyle('overflow', 'hidden');
17431         var triggerField = this;
17432         ts.each(function(t, all, index){
17433             t.hide = function(){
17434                 var w = triggerField.wrap.getWidth();
17435                 this.dom.style.display = 'none';
17436                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17437             };
17438             t.show = function(){
17439                 var w = triggerField.wrap.getWidth();
17440                 this.dom.style.display = '';
17441                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17442             };
17443             var triggerIndex = 'Trigger'+(index+1);
17444
17445             if(this['hide'+triggerIndex]){
17446                 t.dom.style.display = 'none';
17447             }
17448             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17449             t.addClassOnOver('x-form-trigger-over');
17450             t.addClassOnClick('x-form-trigger-click');
17451         }, this);
17452         this.triggers = ts.elements;
17453     },
17454
17455     onTrigger1Click : Roo.emptyFn,
17456     onTrigger2Click : Roo.emptyFn
17457 });/*
17458  * Based on:
17459  * Ext JS Library 1.1.1
17460  * Copyright(c) 2006-2007, Ext JS, LLC.
17461  *
17462  * Originally Released Under LGPL - original licence link has changed is not relivant.
17463  *
17464  * Fork - LGPL
17465  * <script type="text/javascript">
17466  */
17467  
17468 /**
17469  * @class Roo.form.TextArea
17470  * @extends Roo.form.TextField
17471  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17472  * support for auto-sizing.
17473  * @constructor
17474  * Creates a new TextArea
17475  * @param {Object} config Configuration options
17476  */
17477 Roo.form.TextArea = function(config){
17478     Roo.form.TextArea.superclass.constructor.call(this, config);
17479     // these are provided exchanges for backwards compat
17480     // minHeight/maxHeight were replaced by growMin/growMax to be
17481     // compatible with TextField growing config values
17482     if(this.minHeight !== undefined){
17483         this.growMin = this.minHeight;
17484     }
17485     if(this.maxHeight !== undefined){
17486         this.growMax = this.maxHeight;
17487     }
17488 };
17489
17490 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17491     /**
17492      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17493      */
17494     growMin : 60,
17495     /**
17496      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17497      */
17498     growMax: 1000,
17499     /**
17500      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17501      * in the field (equivalent to setting overflow: hidden, defaults to false)
17502      */
17503     preventScrollbars: false,
17504     /**
17505      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17506      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17507      */
17508
17509     // private
17510     onRender : function(ct, position){
17511         if(!this.el){
17512             this.defaultAutoCreate = {
17513                 tag: "textarea",
17514                 style:"width:300px;height:60px;",
17515                 autocomplete: "new-password"
17516             };
17517         }
17518         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17519         if(this.grow){
17520             this.textSizeEl = Roo.DomHelper.append(document.body, {
17521                 tag: "pre", cls: "x-form-grow-sizer"
17522             });
17523             if(this.preventScrollbars){
17524                 this.el.setStyle("overflow", "hidden");
17525             }
17526             this.el.setHeight(this.growMin);
17527         }
17528     },
17529
17530     onDestroy : function(){
17531         if(this.textSizeEl){
17532             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17533         }
17534         Roo.form.TextArea.superclass.onDestroy.call(this);
17535     },
17536
17537     // private
17538     onKeyUp : function(e){
17539         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17540             this.autoSize();
17541         }
17542     },
17543
17544     /**
17545      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17546      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17547      */
17548     autoSize : function(){
17549         if(!this.grow || !this.textSizeEl){
17550             return;
17551         }
17552         var el = this.el;
17553         var v = el.dom.value;
17554         var ts = this.textSizeEl;
17555
17556         ts.innerHTML = '';
17557         ts.appendChild(document.createTextNode(v));
17558         v = ts.innerHTML;
17559
17560         Roo.fly(ts).setWidth(this.el.getWidth());
17561         if(v.length < 1){
17562             v = "&#160;&#160;";
17563         }else{
17564             if(Roo.isIE){
17565                 v = v.replace(/\n/g, '<p>&#160;</p>');
17566             }
17567             v += "&#160;\n&#160;";
17568         }
17569         ts.innerHTML = v;
17570         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17571         if(h != this.lastHeight){
17572             this.lastHeight = h;
17573             this.el.setHeight(h);
17574             this.fireEvent("autosize", this, h);
17575         }
17576     }
17577 });/*
17578  * Based on:
17579  * Ext JS Library 1.1.1
17580  * Copyright(c) 2006-2007, Ext JS, LLC.
17581  *
17582  * Originally Released Under LGPL - original licence link has changed is not relivant.
17583  *
17584  * Fork - LGPL
17585  * <script type="text/javascript">
17586  */
17587  
17588
17589 /**
17590  * @class Roo.form.NumberField
17591  * @extends Roo.form.TextField
17592  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17593  * @constructor
17594  * Creates a new NumberField
17595  * @param {Object} config Configuration options
17596  */
17597 Roo.form.NumberField = function(config){
17598     Roo.form.NumberField.superclass.constructor.call(this, config);
17599 };
17600
17601 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17602     /**
17603      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17604      */
17605     fieldClass: "x-form-field x-form-num-field",
17606     /**
17607      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17608      */
17609     allowDecimals : true,
17610     /**
17611      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17612      */
17613     decimalSeparator : ".",
17614     /**
17615      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17616      */
17617     decimalPrecision : 2,
17618     /**
17619      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17620      */
17621     allowNegative : true,
17622     /**
17623      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17624      */
17625     minValue : Number.NEGATIVE_INFINITY,
17626     /**
17627      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17628      */
17629     maxValue : Number.MAX_VALUE,
17630     /**
17631      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17632      */
17633     minText : "The minimum value for this field is {0}",
17634     /**
17635      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17636      */
17637     maxText : "The maximum value for this field is {0}",
17638     /**
17639      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17640      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17641      */
17642     nanText : "{0} is not a valid number",
17643
17644     // private
17645     initEvents : function(){
17646         Roo.form.NumberField.superclass.initEvents.call(this);
17647         var allowed = "0123456789";
17648         if(this.allowDecimals){
17649             allowed += this.decimalSeparator;
17650         }
17651         if(this.allowNegative){
17652             allowed += "-";
17653         }
17654         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17655         var keyPress = function(e){
17656             var k = e.getKey();
17657             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17658                 return;
17659             }
17660             var c = e.getCharCode();
17661             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17662                 e.stopEvent();
17663             }
17664         };
17665         this.el.on("keypress", keyPress, this);
17666     },
17667
17668     // private
17669     validateValue : function(value){
17670         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17671             return false;
17672         }
17673         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17674              return true;
17675         }
17676         var num = this.parseValue(value);
17677         if(isNaN(num)){
17678             this.markInvalid(String.format(this.nanText, value));
17679             return false;
17680         }
17681         if(num < this.minValue){
17682             this.markInvalid(String.format(this.minText, this.minValue));
17683             return false;
17684         }
17685         if(num > this.maxValue){
17686             this.markInvalid(String.format(this.maxText, this.maxValue));
17687             return false;
17688         }
17689         return true;
17690     },
17691
17692     getValue : function(){
17693         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17694     },
17695
17696     // private
17697     parseValue : function(value){
17698         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17699         return isNaN(value) ? '' : value;
17700     },
17701
17702     // private
17703     fixPrecision : function(value){
17704         var nan = isNaN(value);
17705         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17706             return nan ? '' : value;
17707         }
17708         return parseFloat(value).toFixed(this.decimalPrecision);
17709     },
17710
17711     setValue : function(v){
17712         v = this.fixPrecision(v);
17713         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17714     },
17715
17716     // private
17717     decimalPrecisionFcn : function(v){
17718         return Math.floor(v);
17719     },
17720
17721     beforeBlur : function(){
17722         var v = this.parseValue(this.getRawValue());
17723         if(v){
17724             this.setValue(v);
17725         }
17726     }
17727 });/*
17728  * Based on:
17729  * Ext JS Library 1.1.1
17730  * Copyright(c) 2006-2007, Ext JS, LLC.
17731  *
17732  * Originally Released Under LGPL - original licence link has changed is not relivant.
17733  *
17734  * Fork - LGPL
17735  * <script type="text/javascript">
17736  */
17737  
17738 /**
17739  * @class Roo.form.DateField
17740  * @extends Roo.form.TriggerField
17741  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17742 * @constructor
17743 * Create a new DateField
17744 * @param {Object} config
17745  */
17746 Roo.form.DateField = function(config){
17747     Roo.form.DateField.superclass.constructor.call(this, config);
17748     
17749       this.addEvents({
17750          
17751         /**
17752          * @event select
17753          * Fires when a date is selected
17754              * @param {Roo.form.DateField} combo This combo box
17755              * @param {Date} date The date selected
17756              */
17757         'select' : true
17758          
17759     });
17760     
17761     
17762     if(typeof this.minValue == "string") {
17763         this.minValue = this.parseDate(this.minValue);
17764     }
17765     if(typeof this.maxValue == "string") {
17766         this.maxValue = this.parseDate(this.maxValue);
17767     }
17768     this.ddMatch = null;
17769     if(this.disabledDates){
17770         var dd = this.disabledDates;
17771         var re = "(?:";
17772         for(var i = 0; i < dd.length; i++){
17773             re += dd[i];
17774             if(i != dd.length-1) {
17775                 re += "|";
17776             }
17777         }
17778         this.ddMatch = new RegExp(re + ")");
17779     }
17780 };
17781
17782 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17783     /**
17784      * @cfg {String} format
17785      * The default date format string which can be overriden for localization support.  The format must be
17786      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17787      */
17788     format : "m/d/y",
17789     /**
17790      * @cfg {String} altFormats
17791      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17792      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17793      */
17794     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17795     /**
17796      * @cfg {Array} disabledDays
17797      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17798      */
17799     disabledDays : null,
17800     /**
17801      * @cfg {String} disabledDaysText
17802      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17803      */
17804     disabledDaysText : "Disabled",
17805     /**
17806      * @cfg {Array} disabledDates
17807      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17808      * expression so they are very powerful. Some examples:
17809      * <ul>
17810      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17811      * <li>["03/08", "09/16"] would disable those days for every year</li>
17812      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17813      * <li>["03/../2006"] would disable every day in March 2006</li>
17814      * <li>["^03"] would disable every day in every March</li>
17815      * </ul>
17816      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17817      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17818      */
17819     disabledDates : null,
17820     /**
17821      * @cfg {String} disabledDatesText
17822      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17823      */
17824     disabledDatesText : "Disabled",
17825     /**
17826      * @cfg {Date/String} minValue
17827      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17828      * valid format (defaults to null).
17829      */
17830     minValue : null,
17831     /**
17832      * @cfg {Date/String} maxValue
17833      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17834      * valid format (defaults to null).
17835      */
17836     maxValue : null,
17837     /**
17838      * @cfg {String} minText
17839      * The error text to display when the date in the cell is before minValue (defaults to
17840      * 'The date in this field must be after {minValue}').
17841      */
17842     minText : "The date in this field must be equal to or after {0}",
17843     /**
17844      * @cfg {String} maxText
17845      * The error text to display when the date in the cell is after maxValue (defaults to
17846      * 'The date in this field must be before {maxValue}').
17847      */
17848     maxText : "The date in this field must be equal to or before {0}",
17849     /**
17850      * @cfg {String} invalidText
17851      * The error text to display when the date in the field is invalid (defaults to
17852      * '{value} is not a valid date - it must be in the format {format}').
17853      */
17854     invalidText : "{0} is not a valid date - it must be in the format {1}",
17855     /**
17856      * @cfg {String} triggerClass
17857      * An additional CSS class used to style the trigger button.  The trigger will always get the
17858      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17859      * which displays a calendar icon).
17860      */
17861     triggerClass : 'x-form-date-trigger',
17862     
17863
17864     /**
17865      * @cfg {Boolean} useIso
17866      * if enabled, then the date field will use a hidden field to store the 
17867      * real value as iso formated date. default (false)
17868      */ 
17869     useIso : false,
17870     /**
17871      * @cfg {String/Object} autoCreate
17872      * A DomHelper element spec, or true for a default element spec (defaults to
17873      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17874      */ 
17875     // private
17876     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17877     
17878     // private
17879     hiddenField: false,
17880     
17881     onRender : function(ct, position)
17882     {
17883         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17884         if (this.useIso) {
17885             //this.el.dom.removeAttribute('name'); 
17886             Roo.log("Changing name?");
17887             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17888             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17889                     'before', true);
17890             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17891             // prevent input submission
17892             this.hiddenName = this.name;
17893         }
17894             
17895             
17896     },
17897     
17898     // private
17899     validateValue : function(value)
17900     {
17901         value = this.formatDate(value);
17902         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17903             Roo.log('super failed');
17904             return false;
17905         }
17906         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17907              return true;
17908         }
17909         var svalue = value;
17910         value = this.parseDate(value);
17911         if(!value){
17912             Roo.log('parse date failed' + svalue);
17913             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17914             return false;
17915         }
17916         var time = value.getTime();
17917         if(this.minValue && time < this.minValue.getTime()){
17918             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17919             return false;
17920         }
17921         if(this.maxValue && time > this.maxValue.getTime()){
17922             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17923             return false;
17924         }
17925         if(this.disabledDays){
17926             var day = value.getDay();
17927             for(var i = 0; i < this.disabledDays.length; i++) {
17928                 if(day === this.disabledDays[i]){
17929                     this.markInvalid(this.disabledDaysText);
17930                     return false;
17931                 }
17932             }
17933         }
17934         var fvalue = this.formatDate(value);
17935         if(this.ddMatch && this.ddMatch.test(fvalue)){
17936             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17937             return false;
17938         }
17939         return true;
17940     },
17941
17942     // private
17943     // Provides logic to override the default TriggerField.validateBlur which just returns true
17944     validateBlur : function(){
17945         return !this.menu || !this.menu.isVisible();
17946     },
17947     
17948     getName: function()
17949     {
17950         // returns hidden if it's set..
17951         if (!this.rendered) {return ''};
17952         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17953         
17954     },
17955
17956     /**
17957      * Returns the current date value of the date field.
17958      * @return {Date} The date value
17959      */
17960     getValue : function(){
17961         
17962         return  this.hiddenField ?
17963                 this.hiddenField.value :
17964                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17965     },
17966
17967     /**
17968      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17969      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17970      * (the default format used is "m/d/y").
17971      * <br />Usage:
17972      * <pre><code>
17973 //All of these calls set the same date value (May 4, 2006)
17974
17975 //Pass a date object:
17976 var dt = new Date('5/4/06');
17977 dateField.setValue(dt);
17978
17979 //Pass a date string (default format):
17980 dateField.setValue('5/4/06');
17981
17982 //Pass a date string (custom format):
17983 dateField.format = 'Y-m-d';
17984 dateField.setValue('2006-5-4');
17985 </code></pre>
17986      * @param {String/Date} date The date or valid date string
17987      */
17988     setValue : function(date){
17989         if (this.hiddenField) {
17990             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17991         }
17992         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17993         // make sure the value field is always stored as a date..
17994         this.value = this.parseDate(date);
17995         
17996         
17997     },
17998
17999     // private
18000     parseDate : function(value){
18001         if(!value || value instanceof Date){
18002             return value;
18003         }
18004         var v = Date.parseDate(value, this.format);
18005          if (!v && this.useIso) {
18006             v = Date.parseDate(value, 'Y-m-d');
18007         }
18008         if(!v && this.altFormats){
18009             if(!this.altFormatsArray){
18010                 this.altFormatsArray = this.altFormats.split("|");
18011             }
18012             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18013                 v = Date.parseDate(value, this.altFormatsArray[i]);
18014             }
18015         }
18016         return v;
18017     },
18018
18019     // private
18020     formatDate : function(date, fmt){
18021         return (!date || !(date instanceof Date)) ?
18022                date : date.dateFormat(fmt || this.format);
18023     },
18024
18025     // private
18026     menuListeners : {
18027         select: function(m, d){
18028             
18029             this.setValue(d);
18030             this.fireEvent('select', this, d);
18031         },
18032         show : function(){ // retain focus styling
18033             this.onFocus();
18034         },
18035         hide : function(){
18036             this.focus.defer(10, this);
18037             var ml = this.menuListeners;
18038             this.menu.un("select", ml.select,  this);
18039             this.menu.un("show", ml.show,  this);
18040             this.menu.un("hide", ml.hide,  this);
18041         }
18042     },
18043
18044     // private
18045     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18046     onTriggerClick : function(){
18047         if(this.disabled){
18048             return;
18049         }
18050         if(this.menu == null){
18051             this.menu = new Roo.menu.DateMenu();
18052         }
18053         Roo.apply(this.menu.picker,  {
18054             showClear: this.allowBlank,
18055             minDate : this.minValue,
18056             maxDate : this.maxValue,
18057             disabledDatesRE : this.ddMatch,
18058             disabledDatesText : this.disabledDatesText,
18059             disabledDays : this.disabledDays,
18060             disabledDaysText : this.disabledDaysText,
18061             format : this.useIso ? 'Y-m-d' : this.format,
18062             minText : String.format(this.minText, this.formatDate(this.minValue)),
18063             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18064         });
18065         this.menu.on(Roo.apply({}, this.menuListeners, {
18066             scope:this
18067         }));
18068         this.menu.picker.setValue(this.getValue() || new Date());
18069         this.menu.show(this.el, "tl-bl?");
18070     },
18071
18072     beforeBlur : function(){
18073         var v = this.parseDate(this.getRawValue());
18074         if(v){
18075             this.setValue(v);
18076         }
18077     },
18078
18079     /*@
18080      * overide
18081      * 
18082      */
18083     isDirty : function() {
18084         if(this.disabled) {
18085             return false;
18086         }
18087         
18088         if(typeof(this.startValue) === 'undefined'){
18089             return false;
18090         }
18091         
18092         return String(this.getValue()) !== String(this.startValue);
18093         
18094     }
18095 });/*
18096  * Based on:
18097  * Ext JS Library 1.1.1
18098  * Copyright(c) 2006-2007, Ext JS, LLC.
18099  *
18100  * Originally Released Under LGPL - original licence link has changed is not relivant.
18101  *
18102  * Fork - LGPL
18103  * <script type="text/javascript">
18104  */
18105  
18106 /**
18107  * @class Roo.form.MonthField
18108  * @extends Roo.form.TriggerField
18109  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18110 * @constructor
18111 * Create a new MonthField
18112 * @param {Object} config
18113  */
18114 Roo.form.MonthField = function(config){
18115     
18116     Roo.form.MonthField.superclass.constructor.call(this, config);
18117     
18118       this.addEvents({
18119          
18120         /**
18121          * @event select
18122          * Fires when a date is selected
18123              * @param {Roo.form.MonthFieeld} combo This combo box
18124              * @param {Date} date The date selected
18125              */
18126         'select' : true
18127          
18128     });
18129     
18130     
18131     if(typeof this.minValue == "string") {
18132         this.minValue = this.parseDate(this.minValue);
18133     }
18134     if(typeof this.maxValue == "string") {
18135         this.maxValue = this.parseDate(this.maxValue);
18136     }
18137     this.ddMatch = null;
18138     if(this.disabledDates){
18139         var dd = this.disabledDates;
18140         var re = "(?:";
18141         for(var i = 0; i < dd.length; i++){
18142             re += dd[i];
18143             if(i != dd.length-1) {
18144                 re += "|";
18145             }
18146         }
18147         this.ddMatch = new RegExp(re + ")");
18148     }
18149 };
18150
18151 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18152     /**
18153      * @cfg {String} format
18154      * The default date format string which can be overriden for localization support.  The format must be
18155      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18156      */
18157     format : "M Y",
18158     /**
18159      * @cfg {String} altFormats
18160      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18161      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18162      */
18163     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18164     /**
18165      * @cfg {Array} disabledDays
18166      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18167      */
18168     disabledDays : [0,1,2,3,4,5,6],
18169     /**
18170      * @cfg {String} disabledDaysText
18171      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18172      */
18173     disabledDaysText : "Disabled",
18174     /**
18175      * @cfg {Array} disabledDates
18176      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18177      * expression so they are very powerful. Some examples:
18178      * <ul>
18179      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18180      * <li>["03/08", "09/16"] would disable those days for every year</li>
18181      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18182      * <li>["03/../2006"] would disable every day in March 2006</li>
18183      * <li>["^03"] would disable every day in every March</li>
18184      * </ul>
18185      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18186      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18187      */
18188     disabledDates : null,
18189     /**
18190      * @cfg {String} disabledDatesText
18191      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18192      */
18193     disabledDatesText : "Disabled",
18194     /**
18195      * @cfg {Date/String} minValue
18196      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18197      * valid format (defaults to null).
18198      */
18199     minValue : null,
18200     /**
18201      * @cfg {Date/String} maxValue
18202      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18203      * valid format (defaults to null).
18204      */
18205     maxValue : null,
18206     /**
18207      * @cfg {String} minText
18208      * The error text to display when the date in the cell is before minValue (defaults to
18209      * 'The date in this field must be after {minValue}').
18210      */
18211     minText : "The date in this field must be equal to or after {0}",
18212     /**
18213      * @cfg {String} maxTextf
18214      * The error text to display when the date in the cell is after maxValue (defaults to
18215      * 'The date in this field must be before {maxValue}').
18216      */
18217     maxText : "The date in this field must be equal to or before {0}",
18218     /**
18219      * @cfg {String} invalidText
18220      * The error text to display when the date in the field is invalid (defaults to
18221      * '{value} is not a valid date - it must be in the format {format}').
18222      */
18223     invalidText : "{0} is not a valid date - it must be in the format {1}",
18224     /**
18225      * @cfg {String} triggerClass
18226      * An additional CSS class used to style the trigger button.  The trigger will always get the
18227      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18228      * which displays a calendar icon).
18229      */
18230     triggerClass : 'x-form-date-trigger',
18231     
18232
18233     /**
18234      * @cfg {Boolean} useIso
18235      * if enabled, then the date field will use a hidden field to store the 
18236      * real value as iso formated date. default (true)
18237      */ 
18238     useIso : true,
18239     /**
18240      * @cfg {String/Object} autoCreate
18241      * A DomHelper element spec, or true for a default element spec (defaults to
18242      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18243      */ 
18244     // private
18245     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18246     
18247     // private
18248     hiddenField: false,
18249     
18250     hideMonthPicker : false,
18251     
18252     onRender : function(ct, position)
18253     {
18254         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18255         if (this.useIso) {
18256             this.el.dom.removeAttribute('name'); 
18257             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18258                     'before', true);
18259             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18260             // prevent input submission
18261             this.hiddenName = this.name;
18262         }
18263             
18264             
18265     },
18266     
18267     // private
18268     validateValue : function(value)
18269     {
18270         value = this.formatDate(value);
18271         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18272             return false;
18273         }
18274         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18275              return true;
18276         }
18277         var svalue = value;
18278         value = this.parseDate(value);
18279         if(!value){
18280             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18281             return false;
18282         }
18283         var time = value.getTime();
18284         if(this.minValue && time < this.minValue.getTime()){
18285             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18286             return false;
18287         }
18288         if(this.maxValue && time > this.maxValue.getTime()){
18289             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18290             return false;
18291         }
18292         /*if(this.disabledDays){
18293             var day = value.getDay();
18294             for(var i = 0; i < this.disabledDays.length; i++) {
18295                 if(day === this.disabledDays[i]){
18296                     this.markInvalid(this.disabledDaysText);
18297                     return false;
18298                 }
18299             }
18300         }
18301         */
18302         var fvalue = this.formatDate(value);
18303         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18304             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18305             return false;
18306         }
18307         */
18308         return true;
18309     },
18310
18311     // private
18312     // Provides logic to override the default TriggerField.validateBlur which just returns true
18313     validateBlur : function(){
18314         return !this.menu || !this.menu.isVisible();
18315     },
18316
18317     /**
18318      * Returns the current date value of the date field.
18319      * @return {Date} The date value
18320      */
18321     getValue : function(){
18322         
18323         
18324         
18325         return  this.hiddenField ?
18326                 this.hiddenField.value :
18327                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18328     },
18329
18330     /**
18331      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18332      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18333      * (the default format used is "m/d/y").
18334      * <br />Usage:
18335      * <pre><code>
18336 //All of these calls set the same date value (May 4, 2006)
18337
18338 //Pass a date object:
18339 var dt = new Date('5/4/06');
18340 monthField.setValue(dt);
18341
18342 //Pass a date string (default format):
18343 monthField.setValue('5/4/06');
18344
18345 //Pass a date string (custom format):
18346 monthField.format = 'Y-m-d';
18347 monthField.setValue('2006-5-4');
18348 </code></pre>
18349      * @param {String/Date} date The date or valid date string
18350      */
18351     setValue : function(date){
18352         Roo.log('month setValue' + date);
18353         // can only be first of month..
18354         
18355         var val = this.parseDate(date);
18356         
18357         if (this.hiddenField) {
18358             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18359         }
18360         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18361         this.value = this.parseDate(date);
18362     },
18363
18364     // private
18365     parseDate : function(value){
18366         if(!value || value instanceof Date){
18367             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18368             return value;
18369         }
18370         var v = Date.parseDate(value, this.format);
18371         if (!v && this.useIso) {
18372             v = Date.parseDate(value, 'Y-m-d');
18373         }
18374         if (v) {
18375             // 
18376             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18377         }
18378         
18379         
18380         if(!v && this.altFormats){
18381             if(!this.altFormatsArray){
18382                 this.altFormatsArray = this.altFormats.split("|");
18383             }
18384             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18385                 v = Date.parseDate(value, this.altFormatsArray[i]);
18386             }
18387         }
18388         return v;
18389     },
18390
18391     // private
18392     formatDate : function(date, fmt){
18393         return (!date || !(date instanceof Date)) ?
18394                date : date.dateFormat(fmt || this.format);
18395     },
18396
18397     // private
18398     menuListeners : {
18399         select: function(m, d){
18400             this.setValue(d);
18401             this.fireEvent('select', this, d);
18402         },
18403         show : function(){ // retain focus styling
18404             this.onFocus();
18405         },
18406         hide : function(){
18407             this.focus.defer(10, this);
18408             var ml = this.menuListeners;
18409             this.menu.un("select", ml.select,  this);
18410             this.menu.un("show", ml.show,  this);
18411             this.menu.un("hide", ml.hide,  this);
18412         }
18413     },
18414     // private
18415     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18416     onTriggerClick : function(){
18417         if(this.disabled){
18418             return;
18419         }
18420         if(this.menu == null){
18421             this.menu = new Roo.menu.DateMenu();
18422            
18423         }
18424         
18425         Roo.apply(this.menu.picker,  {
18426             
18427             showClear: this.allowBlank,
18428             minDate : this.minValue,
18429             maxDate : this.maxValue,
18430             disabledDatesRE : this.ddMatch,
18431             disabledDatesText : this.disabledDatesText,
18432             
18433             format : this.useIso ? 'Y-m-d' : this.format,
18434             minText : String.format(this.minText, this.formatDate(this.minValue)),
18435             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18436             
18437         });
18438          this.menu.on(Roo.apply({}, this.menuListeners, {
18439             scope:this
18440         }));
18441        
18442         
18443         var m = this.menu;
18444         var p = m.picker;
18445         
18446         // hide month picker get's called when we called by 'before hide';
18447         
18448         var ignorehide = true;
18449         p.hideMonthPicker  = function(disableAnim){
18450             if (ignorehide) {
18451                 return;
18452             }
18453              if(this.monthPicker){
18454                 Roo.log("hideMonthPicker called");
18455                 if(disableAnim === true){
18456                     this.monthPicker.hide();
18457                 }else{
18458                     this.monthPicker.slideOut('t', {duration:.2});
18459                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18460                     p.fireEvent("select", this, this.value);
18461                     m.hide();
18462                 }
18463             }
18464         }
18465         
18466         Roo.log('picker set value');
18467         Roo.log(this.getValue());
18468         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18469         m.show(this.el, 'tl-bl?');
18470         ignorehide  = false;
18471         // this will trigger hideMonthPicker..
18472         
18473         
18474         // hidden the day picker
18475         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18476         
18477         
18478         
18479       
18480         
18481         p.showMonthPicker.defer(100, p);
18482     
18483         
18484        
18485     },
18486
18487     beforeBlur : function(){
18488         var v = this.parseDate(this.getRawValue());
18489         if(v){
18490             this.setValue(v);
18491         }
18492     }
18493
18494     /** @cfg {Boolean} grow @hide */
18495     /** @cfg {Number} growMin @hide */
18496     /** @cfg {Number} growMax @hide */
18497     /**
18498      * @hide
18499      * @method autoSize
18500      */
18501 });/*
18502  * Based on:
18503  * Ext JS Library 1.1.1
18504  * Copyright(c) 2006-2007, Ext JS, LLC.
18505  *
18506  * Originally Released Under LGPL - original licence link has changed is not relivant.
18507  *
18508  * Fork - LGPL
18509  * <script type="text/javascript">
18510  */
18511  
18512
18513 /**
18514  * @class Roo.form.ComboBox
18515  * @extends Roo.form.TriggerField
18516  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18517  * @constructor
18518  * Create a new ComboBox.
18519  * @param {Object} config Configuration options
18520  */
18521 Roo.form.ComboBox = function(config){
18522     Roo.form.ComboBox.superclass.constructor.call(this, config);
18523     this.addEvents({
18524         /**
18525          * @event expand
18526          * Fires when the dropdown list is expanded
18527              * @param {Roo.form.ComboBox} combo This combo box
18528              */
18529         'expand' : true,
18530         /**
18531          * @event collapse
18532          * Fires when the dropdown list is collapsed
18533              * @param {Roo.form.ComboBox} combo This combo box
18534              */
18535         'collapse' : true,
18536         /**
18537          * @event beforeselect
18538          * Fires before a list item is selected. Return false to cancel the selection.
18539              * @param {Roo.form.ComboBox} combo This combo box
18540              * @param {Roo.data.Record} record The data record returned from the underlying store
18541              * @param {Number} index The index of the selected item in the dropdown list
18542              */
18543         'beforeselect' : true,
18544         /**
18545          * @event select
18546          * Fires when a list item is selected
18547              * @param {Roo.form.ComboBox} combo This combo box
18548              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18549              * @param {Number} index The index of the selected item in the dropdown list
18550              */
18551         'select' : true,
18552         /**
18553          * @event beforequery
18554          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18555          * The event object passed has these properties:
18556              * @param {Roo.form.ComboBox} combo This combo box
18557              * @param {String} query The query
18558              * @param {Boolean} forceAll true to force "all" query
18559              * @param {Boolean} cancel true to cancel the query
18560              * @param {Object} e The query event object
18561              */
18562         'beforequery': true,
18563          /**
18564          * @event add
18565          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18566              * @param {Roo.form.ComboBox} combo This combo box
18567              */
18568         'add' : true,
18569         /**
18570          * @event edit
18571          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18572              * @param {Roo.form.ComboBox} combo This combo box
18573              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18574              */
18575         'edit' : true
18576         
18577         
18578     });
18579     if(this.transform){
18580         this.allowDomMove = false;
18581         var s = Roo.getDom(this.transform);
18582         if(!this.hiddenName){
18583             this.hiddenName = s.name;
18584         }
18585         if(!this.store){
18586             this.mode = 'local';
18587             var d = [], opts = s.options;
18588             for(var i = 0, len = opts.length;i < len; i++){
18589                 var o = opts[i];
18590                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18591                 if(o.selected) {
18592                     this.value = value;
18593                 }
18594                 d.push([value, o.text]);
18595             }
18596             this.store = new Roo.data.SimpleStore({
18597                 'id': 0,
18598                 fields: ['value', 'text'],
18599                 data : d
18600             });
18601             this.valueField = 'value';
18602             this.displayField = 'text';
18603         }
18604         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18605         if(!this.lazyRender){
18606             this.target = true;
18607             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18608             s.parentNode.removeChild(s); // remove it
18609             this.render(this.el.parentNode);
18610         }else{
18611             s.parentNode.removeChild(s); // remove it
18612         }
18613
18614     }
18615     if (this.store) {
18616         this.store = Roo.factory(this.store, Roo.data);
18617     }
18618     
18619     this.selectedIndex = -1;
18620     if(this.mode == 'local'){
18621         if(config.queryDelay === undefined){
18622             this.queryDelay = 10;
18623         }
18624         if(config.minChars === undefined){
18625             this.minChars = 0;
18626         }
18627     }
18628 };
18629
18630 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18631     /**
18632      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18633      */
18634     /**
18635      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18636      * rendering into an Roo.Editor, defaults to false)
18637      */
18638     /**
18639      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18640      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18641      */
18642     /**
18643      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18644      */
18645     /**
18646      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18647      * the dropdown list (defaults to undefined, with no header element)
18648      */
18649
18650      /**
18651      * @cfg {String/Roo.Template} tpl The template to use to render the output
18652      */
18653      
18654     // private
18655     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18656     /**
18657      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18658      */
18659     listWidth: undefined,
18660     /**
18661      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18662      * mode = 'remote' or 'text' if mode = 'local')
18663      */
18664     displayField: undefined,
18665     /**
18666      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18667      * mode = 'remote' or 'value' if mode = 'local'). 
18668      * Note: use of a valueField requires the user make a selection
18669      * in order for a value to be mapped.
18670      */
18671     valueField: undefined,
18672     
18673     
18674     /**
18675      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18676      * field's data value (defaults to the underlying DOM element's name)
18677      */
18678     hiddenName: undefined,
18679     /**
18680      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18681      */
18682     listClass: '',
18683     /**
18684      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18685      */
18686     selectedClass: 'x-combo-selected',
18687     /**
18688      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18689      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18690      * which displays a downward arrow icon).
18691      */
18692     triggerClass : 'x-form-arrow-trigger',
18693     /**
18694      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18695      */
18696     shadow:'sides',
18697     /**
18698      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18699      * anchor positions (defaults to 'tl-bl')
18700      */
18701     listAlign: 'tl-bl?',
18702     /**
18703      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18704      */
18705     maxHeight: 300,
18706     /**
18707      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18708      * query specified by the allQuery config option (defaults to 'query')
18709      */
18710     triggerAction: 'query',
18711     /**
18712      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18713      * (defaults to 4, does not apply if editable = false)
18714      */
18715     minChars : 4,
18716     /**
18717      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18718      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18719      */
18720     typeAhead: false,
18721     /**
18722      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18723      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18724      */
18725     queryDelay: 500,
18726     /**
18727      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18728      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18729      */
18730     pageSize: 0,
18731     /**
18732      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18733      * when editable = true (defaults to false)
18734      */
18735     selectOnFocus:false,
18736     /**
18737      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18738      */
18739     queryParam: 'query',
18740     /**
18741      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18742      * when mode = 'remote' (defaults to 'Loading...')
18743      */
18744     loadingText: 'Loading...',
18745     /**
18746      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18747      */
18748     resizable: false,
18749     /**
18750      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18751      */
18752     handleHeight : 8,
18753     /**
18754      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18755      * traditional select (defaults to true)
18756      */
18757     editable: true,
18758     /**
18759      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18760      */
18761     allQuery: '',
18762     /**
18763      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18764      */
18765     mode: 'remote',
18766     /**
18767      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18768      * listWidth has a higher value)
18769      */
18770     minListWidth : 70,
18771     /**
18772      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18773      * allow the user to set arbitrary text into the field (defaults to false)
18774      */
18775     forceSelection:false,
18776     /**
18777      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18778      * if typeAhead = true (defaults to 250)
18779      */
18780     typeAheadDelay : 250,
18781     /**
18782      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18783      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18784      */
18785     valueNotFoundText : undefined,
18786     /**
18787      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18788      */
18789     blockFocus : false,
18790     
18791     /**
18792      * @cfg {Boolean} disableClear Disable showing of clear button.
18793      */
18794     disableClear : false,
18795     /**
18796      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18797      */
18798     alwaysQuery : false,
18799     
18800     //private
18801     addicon : false,
18802     editicon: false,
18803     
18804     // element that contains real text value.. (when hidden is used..)
18805      
18806     // private
18807     onRender : function(ct, position){
18808         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18809         if(this.hiddenName){
18810             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18811                     'before', true);
18812             this.hiddenField.value =
18813                 this.hiddenValue !== undefined ? this.hiddenValue :
18814                 this.value !== undefined ? this.value : '';
18815
18816             // prevent input submission
18817             this.el.dom.removeAttribute('name');
18818              
18819              
18820         }
18821         if(Roo.isGecko){
18822             this.el.dom.setAttribute('autocomplete', 'off');
18823         }
18824
18825         var cls = 'x-combo-list';
18826
18827         this.list = new Roo.Layer({
18828             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18829         });
18830
18831         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18832         this.list.setWidth(lw);
18833         this.list.swallowEvent('mousewheel');
18834         this.assetHeight = 0;
18835
18836         if(this.title){
18837             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18838             this.assetHeight += this.header.getHeight();
18839         }
18840
18841         this.innerList = this.list.createChild({cls:cls+'-inner'});
18842         this.innerList.on('mouseover', this.onViewOver, this);
18843         this.innerList.on('mousemove', this.onViewMove, this);
18844         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18845         
18846         if(this.allowBlank && !this.pageSize && !this.disableClear){
18847             this.footer = this.list.createChild({cls:cls+'-ft'});
18848             this.pageTb = new Roo.Toolbar(this.footer);
18849            
18850         }
18851         if(this.pageSize){
18852             this.footer = this.list.createChild({cls:cls+'-ft'});
18853             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18854                     {pageSize: this.pageSize});
18855             
18856         }
18857         
18858         if (this.pageTb && this.allowBlank && !this.disableClear) {
18859             var _this = this;
18860             this.pageTb.add(new Roo.Toolbar.Fill(), {
18861                 cls: 'x-btn-icon x-btn-clear',
18862                 text: '&#160;',
18863                 handler: function()
18864                 {
18865                     _this.collapse();
18866                     _this.clearValue();
18867                     _this.onSelect(false, -1);
18868                 }
18869             });
18870         }
18871         if (this.footer) {
18872             this.assetHeight += this.footer.getHeight();
18873         }
18874         
18875
18876         if(!this.tpl){
18877             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18878         }
18879
18880         this.view = new Roo.View(this.innerList, this.tpl, {
18881             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18882         });
18883
18884         this.view.on('click', this.onViewClick, this);
18885
18886         this.store.on('beforeload', this.onBeforeLoad, this);
18887         this.store.on('load', this.onLoad, this);
18888         this.store.on('loadexception', this.onLoadException, this);
18889
18890         if(this.resizable){
18891             this.resizer = new Roo.Resizable(this.list,  {
18892                pinned:true, handles:'se'
18893             });
18894             this.resizer.on('resize', function(r, w, h){
18895                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18896                 this.listWidth = w;
18897                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18898                 this.restrictHeight();
18899             }, this);
18900             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18901         }
18902         if(!this.editable){
18903             this.editable = true;
18904             this.setEditable(false);
18905         }  
18906         
18907         
18908         if (typeof(this.events.add.listeners) != 'undefined') {
18909             
18910             this.addicon = this.wrap.createChild(
18911                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18912        
18913             this.addicon.on('click', function(e) {
18914                 this.fireEvent('add', this);
18915             }, this);
18916         }
18917         if (typeof(this.events.edit.listeners) != 'undefined') {
18918             
18919             this.editicon = this.wrap.createChild(
18920                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18921             if (this.addicon) {
18922                 this.editicon.setStyle('margin-left', '40px');
18923             }
18924             this.editicon.on('click', function(e) {
18925                 
18926                 // we fire even  if inothing is selected..
18927                 this.fireEvent('edit', this, this.lastData );
18928                 
18929             }, this);
18930         }
18931         
18932         
18933         
18934     },
18935
18936     // private
18937     initEvents : function(){
18938         Roo.form.ComboBox.superclass.initEvents.call(this);
18939
18940         this.keyNav = new Roo.KeyNav(this.el, {
18941             "up" : function(e){
18942                 this.inKeyMode = true;
18943                 this.selectPrev();
18944             },
18945
18946             "down" : function(e){
18947                 if(!this.isExpanded()){
18948                     this.onTriggerClick();
18949                 }else{
18950                     this.inKeyMode = true;
18951                     this.selectNext();
18952                 }
18953             },
18954
18955             "enter" : function(e){
18956                 this.onViewClick();
18957                 //return true;
18958             },
18959
18960             "esc" : function(e){
18961                 this.collapse();
18962             },
18963
18964             "tab" : function(e){
18965                 this.onViewClick(false);
18966                 this.fireEvent("specialkey", this, e);
18967                 return true;
18968             },
18969
18970             scope : this,
18971
18972             doRelay : function(foo, bar, hname){
18973                 if(hname == 'down' || this.scope.isExpanded()){
18974                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18975                 }
18976                 return true;
18977             },
18978
18979             forceKeyDown: true
18980         });
18981         this.queryDelay = Math.max(this.queryDelay || 10,
18982                 this.mode == 'local' ? 10 : 250);
18983         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18984         if(this.typeAhead){
18985             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18986         }
18987         if(this.editable !== false){
18988             this.el.on("keyup", this.onKeyUp, this);
18989         }
18990         if(this.forceSelection){
18991             this.on('blur', this.doForce, this);
18992         }
18993     },
18994
18995     onDestroy : function(){
18996         if(this.view){
18997             this.view.setStore(null);
18998             this.view.el.removeAllListeners();
18999             this.view.el.remove();
19000             this.view.purgeListeners();
19001         }
19002         if(this.list){
19003             this.list.destroy();
19004         }
19005         if(this.store){
19006             this.store.un('beforeload', this.onBeforeLoad, this);
19007             this.store.un('load', this.onLoad, this);
19008             this.store.un('loadexception', this.onLoadException, this);
19009         }
19010         Roo.form.ComboBox.superclass.onDestroy.call(this);
19011     },
19012
19013     // private
19014     fireKey : function(e){
19015         if(e.isNavKeyPress() && !this.list.isVisible()){
19016             this.fireEvent("specialkey", this, e);
19017         }
19018     },
19019
19020     // private
19021     onResize: function(w, h){
19022         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19023         
19024         if(typeof w != 'number'){
19025             // we do not handle it!?!?
19026             return;
19027         }
19028         var tw = this.trigger.getWidth();
19029         tw += this.addicon ? this.addicon.getWidth() : 0;
19030         tw += this.editicon ? this.editicon.getWidth() : 0;
19031         var x = w - tw;
19032         this.el.setWidth( this.adjustWidth('input', x));
19033             
19034         this.trigger.setStyle('left', x+'px');
19035         
19036         if(this.list && this.listWidth === undefined){
19037             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19038             this.list.setWidth(lw);
19039             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19040         }
19041         
19042     
19043         
19044     },
19045
19046     /**
19047      * Allow or prevent the user from directly editing the field text.  If false is passed,
19048      * the user will only be able to select from the items defined in the dropdown list.  This method
19049      * is the runtime equivalent of setting the 'editable' config option at config time.
19050      * @param {Boolean} value True to allow the user to directly edit the field text
19051      */
19052     setEditable : function(value){
19053         if(value == this.editable){
19054             return;
19055         }
19056         this.editable = value;
19057         if(!value){
19058             this.el.dom.setAttribute('readOnly', true);
19059             this.el.on('mousedown', this.onTriggerClick,  this);
19060             this.el.addClass('x-combo-noedit');
19061         }else{
19062             this.el.dom.setAttribute('readOnly', false);
19063             this.el.un('mousedown', this.onTriggerClick,  this);
19064             this.el.removeClass('x-combo-noedit');
19065         }
19066     },
19067
19068     // private
19069     onBeforeLoad : function(){
19070         if(!this.hasFocus){
19071             return;
19072         }
19073         this.innerList.update(this.loadingText ?
19074                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19075         this.restrictHeight();
19076         this.selectedIndex = -1;
19077     },
19078
19079     // private
19080     onLoad : function(){
19081         if(!this.hasFocus){
19082             return;
19083         }
19084         if(this.store.getCount() > 0){
19085             this.expand();
19086             this.restrictHeight();
19087             if(this.lastQuery == this.allQuery){
19088                 if(this.editable){
19089                     this.el.dom.select();
19090                 }
19091                 if(!this.selectByValue(this.value, true)){
19092                     this.select(0, true);
19093                 }
19094             }else{
19095                 this.selectNext();
19096                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19097                     this.taTask.delay(this.typeAheadDelay);
19098                 }
19099             }
19100         }else{
19101             this.onEmptyResults();
19102         }
19103         //this.el.focus();
19104     },
19105     // private
19106     onLoadException : function()
19107     {
19108         this.collapse();
19109         Roo.log(this.store.reader.jsonData);
19110         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19111             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19112         }
19113         
19114         
19115     },
19116     // private
19117     onTypeAhead : function(){
19118         if(this.store.getCount() > 0){
19119             var r = this.store.getAt(0);
19120             var newValue = r.data[this.displayField];
19121             var len = newValue.length;
19122             var selStart = this.getRawValue().length;
19123             if(selStart != len){
19124                 this.setRawValue(newValue);
19125                 this.selectText(selStart, newValue.length);
19126             }
19127         }
19128     },
19129
19130     // private
19131     onSelect : function(record, index){
19132         if(this.fireEvent('beforeselect', this, record, index) !== false){
19133             this.setFromData(index > -1 ? record.data : false);
19134             this.collapse();
19135             this.fireEvent('select', this, record, index);
19136         }
19137     },
19138
19139     /**
19140      * Returns the currently selected field value or empty string if no value is set.
19141      * @return {String} value The selected value
19142      */
19143     getValue : function(){
19144         if(this.valueField){
19145             return typeof this.value != 'undefined' ? this.value : '';
19146         }
19147         return Roo.form.ComboBox.superclass.getValue.call(this);
19148     },
19149
19150     /**
19151      * Clears any text/value currently set in the field
19152      */
19153     clearValue : function(){
19154         if(this.hiddenField){
19155             this.hiddenField.value = '';
19156         }
19157         this.value = '';
19158         this.setRawValue('');
19159         this.lastSelectionText = '';
19160         
19161     },
19162
19163     /**
19164      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19165      * will be displayed in the field.  If the value does not match the data value of an existing item,
19166      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19167      * Otherwise the field will be blank (although the value will still be set).
19168      * @param {String} value The value to match
19169      */
19170     setValue : function(v){
19171         var text = v;
19172         if(this.valueField){
19173             var r = this.findRecord(this.valueField, v);
19174             if(r){
19175                 text = r.data[this.displayField];
19176             }else if(this.valueNotFoundText !== undefined){
19177                 text = this.valueNotFoundText;
19178             }
19179         }
19180         this.lastSelectionText = text;
19181         if(this.hiddenField){
19182             this.hiddenField.value = v;
19183         }
19184         Roo.form.ComboBox.superclass.setValue.call(this, text);
19185         this.value = v;
19186     },
19187     /**
19188      * @property {Object} the last set data for the element
19189      */
19190     
19191     lastData : false,
19192     /**
19193      * Sets the value of the field based on a object which is related to the record format for the store.
19194      * @param {Object} value the value to set as. or false on reset?
19195      */
19196     setFromData : function(o){
19197         var dv = ''; // display value
19198         var vv = ''; // value value..
19199         this.lastData = o;
19200         if (this.displayField) {
19201             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19202         } else {
19203             // this is an error condition!!!
19204             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19205         }
19206         
19207         if(this.valueField){
19208             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19209         }
19210         if(this.hiddenField){
19211             this.hiddenField.value = vv;
19212             
19213             this.lastSelectionText = dv;
19214             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19215             this.value = vv;
19216             return;
19217         }
19218         // no hidden field.. - we store the value in 'value', but still display
19219         // display field!!!!
19220         this.lastSelectionText = dv;
19221         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19222         this.value = vv;
19223         
19224         
19225     },
19226     // private
19227     reset : function(){
19228         // overridden so that last data is reset..
19229         this.setValue(this.resetValue);
19230         this.clearInvalid();
19231         this.lastData = false;
19232         if (this.view) {
19233             this.view.clearSelections();
19234         }
19235     },
19236     // private
19237     findRecord : function(prop, value){
19238         var record;
19239         if(this.store.getCount() > 0){
19240             this.store.each(function(r){
19241                 if(r.data[prop] == value){
19242                     record = r;
19243                     return false;
19244                 }
19245                 return true;
19246             });
19247         }
19248         return record;
19249     },
19250     
19251     getName: function()
19252     {
19253         // returns hidden if it's set..
19254         if (!this.rendered) {return ''};
19255         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19256         
19257     },
19258     // private
19259     onViewMove : function(e, t){
19260         this.inKeyMode = false;
19261     },
19262
19263     // private
19264     onViewOver : function(e, t){
19265         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19266             return;
19267         }
19268         var item = this.view.findItemFromChild(t);
19269         if(item){
19270             var index = this.view.indexOf(item);
19271             this.select(index, false);
19272         }
19273     },
19274
19275     // private
19276     onViewClick : function(doFocus)
19277     {
19278         var index = this.view.getSelectedIndexes()[0];
19279         var r = this.store.getAt(index);
19280         if(r){
19281             this.onSelect(r, index);
19282         }
19283         if(doFocus !== false && !this.blockFocus){
19284             this.el.focus();
19285         }
19286     },
19287
19288     // private
19289     restrictHeight : function(){
19290         this.innerList.dom.style.height = '';
19291         var inner = this.innerList.dom;
19292         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19293         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19294         this.list.beginUpdate();
19295         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19296         this.list.alignTo(this.el, this.listAlign);
19297         this.list.endUpdate();
19298     },
19299
19300     // private
19301     onEmptyResults : function(){
19302         this.collapse();
19303     },
19304
19305     /**
19306      * Returns true if the dropdown list is expanded, else false.
19307      */
19308     isExpanded : function(){
19309         return this.list.isVisible();
19310     },
19311
19312     /**
19313      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19314      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19315      * @param {String} value The data value of the item to select
19316      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19317      * selected item if it is not currently in view (defaults to true)
19318      * @return {Boolean} True if the value matched an item in the list, else false
19319      */
19320     selectByValue : function(v, scrollIntoView){
19321         if(v !== undefined && v !== null){
19322             var r = this.findRecord(this.valueField || this.displayField, v);
19323             if(r){
19324                 this.select(this.store.indexOf(r), scrollIntoView);
19325                 return true;
19326             }
19327         }
19328         return false;
19329     },
19330
19331     /**
19332      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19333      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19334      * @param {Number} index The zero-based index of the list item to select
19335      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19336      * selected item if it is not currently in view (defaults to true)
19337      */
19338     select : function(index, scrollIntoView){
19339         this.selectedIndex = index;
19340         this.view.select(index);
19341         if(scrollIntoView !== false){
19342             var el = this.view.getNode(index);
19343             if(el){
19344                 this.innerList.scrollChildIntoView(el, false);
19345             }
19346         }
19347     },
19348
19349     // private
19350     selectNext : function(){
19351         var ct = this.store.getCount();
19352         if(ct > 0){
19353             if(this.selectedIndex == -1){
19354                 this.select(0);
19355             }else if(this.selectedIndex < ct-1){
19356                 this.select(this.selectedIndex+1);
19357             }
19358         }
19359     },
19360
19361     // private
19362     selectPrev : function(){
19363         var ct = this.store.getCount();
19364         if(ct > 0){
19365             if(this.selectedIndex == -1){
19366                 this.select(0);
19367             }else if(this.selectedIndex != 0){
19368                 this.select(this.selectedIndex-1);
19369             }
19370         }
19371     },
19372
19373     // private
19374     onKeyUp : function(e){
19375         if(this.editable !== false && !e.isSpecialKey()){
19376             this.lastKey = e.getKey();
19377             this.dqTask.delay(this.queryDelay);
19378         }
19379     },
19380
19381     // private
19382     validateBlur : function(){
19383         return !this.list || !this.list.isVisible();   
19384     },
19385
19386     // private
19387     initQuery : function(){
19388         this.doQuery(this.getRawValue());
19389     },
19390
19391     // private
19392     doForce : function(){
19393         if(this.el.dom.value.length > 0){
19394             this.el.dom.value =
19395                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19396              
19397         }
19398     },
19399
19400     /**
19401      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19402      * query allowing the query action to be canceled if needed.
19403      * @param {String} query The SQL query to execute
19404      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19405      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19406      * saved in the current store (defaults to false)
19407      */
19408     doQuery : function(q, forceAll){
19409         if(q === undefined || q === null){
19410             q = '';
19411         }
19412         var qe = {
19413             query: q,
19414             forceAll: forceAll,
19415             combo: this,
19416             cancel:false
19417         };
19418         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19419             return false;
19420         }
19421         q = qe.query;
19422         forceAll = qe.forceAll;
19423         if(forceAll === true || (q.length >= this.minChars)){
19424             if(this.lastQuery != q || this.alwaysQuery){
19425                 this.lastQuery = q;
19426                 if(this.mode == 'local'){
19427                     this.selectedIndex = -1;
19428                     if(forceAll){
19429                         this.store.clearFilter();
19430                     }else{
19431                         this.store.filter(this.displayField, q);
19432                     }
19433                     this.onLoad();
19434                 }else{
19435                     this.store.baseParams[this.queryParam] = q;
19436                     this.store.load({
19437                         params: this.getParams(q)
19438                     });
19439                     this.expand();
19440                 }
19441             }else{
19442                 this.selectedIndex = -1;
19443                 this.onLoad();   
19444             }
19445         }
19446     },
19447
19448     // private
19449     getParams : function(q){
19450         var p = {};
19451         //p[this.queryParam] = q;
19452         if(this.pageSize){
19453             p.start = 0;
19454             p.limit = this.pageSize;
19455         }
19456         return p;
19457     },
19458
19459     /**
19460      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19461      */
19462     collapse : function(){
19463         if(!this.isExpanded()){
19464             return;
19465         }
19466         this.list.hide();
19467         Roo.get(document).un('mousedown', this.collapseIf, this);
19468         Roo.get(document).un('mousewheel', this.collapseIf, this);
19469         if (!this.editable) {
19470             Roo.get(document).un('keydown', this.listKeyPress, this);
19471         }
19472         this.fireEvent('collapse', this);
19473     },
19474
19475     // private
19476     collapseIf : function(e){
19477         if(!e.within(this.wrap) && !e.within(this.list)){
19478             this.collapse();
19479         }
19480     },
19481
19482     /**
19483      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19484      */
19485     expand : function(){
19486         if(this.isExpanded() || !this.hasFocus){
19487             return;
19488         }
19489         this.list.alignTo(this.el, this.listAlign);
19490         this.list.show();
19491         Roo.get(document).on('mousedown', this.collapseIf, this);
19492         Roo.get(document).on('mousewheel', this.collapseIf, this);
19493         if (!this.editable) {
19494             Roo.get(document).on('keydown', this.listKeyPress, this);
19495         }
19496         
19497         this.fireEvent('expand', this);
19498     },
19499
19500     // private
19501     // Implements the default empty TriggerField.onTriggerClick function
19502     onTriggerClick : function(){
19503         if(this.disabled){
19504             return;
19505         }
19506         if(this.isExpanded()){
19507             this.collapse();
19508             if (!this.blockFocus) {
19509                 this.el.focus();
19510             }
19511             
19512         }else {
19513             this.hasFocus = true;
19514             if(this.triggerAction == 'all') {
19515                 this.doQuery(this.allQuery, true);
19516             } else {
19517                 this.doQuery(this.getRawValue());
19518             }
19519             if (!this.blockFocus) {
19520                 this.el.focus();
19521             }
19522         }
19523     },
19524     listKeyPress : function(e)
19525     {
19526         //Roo.log('listkeypress');
19527         // scroll to first matching element based on key pres..
19528         if (e.isSpecialKey()) {
19529             return false;
19530         }
19531         var k = String.fromCharCode(e.getKey()).toUpperCase();
19532         //Roo.log(k);
19533         var match  = false;
19534         var csel = this.view.getSelectedNodes();
19535         var cselitem = false;
19536         if (csel.length) {
19537             var ix = this.view.indexOf(csel[0]);
19538             cselitem  = this.store.getAt(ix);
19539             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19540                 cselitem = false;
19541             }
19542             
19543         }
19544         
19545         this.store.each(function(v) { 
19546             if (cselitem) {
19547                 // start at existing selection.
19548                 if (cselitem.id == v.id) {
19549                     cselitem = false;
19550                 }
19551                 return;
19552             }
19553                 
19554             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19555                 match = this.store.indexOf(v);
19556                 return false;
19557             }
19558         }, this);
19559         
19560         if (match === false) {
19561             return true; // no more action?
19562         }
19563         // scroll to?
19564         this.view.select(match);
19565         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19566         sn.scrollIntoView(sn.dom.parentNode, false);
19567     }
19568
19569     /** 
19570     * @cfg {Boolean} grow 
19571     * @hide 
19572     */
19573     /** 
19574     * @cfg {Number} growMin 
19575     * @hide 
19576     */
19577     /** 
19578     * @cfg {Number} growMax 
19579     * @hide 
19580     */
19581     /**
19582      * @hide
19583      * @method autoSize
19584      */
19585 });/*
19586  * Copyright(c) 2010-2012, Roo J Solutions Limited
19587  *
19588  * Licence LGPL
19589  *
19590  */
19591
19592 /**
19593  * @class Roo.form.ComboBoxArray
19594  * @extends Roo.form.TextField
19595  * A facebook style adder... for lists of email / people / countries  etc...
19596  * pick multiple items from a combo box, and shows each one.
19597  *
19598  *  Fred [x]  Brian [x]  [Pick another |v]
19599  *
19600  *
19601  *  For this to work: it needs various extra information
19602  *    - normal combo problay has
19603  *      name, hiddenName
19604  *    + displayField, valueField
19605  *
19606  *    For our purpose...
19607  *
19608  *
19609  *   If we change from 'extends' to wrapping...
19610  *   
19611  *  
19612  *
19613  
19614  
19615  * @constructor
19616  * Create a new ComboBoxArray.
19617  * @param {Object} config Configuration options
19618  */
19619  
19620
19621 Roo.form.ComboBoxArray = function(config)
19622 {
19623     this.addEvents({
19624         /**
19625          * @event beforeremove
19626          * Fires before remove the value from the list
19627              * @param {Roo.form.ComboBoxArray} _self This combo box array
19628              * @param {Roo.form.ComboBoxArray.Item} item removed item
19629              */
19630         'beforeremove' : true,
19631         /**
19632          * @event remove
19633          * Fires when remove the value from the list
19634              * @param {Roo.form.ComboBoxArray} _self This combo box array
19635              * @param {Roo.form.ComboBoxArray.Item} item removed item
19636              */
19637         'remove' : true
19638         
19639         
19640     });
19641     
19642     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19643     
19644     this.items = new Roo.util.MixedCollection(false);
19645     
19646     // construct the child combo...
19647     
19648     
19649     
19650     
19651    
19652     
19653 }
19654
19655  
19656 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19657
19658     /**
19659      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19660      */
19661     
19662     lastData : false,
19663     
19664     // behavies liek a hiddne field
19665     inputType:      'hidden',
19666     /**
19667      * @cfg {Number} width The width of the box that displays the selected element
19668      */ 
19669     width:          300,
19670
19671     
19672     
19673     /**
19674      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19675      */
19676     name : false,
19677     /**
19678      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19679      */
19680     hiddenName : false,
19681     
19682     
19683     // private the array of items that are displayed..
19684     items  : false,
19685     // private - the hidden field el.
19686     hiddenEl : false,
19687     // private - the filed el..
19688     el : false,
19689     
19690     //validateValue : function() { return true; }, // all values are ok!
19691     //onAddClick: function() { },
19692     
19693     onRender : function(ct, position) 
19694     {
19695         
19696         // create the standard hidden element
19697         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19698         
19699         
19700         // give fake names to child combo;
19701         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19702         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19703         
19704         this.combo = Roo.factory(this.combo, Roo.form);
19705         this.combo.onRender(ct, position);
19706         if (typeof(this.combo.width) != 'undefined') {
19707             this.combo.onResize(this.combo.width,0);
19708         }
19709         
19710         this.combo.initEvents();
19711         
19712         // assigned so form know we need to do this..
19713         this.store          = this.combo.store;
19714         this.valueField     = this.combo.valueField;
19715         this.displayField   = this.combo.displayField ;
19716         
19717         
19718         this.combo.wrap.addClass('x-cbarray-grp');
19719         
19720         var cbwrap = this.combo.wrap.createChild(
19721             {tag: 'div', cls: 'x-cbarray-cb'},
19722             this.combo.el.dom
19723         );
19724         
19725              
19726         this.hiddenEl = this.combo.wrap.createChild({
19727             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19728         });
19729         this.el = this.combo.wrap.createChild({
19730             tag: 'input',  type:'hidden' , name: this.name, value : ''
19731         });
19732          //   this.el.dom.removeAttribute("name");
19733         
19734         
19735         this.outerWrap = this.combo.wrap;
19736         this.wrap = cbwrap;
19737         
19738         this.outerWrap.setWidth(this.width);
19739         this.outerWrap.dom.removeChild(this.el.dom);
19740         
19741         this.wrap.dom.appendChild(this.el.dom);
19742         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19743         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19744         
19745         this.combo.trigger.setStyle('position','relative');
19746         this.combo.trigger.setStyle('left', '0px');
19747         this.combo.trigger.setStyle('top', '2px');
19748         
19749         this.combo.el.setStyle('vertical-align', 'text-bottom');
19750         
19751         //this.trigger.setStyle('vertical-align', 'top');
19752         
19753         // this should use the code from combo really... on('add' ....)
19754         if (this.adder) {
19755             
19756         
19757             this.adder = this.outerWrap.createChild(
19758                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19759             var _t = this;
19760             this.adder.on('click', function(e) {
19761                 _t.fireEvent('adderclick', this, e);
19762             }, _t);
19763         }
19764         //var _t = this;
19765         //this.adder.on('click', this.onAddClick, _t);
19766         
19767         
19768         this.combo.on('select', function(cb, rec, ix) {
19769             this.addItem(rec.data);
19770             
19771             cb.setValue('');
19772             cb.el.dom.value = '';
19773             //cb.lastData = rec.data;
19774             // add to list
19775             
19776         }, this);
19777         
19778         
19779     },
19780     
19781     
19782     getName: function()
19783     {
19784         // returns hidden if it's set..
19785         if (!this.rendered) {return ''};
19786         return  this.hiddenName ? this.hiddenName : this.name;
19787         
19788     },
19789     
19790     
19791     onResize: function(w, h){
19792         
19793         return;
19794         // not sure if this is needed..
19795         //this.combo.onResize(w,h);
19796         
19797         if(typeof w != 'number'){
19798             // we do not handle it!?!?
19799             return;
19800         }
19801         var tw = this.combo.trigger.getWidth();
19802         tw += this.addicon ? this.addicon.getWidth() : 0;
19803         tw += this.editicon ? this.editicon.getWidth() : 0;
19804         var x = w - tw;
19805         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19806             
19807         this.combo.trigger.setStyle('left', '0px');
19808         
19809         if(this.list && this.listWidth === undefined){
19810             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19811             this.list.setWidth(lw);
19812             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19813         }
19814         
19815     
19816         
19817     },
19818     
19819     addItem: function(rec)
19820     {
19821         var valueField = this.combo.valueField;
19822         var displayField = this.combo.displayField;
19823         if (this.items.indexOfKey(rec[valueField]) > -1) {
19824             //console.log("GOT " + rec.data.id);
19825             return;
19826         }
19827         
19828         var x = new Roo.form.ComboBoxArray.Item({
19829             //id : rec[this.idField],
19830             data : rec,
19831             displayField : displayField ,
19832             tipField : displayField ,
19833             cb : this
19834         });
19835         // use the 
19836         this.items.add(rec[valueField],x);
19837         // add it before the element..
19838         this.updateHiddenEl();
19839         x.render(this.outerWrap, this.wrap.dom);
19840         // add the image handler..
19841     },
19842     
19843     updateHiddenEl : function()
19844     {
19845         this.validate();
19846         if (!this.hiddenEl) {
19847             return;
19848         }
19849         var ar = [];
19850         var idField = this.combo.valueField;
19851         
19852         this.items.each(function(f) {
19853             ar.push(f.data[idField]);
19854            
19855         });
19856         this.hiddenEl.dom.value = ar.join(',');
19857         this.validate();
19858     },
19859     
19860     reset : function()
19861     {
19862         this.items.clear();
19863         
19864         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19865            el.remove();
19866         });
19867         
19868         this.el.dom.value = '';
19869         if (this.hiddenEl) {
19870             this.hiddenEl.dom.value = '';
19871         }
19872         
19873     },
19874     getValue: function()
19875     {
19876         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19877     },
19878     setValue: function(v) // not a valid action - must use addItems..
19879     {
19880          
19881         this.reset();
19882         
19883         
19884         
19885         if (this.store.isLocal && (typeof(v) == 'string')) {
19886             // then we can use the store to find the values..
19887             // comma seperated at present.. this needs to allow JSON based encoding..
19888             this.hiddenEl.value  = v;
19889             var v_ar = [];
19890             Roo.each(v.split(','), function(k) {
19891                 Roo.log("CHECK " + this.valueField + ',' + k);
19892                 var li = this.store.query(this.valueField, k);
19893                 if (!li.length) {
19894                     return;
19895                 }
19896                 var add = {};
19897                 add[this.valueField] = k;
19898                 add[this.displayField] = li.item(0).data[this.displayField];
19899                 
19900                 this.addItem(add);
19901             }, this) 
19902              
19903         }
19904         if (typeof(v) == 'object' ) {
19905             // then let's assume it's an array of objects..
19906             Roo.each(v, function(l) {
19907                 this.addItem(l);
19908             }, this);
19909              
19910         }
19911         
19912         
19913     },
19914     setFromData: function(v)
19915     {
19916         // this recieves an object, if setValues is called.
19917         this.reset();
19918         this.el.dom.value = v[this.displayField];
19919         this.hiddenEl.dom.value = v[this.valueField];
19920         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19921             return;
19922         }
19923         var kv = v[this.valueField];
19924         var dv = v[this.displayField];
19925         kv = typeof(kv) != 'string' ? '' : kv;
19926         dv = typeof(dv) != 'string' ? '' : dv;
19927         
19928         
19929         var keys = kv.split(',');
19930         var display = dv.split(',');
19931         for (var i = 0 ; i < keys.length; i++) {
19932             
19933             add = {};
19934             add[this.valueField] = keys[i];
19935             add[this.displayField] = display[i];
19936             this.addItem(add);
19937         }
19938       
19939         
19940     },
19941     
19942     /**
19943      * Validates the combox array value
19944      * @return {Boolean} True if the value is valid, else false
19945      */
19946     validate : function(){
19947         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19948             this.clearInvalid();
19949             return true;
19950         }
19951         return false;
19952     },
19953     
19954     validateValue : function(value){
19955         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19956         
19957     },
19958     
19959     /*@
19960      * overide
19961      * 
19962      */
19963     isDirty : function() {
19964         if(this.disabled) {
19965             return false;
19966         }
19967         
19968         try {
19969             var d = Roo.decode(String(this.originalValue));
19970         } catch (e) {
19971             return String(this.getValue()) !== String(this.originalValue);
19972         }
19973         
19974         var originalValue = [];
19975         
19976         for (var i = 0; i < d.length; i++){
19977             originalValue.push(d[i][this.valueField]);
19978         }
19979         
19980         return String(this.getValue()) !== String(originalValue.join(','));
19981         
19982     }
19983     
19984 });
19985
19986
19987
19988 /**
19989  * @class Roo.form.ComboBoxArray.Item
19990  * @extends Roo.BoxComponent
19991  * A selected item in the list
19992  *  Fred [x]  Brian [x]  [Pick another |v]
19993  * 
19994  * @constructor
19995  * Create a new item.
19996  * @param {Object} config Configuration options
19997  */
19998  
19999 Roo.form.ComboBoxArray.Item = function(config) {
20000     config.id = Roo.id();
20001     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20002 }
20003
20004 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20005     data : {},
20006     cb: false,
20007     displayField : false,
20008     tipField : false,
20009     
20010     
20011     defaultAutoCreate : {
20012         tag: 'div',
20013         cls: 'x-cbarray-item',
20014         cn : [ 
20015             { tag: 'div' },
20016             {
20017                 tag: 'img',
20018                 width:16,
20019                 height : 16,
20020                 src : Roo.BLANK_IMAGE_URL ,
20021                 align: 'center'
20022             }
20023         ]
20024         
20025     },
20026     
20027  
20028     onRender : function(ct, position)
20029     {
20030         Roo.form.Field.superclass.onRender.call(this, ct, position);
20031         
20032         if(!this.el){
20033             var cfg = this.getAutoCreate();
20034             this.el = ct.createChild(cfg, position);
20035         }
20036         
20037         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20038         
20039         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20040             this.cb.renderer(this.data) :
20041             String.format('{0}',this.data[this.displayField]);
20042         
20043             
20044         this.el.child('div').dom.setAttribute('qtip',
20045                         String.format('{0}',this.data[this.tipField])
20046         );
20047         
20048         this.el.child('img').on('click', this.remove, this);
20049         
20050     },
20051    
20052     remove : function()
20053     {
20054         if(this.cb.disabled){
20055             return;
20056         }
20057         
20058         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20059             this.cb.items.remove(this);
20060             this.el.child('img').un('click', this.remove, this);
20061             this.el.remove();
20062             this.cb.updateHiddenEl();
20063
20064             this.cb.fireEvent('remove', this.cb, this);
20065         }
20066         
20067     }
20068 });/*
20069  * Based on:
20070  * Ext JS Library 1.1.1
20071  * Copyright(c) 2006-2007, Ext JS, LLC.
20072  *
20073  * Originally Released Under LGPL - original licence link has changed is not relivant.
20074  *
20075  * Fork - LGPL
20076  * <script type="text/javascript">
20077  */
20078 /**
20079  * @class Roo.form.Checkbox
20080  * @extends Roo.form.Field
20081  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20082  * @constructor
20083  * Creates a new Checkbox
20084  * @param {Object} config Configuration options
20085  */
20086 Roo.form.Checkbox = function(config){
20087     Roo.form.Checkbox.superclass.constructor.call(this, config);
20088     this.addEvents({
20089         /**
20090          * @event check
20091          * Fires when the checkbox is checked or unchecked.
20092              * @param {Roo.form.Checkbox} this This checkbox
20093              * @param {Boolean} checked The new checked value
20094              */
20095         check : true
20096     });
20097 };
20098
20099 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20100     /**
20101      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20102      */
20103     focusClass : undefined,
20104     /**
20105      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20106      */
20107     fieldClass: "x-form-field",
20108     /**
20109      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20110      */
20111     checked: false,
20112     /**
20113      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20114      * {tag: "input", type: "checkbox", autocomplete: "off"})
20115      */
20116     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20117     /**
20118      * @cfg {String} boxLabel The text that appears beside the checkbox
20119      */
20120     boxLabel : "",
20121     /**
20122      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20123      */  
20124     inputValue : '1',
20125     /**
20126      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20127      */
20128      valueOff: '0', // value when not checked..
20129
20130     actionMode : 'viewEl', 
20131     //
20132     // private
20133     itemCls : 'x-menu-check-item x-form-item',
20134     groupClass : 'x-menu-group-item',
20135     inputType : 'hidden',
20136     
20137     
20138     inSetChecked: false, // check that we are not calling self...
20139     
20140     inputElement: false, // real input element?
20141     basedOn: false, // ????
20142     
20143     isFormField: true, // not sure where this is needed!!!!
20144
20145     onResize : function(){
20146         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20147         if(!this.boxLabel){
20148             this.el.alignTo(this.wrap, 'c-c');
20149         }
20150     },
20151
20152     initEvents : function(){
20153         Roo.form.Checkbox.superclass.initEvents.call(this);
20154         this.el.on("click", this.onClick,  this);
20155         this.el.on("change", this.onClick,  this);
20156     },
20157
20158
20159     getResizeEl : function(){
20160         return this.wrap;
20161     },
20162
20163     getPositionEl : function(){
20164         return this.wrap;
20165     },
20166
20167     // private
20168     onRender : function(ct, position){
20169         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20170         /*
20171         if(this.inputValue !== undefined){
20172             this.el.dom.value = this.inputValue;
20173         }
20174         */
20175         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20176         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20177         var viewEl = this.wrap.createChild({ 
20178             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20179         this.viewEl = viewEl;   
20180         this.wrap.on('click', this.onClick,  this); 
20181         
20182         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20183         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20184         
20185         
20186         
20187         if(this.boxLabel){
20188             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20189         //    viewEl.on('click', this.onClick,  this); 
20190         }
20191         //if(this.checked){
20192             this.setChecked(this.checked);
20193         //}else{
20194             //this.checked = this.el.dom;
20195         //}
20196
20197     },
20198
20199     // private
20200     initValue : Roo.emptyFn,
20201
20202     /**
20203      * Returns the checked state of the checkbox.
20204      * @return {Boolean} True if checked, else false
20205      */
20206     getValue : function(){
20207         if(this.el){
20208             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20209         }
20210         return this.valueOff;
20211         
20212     },
20213
20214         // private
20215     onClick : function(){ 
20216         if (this.disabled) {
20217             return;
20218         }
20219         this.setChecked(!this.checked);
20220
20221         //if(this.el.dom.checked != this.checked){
20222         //    this.setValue(this.el.dom.checked);
20223        // }
20224     },
20225
20226     /**
20227      * Sets the checked state of the checkbox.
20228      * On is always based on a string comparison between inputValue and the param.
20229      * @param {Boolean/String} value - the value to set 
20230      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20231      */
20232     setValue : function(v,suppressEvent){
20233         
20234         
20235         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20236         //if(this.el && this.el.dom){
20237         //    this.el.dom.checked = this.checked;
20238         //    this.el.dom.defaultChecked = this.checked;
20239         //}
20240         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20241         //this.fireEvent("check", this, this.checked);
20242     },
20243     // private..
20244     setChecked : function(state,suppressEvent)
20245     {
20246         if (this.inSetChecked) {
20247             this.checked = state;
20248             return;
20249         }
20250         
20251     
20252         if(this.wrap){
20253             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20254         }
20255         this.checked = state;
20256         if(suppressEvent !== true){
20257             this.fireEvent('check', this, state);
20258         }
20259         this.inSetChecked = true;
20260         this.el.dom.value = state ? this.inputValue : this.valueOff;
20261         this.inSetChecked = false;
20262         
20263     },
20264     // handle setting of hidden value by some other method!!?!?
20265     setFromHidden: function()
20266     {
20267         if(!this.el){
20268             return;
20269         }
20270         //console.log("SET FROM HIDDEN");
20271         //alert('setFrom hidden');
20272         this.setValue(this.el.dom.value);
20273     },
20274     
20275     onDestroy : function()
20276     {
20277         if(this.viewEl){
20278             Roo.get(this.viewEl).remove();
20279         }
20280          
20281         Roo.form.Checkbox.superclass.onDestroy.call(this);
20282     },
20283     
20284     setBoxLabel : function(str)
20285     {
20286         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20287     }
20288
20289 });/*
20290  * Based on:
20291  * Ext JS Library 1.1.1
20292  * Copyright(c) 2006-2007, Ext JS, LLC.
20293  *
20294  * Originally Released Under LGPL - original licence link has changed is not relivant.
20295  *
20296  * Fork - LGPL
20297  * <script type="text/javascript">
20298  */
20299  
20300 /**
20301  * @class Roo.form.Radio
20302  * @extends Roo.form.Checkbox
20303  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20304  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20305  * @constructor
20306  * Creates a new Radio
20307  * @param {Object} config Configuration options
20308  */
20309 Roo.form.Radio = function(){
20310     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20311 };
20312 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20313     inputType: 'radio',
20314
20315     /**
20316      * If this radio is part of a group, it will return the selected value
20317      * @return {String}
20318      */
20319     getGroupValue : function(){
20320         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20321     },
20322     
20323     
20324     onRender : function(ct, position){
20325         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20326         
20327         if(this.inputValue !== undefined){
20328             this.el.dom.value = this.inputValue;
20329         }
20330          
20331         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20332         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20333         //var viewEl = this.wrap.createChild({ 
20334         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20335         //this.viewEl = viewEl;   
20336         //this.wrap.on('click', this.onClick,  this); 
20337         
20338         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20339         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20340         
20341         
20342         
20343         if(this.boxLabel){
20344             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20345         //    viewEl.on('click', this.onClick,  this); 
20346         }
20347          if(this.checked){
20348             this.el.dom.checked =   'checked' ;
20349         }
20350          
20351     } 
20352     
20353     
20354 });//<script type="text/javascript">
20355
20356 /*
20357  * Based  Ext JS Library 1.1.1
20358  * Copyright(c) 2006-2007, Ext JS, LLC.
20359  * LGPL
20360  *
20361  */
20362  
20363 /**
20364  * @class Roo.HtmlEditorCore
20365  * @extends Roo.Component
20366  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20367  *
20368  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20369  */
20370
20371 Roo.HtmlEditorCore = function(config){
20372     
20373     
20374     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20375     
20376     
20377     this.addEvents({
20378         /**
20379          * @event initialize
20380          * Fires when the editor is fully initialized (including the iframe)
20381          * @param {Roo.HtmlEditorCore} this
20382          */
20383         initialize: true,
20384         /**
20385          * @event activate
20386          * Fires when the editor is first receives the focus. Any insertion must wait
20387          * until after this event.
20388          * @param {Roo.HtmlEditorCore} this
20389          */
20390         activate: true,
20391          /**
20392          * @event beforesync
20393          * Fires before the textarea is updated with content from the editor iframe. Return false
20394          * to cancel the sync.
20395          * @param {Roo.HtmlEditorCore} this
20396          * @param {String} html
20397          */
20398         beforesync: true,
20399          /**
20400          * @event beforepush
20401          * Fires before the iframe editor is updated with content from the textarea. Return false
20402          * to cancel the push.
20403          * @param {Roo.HtmlEditorCore} this
20404          * @param {String} html
20405          */
20406         beforepush: true,
20407          /**
20408          * @event sync
20409          * Fires when the textarea is updated with content from the editor iframe.
20410          * @param {Roo.HtmlEditorCore} this
20411          * @param {String} html
20412          */
20413         sync: true,
20414          /**
20415          * @event push
20416          * Fires when the iframe editor is updated with content from the textarea.
20417          * @param {Roo.HtmlEditorCore} this
20418          * @param {String} html
20419          */
20420         push: true,
20421         
20422         /**
20423          * @event editorevent
20424          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20425          * @param {Roo.HtmlEditorCore} this
20426          */
20427         editorevent: true
20428         
20429     });
20430     
20431     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20432     
20433     // defaults : white / black...
20434     this.applyBlacklists();
20435     
20436     
20437     
20438 };
20439
20440
20441 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20442
20443
20444      /**
20445      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20446      */
20447     
20448     owner : false,
20449     
20450      /**
20451      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20452      *                        Roo.resizable.
20453      */
20454     resizable : false,
20455      /**
20456      * @cfg {Number} height (in pixels)
20457      */   
20458     height: 300,
20459    /**
20460      * @cfg {Number} width (in pixels)
20461      */   
20462     width: 500,
20463     
20464     /**
20465      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20466      * 
20467      */
20468     stylesheets: false,
20469     
20470     // id of frame..
20471     frameId: false,
20472     
20473     // private properties
20474     validationEvent : false,
20475     deferHeight: true,
20476     initialized : false,
20477     activated : false,
20478     sourceEditMode : false,
20479     onFocus : Roo.emptyFn,
20480     iframePad:3,
20481     hideMode:'offsets',
20482     
20483     clearUp: true,
20484     
20485     // blacklist + whitelisted elements..
20486     black: false,
20487     white: false,
20488      
20489     
20490
20491     /**
20492      * Protected method that will not generally be called directly. It
20493      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20494      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20495      */
20496     getDocMarkup : function(){
20497         // body styles..
20498         var st = '';
20499         
20500         // inherit styels from page...?? 
20501         if (this.stylesheets === false) {
20502             
20503             Roo.get(document.head).select('style').each(function(node) {
20504                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20505             });
20506             
20507             Roo.get(document.head).select('link').each(function(node) { 
20508                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20509             });
20510             
20511         } else if (!this.stylesheets.length) {
20512                 // simple..
20513                 st = '<style type="text/css">' +
20514                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20515                    '</style>';
20516         } else { 
20517             st = '<style type="text/css">' +
20518                     this.stylesheets +
20519                 '</style>';
20520         }
20521         
20522         st +=  '<style type="text/css">' +
20523             'IMG { cursor: pointer } ' +
20524         '</style>';
20525
20526         var cls = 'roo-htmleditor-body';
20527         
20528         if(this.bodyCls.length){
20529             cls += ' ' + this.bodyCls;
20530         }
20531         
20532         return '<html><head>' + st  +
20533             //<style type="text/css">' +
20534             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20535             //'</style>' +
20536             ' </head><body class="' +  cls + '"></body></html>';
20537     },
20538
20539     // private
20540     onRender : function(ct, position)
20541     {
20542         var _t = this;
20543         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20544         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20545         
20546         
20547         this.el.dom.style.border = '0 none';
20548         this.el.dom.setAttribute('tabIndex', -1);
20549         this.el.addClass('x-hidden hide');
20550         
20551         
20552         
20553         if(Roo.isIE){ // fix IE 1px bogus margin
20554             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20555         }
20556        
20557         
20558         this.frameId = Roo.id();
20559         
20560          
20561         
20562         var iframe = this.owner.wrap.createChild({
20563             tag: 'iframe',
20564             cls: 'form-control', // bootstrap..
20565             id: this.frameId,
20566             name: this.frameId,
20567             frameBorder : 'no',
20568             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20569         }, this.el
20570         );
20571         
20572         
20573         this.iframe = iframe.dom;
20574
20575          this.assignDocWin();
20576         
20577         this.doc.designMode = 'on';
20578        
20579         this.doc.open();
20580         this.doc.write(this.getDocMarkup());
20581         this.doc.close();
20582
20583         
20584         var task = { // must defer to wait for browser to be ready
20585             run : function(){
20586                 //console.log("run task?" + this.doc.readyState);
20587                 this.assignDocWin();
20588                 if(this.doc.body || this.doc.readyState == 'complete'){
20589                     try {
20590                         this.doc.designMode="on";
20591                     } catch (e) {
20592                         return;
20593                     }
20594                     Roo.TaskMgr.stop(task);
20595                     this.initEditor.defer(10, this);
20596                 }
20597             },
20598             interval : 10,
20599             duration: 10000,
20600             scope: this
20601         };
20602         Roo.TaskMgr.start(task);
20603
20604     },
20605
20606     // private
20607     onResize : function(w, h)
20608     {
20609          Roo.log('resize: ' +w + ',' + h );
20610         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20611         if(!this.iframe){
20612             return;
20613         }
20614         if(typeof w == 'number'){
20615             
20616             this.iframe.style.width = w + 'px';
20617         }
20618         if(typeof h == 'number'){
20619             
20620             this.iframe.style.height = h + 'px';
20621             if(this.doc){
20622                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20623             }
20624         }
20625         
20626     },
20627
20628     /**
20629      * Toggles the editor between standard and source edit mode.
20630      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20631      */
20632     toggleSourceEdit : function(sourceEditMode){
20633         
20634         this.sourceEditMode = sourceEditMode === true;
20635         
20636         if(this.sourceEditMode){
20637  
20638             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20639             
20640         }else{
20641             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20642             //this.iframe.className = '';
20643             this.deferFocus();
20644         }
20645         //this.setSize(this.owner.wrap.getSize());
20646         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20647     },
20648
20649     
20650   
20651
20652     /**
20653      * Protected method that will not generally be called directly. If you need/want
20654      * custom HTML cleanup, this is the method you should override.
20655      * @param {String} html The HTML to be cleaned
20656      * return {String} The cleaned HTML
20657      */
20658     cleanHtml : function(html){
20659         html = String(html);
20660         if(html.length > 5){
20661             if(Roo.isSafari){ // strip safari nonsense
20662                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20663             }
20664         }
20665         if(html == '&nbsp;'){
20666             html = '';
20667         }
20668         return html;
20669     },
20670
20671     /**
20672      * HTML Editor -> Textarea
20673      * Protected method that will not generally be called directly. Syncs the contents
20674      * of the editor iframe with the textarea.
20675      */
20676     syncValue : function(){
20677         if(this.initialized){
20678             var bd = (this.doc.body || this.doc.documentElement);
20679             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20680             var html = bd.innerHTML;
20681             if(Roo.isSafari){
20682                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20683                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20684                 if(m && m[1]){
20685                     html = '<div style="'+m[0]+'">' + html + '</div>';
20686                 }
20687             }
20688             html = this.cleanHtml(html);
20689             // fix up the special chars.. normaly like back quotes in word...
20690             // however we do not want to do this with chinese..
20691             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20692                 var cc = b.charCodeAt();
20693                 if (
20694                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20695                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20696                     (cc >= 0xf900 && cc < 0xfb00 )
20697                 ) {
20698                         return b;
20699                 }
20700                 return "&#"+cc+";" 
20701             });
20702             if(this.owner.fireEvent('beforesync', this, html) !== false){
20703                 this.el.dom.value = html;
20704                 this.owner.fireEvent('sync', this, html);
20705             }
20706         }
20707     },
20708
20709     /**
20710      * Protected method that will not generally be called directly. Pushes the value of the textarea
20711      * into the iframe editor.
20712      */
20713     pushValue : function(){
20714         if(this.initialized){
20715             var v = this.el.dom.value.trim();
20716             
20717 //            if(v.length < 1){
20718 //                v = '&#160;';
20719 //            }
20720             
20721             if(this.owner.fireEvent('beforepush', this, v) !== false){
20722                 var d = (this.doc.body || this.doc.documentElement);
20723                 d.innerHTML = v;
20724                 this.cleanUpPaste();
20725                 this.el.dom.value = d.innerHTML;
20726                 this.owner.fireEvent('push', this, v);
20727             }
20728         }
20729     },
20730
20731     // private
20732     deferFocus : function(){
20733         this.focus.defer(10, this);
20734     },
20735
20736     // doc'ed in Field
20737     focus : function(){
20738         if(this.win && !this.sourceEditMode){
20739             this.win.focus();
20740         }else{
20741             this.el.focus();
20742         }
20743     },
20744     
20745     assignDocWin: function()
20746     {
20747         var iframe = this.iframe;
20748         
20749          if(Roo.isIE){
20750             this.doc = iframe.contentWindow.document;
20751             this.win = iframe.contentWindow;
20752         } else {
20753 //            if (!Roo.get(this.frameId)) {
20754 //                return;
20755 //            }
20756 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20757 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20758             
20759             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20760                 return;
20761             }
20762             
20763             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20764             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20765         }
20766     },
20767     
20768     // private
20769     initEditor : function(){
20770         //console.log("INIT EDITOR");
20771         this.assignDocWin();
20772         
20773         
20774         
20775         this.doc.designMode="on";
20776         this.doc.open();
20777         this.doc.write(this.getDocMarkup());
20778         this.doc.close();
20779         
20780         var dbody = (this.doc.body || this.doc.documentElement);
20781         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20782         // this copies styles from the containing element into thsi one..
20783         // not sure why we need all of this..
20784         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20785         
20786         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20787         //ss['background-attachment'] = 'fixed'; // w3c
20788         dbody.bgProperties = 'fixed'; // ie
20789         //Roo.DomHelper.applyStyles(dbody, ss);
20790         Roo.EventManager.on(this.doc, {
20791             //'mousedown': this.onEditorEvent,
20792             'mouseup': this.onEditorEvent,
20793             'dblclick': this.onEditorEvent,
20794             'click': this.onEditorEvent,
20795             'keyup': this.onEditorEvent,
20796             buffer:100,
20797             scope: this
20798         });
20799         if(Roo.isGecko){
20800             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20801         }
20802         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20803             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20804         }
20805         this.initialized = true;
20806
20807         this.owner.fireEvent('initialize', this);
20808         this.pushValue();
20809     },
20810
20811     // private
20812     onDestroy : function(){
20813         
20814         
20815         
20816         if(this.rendered){
20817             
20818             //for (var i =0; i < this.toolbars.length;i++) {
20819             //    // fixme - ask toolbars for heights?
20820             //    this.toolbars[i].onDestroy();
20821            // }
20822             
20823             //this.wrap.dom.innerHTML = '';
20824             //this.wrap.remove();
20825         }
20826     },
20827
20828     // private
20829     onFirstFocus : function(){
20830         
20831         this.assignDocWin();
20832         
20833         
20834         this.activated = true;
20835          
20836     
20837         if(Roo.isGecko){ // prevent silly gecko errors
20838             this.win.focus();
20839             var s = this.win.getSelection();
20840             if(!s.focusNode || s.focusNode.nodeType != 3){
20841                 var r = s.getRangeAt(0);
20842                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20843                 r.collapse(true);
20844                 this.deferFocus();
20845             }
20846             try{
20847                 this.execCmd('useCSS', true);
20848                 this.execCmd('styleWithCSS', false);
20849             }catch(e){}
20850         }
20851         this.owner.fireEvent('activate', this);
20852     },
20853
20854     // private
20855     adjustFont: function(btn){
20856         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20857         //if(Roo.isSafari){ // safari
20858         //    adjust *= 2;
20859        // }
20860         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20861         if(Roo.isSafari){ // safari
20862             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20863             v =  (v < 10) ? 10 : v;
20864             v =  (v > 48) ? 48 : v;
20865             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20866             
20867         }
20868         
20869         
20870         v = Math.max(1, v+adjust);
20871         
20872         this.execCmd('FontSize', v  );
20873     },
20874
20875     onEditorEvent : function(e)
20876     {
20877         this.owner.fireEvent('editorevent', this, e);
20878       //  this.updateToolbar();
20879         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20880     },
20881
20882     insertTag : function(tg)
20883     {
20884         // could be a bit smarter... -> wrap the current selected tRoo..
20885         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20886             
20887             range = this.createRange(this.getSelection());
20888             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20889             wrappingNode.appendChild(range.extractContents());
20890             range.insertNode(wrappingNode);
20891
20892             return;
20893             
20894             
20895             
20896         }
20897         this.execCmd("formatblock",   tg);
20898         
20899     },
20900     
20901     insertText : function(txt)
20902     {
20903         
20904         
20905         var range = this.createRange();
20906         range.deleteContents();
20907                //alert(Sender.getAttribute('label'));
20908                
20909         range.insertNode(this.doc.createTextNode(txt));
20910     } ,
20911     
20912      
20913
20914     /**
20915      * Executes a Midas editor command on the editor document and performs necessary focus and
20916      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20917      * @param {String} cmd The Midas command
20918      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20919      */
20920     relayCmd : function(cmd, value){
20921         this.win.focus();
20922         this.execCmd(cmd, value);
20923         this.owner.fireEvent('editorevent', this);
20924         //this.updateToolbar();
20925         this.owner.deferFocus();
20926     },
20927
20928     /**
20929      * Executes a Midas editor command directly on the editor document.
20930      * For visual commands, you should use {@link #relayCmd} instead.
20931      * <b>This should only be called after the editor is initialized.</b>
20932      * @param {String} cmd The Midas command
20933      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20934      */
20935     execCmd : function(cmd, value){
20936         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20937         this.syncValue();
20938     },
20939  
20940  
20941    
20942     /**
20943      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20944      * to insert tRoo.
20945      * @param {String} text | dom node.. 
20946      */
20947     insertAtCursor : function(text)
20948     {
20949         
20950         if(!this.activated){
20951             return;
20952         }
20953         /*
20954         if(Roo.isIE){
20955             this.win.focus();
20956             var r = this.doc.selection.createRange();
20957             if(r){
20958                 r.collapse(true);
20959                 r.pasteHTML(text);
20960                 this.syncValue();
20961                 this.deferFocus();
20962             
20963             }
20964             return;
20965         }
20966         */
20967         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20968             this.win.focus();
20969             
20970             
20971             // from jquery ui (MIT licenced)
20972             var range, node;
20973             var win = this.win;
20974             
20975             if (win.getSelection && win.getSelection().getRangeAt) {
20976                 range = win.getSelection().getRangeAt(0);
20977                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20978                 range.insertNode(node);
20979             } else if (win.document.selection && win.document.selection.createRange) {
20980                 // no firefox support
20981                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20982                 win.document.selection.createRange().pasteHTML(txt);
20983             } else {
20984                 // no firefox support
20985                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20986                 this.execCmd('InsertHTML', txt);
20987             } 
20988             
20989             this.syncValue();
20990             
20991             this.deferFocus();
20992         }
20993     },
20994  // private
20995     mozKeyPress : function(e){
20996         if(e.ctrlKey){
20997             var c = e.getCharCode(), cmd;
20998           
20999             if(c > 0){
21000                 c = String.fromCharCode(c).toLowerCase();
21001                 switch(c){
21002                     case 'b':
21003                         cmd = 'bold';
21004                         break;
21005                     case 'i':
21006                         cmd = 'italic';
21007                         break;
21008                     
21009                     case 'u':
21010                         cmd = 'underline';
21011                         break;
21012                     
21013                     case 'v':
21014                         this.cleanUpPaste.defer(100, this);
21015                         return;
21016                         
21017                 }
21018                 if(cmd){
21019                     this.win.focus();
21020                     this.execCmd(cmd);
21021                     this.deferFocus();
21022                     e.preventDefault();
21023                 }
21024                 
21025             }
21026         }
21027     },
21028
21029     // private
21030     fixKeys : function(){ // load time branching for fastest keydown performance
21031         if(Roo.isIE){
21032             return function(e){
21033                 var k = e.getKey(), r;
21034                 if(k == e.TAB){
21035                     e.stopEvent();
21036                     r = this.doc.selection.createRange();
21037                     if(r){
21038                         r.collapse(true);
21039                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21040                         this.deferFocus();
21041                     }
21042                     return;
21043                 }
21044                 
21045                 if(k == e.ENTER){
21046                     r = this.doc.selection.createRange();
21047                     if(r){
21048                         var target = r.parentElement();
21049                         if(!target || target.tagName.toLowerCase() != 'li'){
21050                             e.stopEvent();
21051                             r.pasteHTML('<br />');
21052                             r.collapse(false);
21053                             r.select();
21054                         }
21055                     }
21056                 }
21057                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21058                     this.cleanUpPaste.defer(100, this);
21059                     return;
21060                 }
21061                 
21062                 
21063             };
21064         }else if(Roo.isOpera){
21065             return function(e){
21066                 var k = e.getKey();
21067                 if(k == e.TAB){
21068                     e.stopEvent();
21069                     this.win.focus();
21070                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21071                     this.deferFocus();
21072                 }
21073                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21074                     this.cleanUpPaste.defer(100, this);
21075                     return;
21076                 }
21077                 
21078             };
21079         }else if(Roo.isSafari){
21080             return function(e){
21081                 var k = e.getKey();
21082                 
21083                 if(k == e.TAB){
21084                     e.stopEvent();
21085                     this.execCmd('InsertText','\t');
21086                     this.deferFocus();
21087                     return;
21088                 }
21089                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21090                     this.cleanUpPaste.defer(100, this);
21091                     return;
21092                 }
21093                 
21094              };
21095         }
21096     }(),
21097     
21098     getAllAncestors: function()
21099     {
21100         var p = this.getSelectedNode();
21101         var a = [];
21102         if (!p) {
21103             a.push(p); // push blank onto stack..
21104             p = this.getParentElement();
21105         }
21106         
21107         
21108         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21109             a.push(p);
21110             p = p.parentNode;
21111         }
21112         a.push(this.doc.body);
21113         return a;
21114     },
21115     lastSel : false,
21116     lastSelNode : false,
21117     
21118     
21119     getSelection : function() 
21120     {
21121         this.assignDocWin();
21122         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21123     },
21124     
21125     getSelectedNode: function() 
21126     {
21127         // this may only work on Gecko!!!
21128         
21129         // should we cache this!!!!
21130         
21131         
21132         
21133          
21134         var range = this.createRange(this.getSelection()).cloneRange();
21135         
21136         if (Roo.isIE) {
21137             var parent = range.parentElement();
21138             while (true) {
21139                 var testRange = range.duplicate();
21140                 testRange.moveToElementText(parent);
21141                 if (testRange.inRange(range)) {
21142                     break;
21143                 }
21144                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21145                     break;
21146                 }
21147                 parent = parent.parentElement;
21148             }
21149             return parent;
21150         }
21151         
21152         // is ancestor a text element.
21153         var ac =  range.commonAncestorContainer;
21154         if (ac.nodeType == 3) {
21155             ac = ac.parentNode;
21156         }
21157         
21158         var ar = ac.childNodes;
21159          
21160         var nodes = [];
21161         var other_nodes = [];
21162         var has_other_nodes = false;
21163         for (var i=0;i<ar.length;i++) {
21164             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21165                 continue;
21166             }
21167             // fullly contained node.
21168             
21169             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21170                 nodes.push(ar[i]);
21171                 continue;
21172             }
21173             
21174             // probably selected..
21175             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21176                 other_nodes.push(ar[i]);
21177                 continue;
21178             }
21179             // outer..
21180             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21181                 continue;
21182             }
21183             
21184             
21185             has_other_nodes = true;
21186         }
21187         if (!nodes.length && other_nodes.length) {
21188             nodes= other_nodes;
21189         }
21190         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21191             return false;
21192         }
21193         
21194         return nodes[0];
21195     },
21196     createRange: function(sel)
21197     {
21198         // this has strange effects when using with 
21199         // top toolbar - not sure if it's a great idea.
21200         //this.editor.contentWindow.focus();
21201         if (typeof sel != "undefined") {
21202             try {
21203                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21204             } catch(e) {
21205                 return this.doc.createRange();
21206             }
21207         } else {
21208             return this.doc.createRange();
21209         }
21210     },
21211     getParentElement: function()
21212     {
21213         
21214         this.assignDocWin();
21215         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21216         
21217         var range = this.createRange(sel);
21218          
21219         try {
21220             var p = range.commonAncestorContainer;
21221             while (p.nodeType == 3) { // text node
21222                 p = p.parentNode;
21223             }
21224             return p;
21225         } catch (e) {
21226             return null;
21227         }
21228     
21229     },
21230     /***
21231      *
21232      * Range intersection.. the hard stuff...
21233      *  '-1' = before
21234      *  '0' = hits..
21235      *  '1' = after.
21236      *         [ -- selected range --- ]
21237      *   [fail]                        [fail]
21238      *
21239      *    basically..
21240      *      if end is before start or  hits it. fail.
21241      *      if start is after end or hits it fail.
21242      *
21243      *   if either hits (but other is outside. - then it's not 
21244      *   
21245      *    
21246      **/
21247     
21248     
21249     // @see http://www.thismuchiknow.co.uk/?p=64.
21250     rangeIntersectsNode : function(range, node)
21251     {
21252         var nodeRange = node.ownerDocument.createRange();
21253         try {
21254             nodeRange.selectNode(node);
21255         } catch (e) {
21256             nodeRange.selectNodeContents(node);
21257         }
21258     
21259         var rangeStartRange = range.cloneRange();
21260         rangeStartRange.collapse(true);
21261     
21262         var rangeEndRange = range.cloneRange();
21263         rangeEndRange.collapse(false);
21264     
21265         var nodeStartRange = nodeRange.cloneRange();
21266         nodeStartRange.collapse(true);
21267     
21268         var nodeEndRange = nodeRange.cloneRange();
21269         nodeEndRange.collapse(false);
21270     
21271         return rangeStartRange.compareBoundaryPoints(
21272                  Range.START_TO_START, nodeEndRange) == -1 &&
21273                rangeEndRange.compareBoundaryPoints(
21274                  Range.START_TO_START, nodeStartRange) == 1;
21275         
21276          
21277     },
21278     rangeCompareNode : function(range, node)
21279     {
21280         var nodeRange = node.ownerDocument.createRange();
21281         try {
21282             nodeRange.selectNode(node);
21283         } catch (e) {
21284             nodeRange.selectNodeContents(node);
21285         }
21286         
21287         
21288         range.collapse(true);
21289     
21290         nodeRange.collapse(true);
21291      
21292         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21293         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21294          
21295         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21296         
21297         var nodeIsBefore   =  ss == 1;
21298         var nodeIsAfter    = ee == -1;
21299         
21300         if (nodeIsBefore && nodeIsAfter) {
21301             return 0; // outer
21302         }
21303         if (!nodeIsBefore && nodeIsAfter) {
21304             return 1; //right trailed.
21305         }
21306         
21307         if (nodeIsBefore && !nodeIsAfter) {
21308             return 2;  // left trailed.
21309         }
21310         // fully contined.
21311         return 3;
21312     },
21313
21314     // private? - in a new class?
21315     cleanUpPaste :  function()
21316     {
21317         // cleans up the whole document..
21318         Roo.log('cleanuppaste');
21319         
21320         this.cleanUpChildren(this.doc.body);
21321         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21322         if (clean != this.doc.body.innerHTML) {
21323             this.doc.body.innerHTML = clean;
21324         }
21325         
21326     },
21327     
21328     cleanWordChars : function(input) {// change the chars to hex code
21329         var he = Roo.HtmlEditorCore;
21330         
21331         var output = input;
21332         Roo.each(he.swapCodes, function(sw) { 
21333             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21334             
21335             output = output.replace(swapper, sw[1]);
21336         });
21337         
21338         return output;
21339     },
21340     
21341     
21342     cleanUpChildren : function (n)
21343     {
21344         if (!n.childNodes.length) {
21345             return;
21346         }
21347         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21348            this.cleanUpChild(n.childNodes[i]);
21349         }
21350     },
21351     
21352     
21353         
21354     
21355     cleanUpChild : function (node)
21356     {
21357         var ed = this;
21358         //console.log(node);
21359         if (node.nodeName == "#text") {
21360             // clean up silly Windows -- stuff?
21361             return; 
21362         }
21363         if (node.nodeName == "#comment") {
21364             node.parentNode.removeChild(node);
21365             // clean up silly Windows -- stuff?
21366             return; 
21367         }
21368         var lcname = node.tagName.toLowerCase();
21369         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21370         // whitelist of tags..
21371         
21372         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21373             // remove node.
21374             node.parentNode.removeChild(node);
21375             return;
21376             
21377         }
21378         
21379         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21380         
21381         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21382         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21383         
21384         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21385         //    remove_keep_children = true;
21386         //}
21387         
21388         if (remove_keep_children) {
21389             this.cleanUpChildren(node);
21390             // inserts everything just before this node...
21391             while (node.childNodes.length) {
21392                 var cn = node.childNodes[0];
21393                 node.removeChild(cn);
21394                 node.parentNode.insertBefore(cn, node);
21395             }
21396             node.parentNode.removeChild(node);
21397             return;
21398         }
21399         
21400         if (!node.attributes || !node.attributes.length) {
21401             this.cleanUpChildren(node);
21402             return;
21403         }
21404         
21405         function cleanAttr(n,v)
21406         {
21407             
21408             if (v.match(/^\./) || v.match(/^\//)) {
21409                 return;
21410             }
21411             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21412                 return;
21413             }
21414             if (v.match(/^#/)) {
21415                 return;
21416             }
21417 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21418             node.removeAttribute(n);
21419             
21420         }
21421         
21422         var cwhite = this.cwhite;
21423         var cblack = this.cblack;
21424             
21425         function cleanStyle(n,v)
21426         {
21427             if (v.match(/expression/)) { //XSS?? should we even bother..
21428                 node.removeAttribute(n);
21429                 return;
21430             }
21431             
21432             var parts = v.split(/;/);
21433             var clean = [];
21434             
21435             Roo.each(parts, function(p) {
21436                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21437                 if (!p.length) {
21438                     return true;
21439                 }
21440                 var l = p.split(':').shift().replace(/\s+/g,'');
21441                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21442                 
21443                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21444 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21445                     //node.removeAttribute(n);
21446                     return true;
21447                 }
21448                 //Roo.log()
21449                 // only allow 'c whitelisted system attributes'
21450                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21451 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21452                     //node.removeAttribute(n);
21453                     return true;
21454                 }
21455                 
21456                 
21457                  
21458                 
21459                 clean.push(p);
21460                 return true;
21461             });
21462             if (clean.length) { 
21463                 node.setAttribute(n, clean.join(';'));
21464             } else {
21465                 node.removeAttribute(n);
21466             }
21467             
21468         }
21469         
21470         
21471         for (var i = node.attributes.length-1; i > -1 ; i--) {
21472             var a = node.attributes[i];
21473             //console.log(a);
21474             
21475             if (a.name.toLowerCase().substr(0,2)=='on')  {
21476                 node.removeAttribute(a.name);
21477                 continue;
21478             }
21479             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21480                 node.removeAttribute(a.name);
21481                 continue;
21482             }
21483             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21484                 cleanAttr(a.name,a.value); // fixme..
21485                 continue;
21486             }
21487             if (a.name == 'style') {
21488                 cleanStyle(a.name,a.value);
21489                 continue;
21490             }
21491             /// clean up MS crap..
21492             // tecnically this should be a list of valid class'es..
21493             
21494             
21495             if (a.name == 'class') {
21496                 if (a.value.match(/^Mso/)) {
21497                     node.className = '';
21498                 }
21499                 
21500                 if (a.value.match(/^body$/)) {
21501                     node.className = '';
21502                 }
21503                 continue;
21504             }
21505             
21506             // style cleanup!?
21507             // class cleanup?
21508             
21509         }
21510         
21511         
21512         this.cleanUpChildren(node);
21513         
21514         
21515     },
21516     
21517     /**
21518      * Clean up MS wordisms...
21519      */
21520     cleanWord : function(node)
21521     {
21522         
21523         
21524         if (!node) {
21525             this.cleanWord(this.doc.body);
21526             return;
21527         }
21528         if (node.nodeName == "#text") {
21529             // clean up silly Windows -- stuff?
21530             return; 
21531         }
21532         if (node.nodeName == "#comment") {
21533             node.parentNode.removeChild(node);
21534             // clean up silly Windows -- stuff?
21535             return; 
21536         }
21537         
21538         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21539             node.parentNode.removeChild(node);
21540             return;
21541         }
21542         
21543         // remove - but keep children..
21544         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21545             while (node.childNodes.length) {
21546                 var cn = node.childNodes[0];
21547                 node.removeChild(cn);
21548                 node.parentNode.insertBefore(cn, node);
21549             }
21550             node.parentNode.removeChild(node);
21551             this.iterateChildren(node, this.cleanWord);
21552             return;
21553         }
21554         // clean styles
21555         if (node.className.length) {
21556             
21557             var cn = node.className.split(/\W+/);
21558             var cna = [];
21559             Roo.each(cn, function(cls) {
21560                 if (cls.match(/Mso[a-zA-Z]+/)) {
21561                     return;
21562                 }
21563                 cna.push(cls);
21564             });
21565             node.className = cna.length ? cna.join(' ') : '';
21566             if (!cna.length) {
21567                 node.removeAttribute("class");
21568             }
21569         }
21570         
21571         if (node.hasAttribute("lang")) {
21572             node.removeAttribute("lang");
21573         }
21574         
21575         if (node.hasAttribute("style")) {
21576             
21577             var styles = node.getAttribute("style").split(";");
21578             var nstyle = [];
21579             Roo.each(styles, function(s) {
21580                 if (!s.match(/:/)) {
21581                     return;
21582                 }
21583                 var kv = s.split(":");
21584                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21585                     return;
21586                 }
21587                 // what ever is left... we allow.
21588                 nstyle.push(s);
21589             });
21590             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21591             if (!nstyle.length) {
21592                 node.removeAttribute('style');
21593             }
21594         }
21595         this.iterateChildren(node, this.cleanWord);
21596         
21597         
21598         
21599     },
21600     /**
21601      * iterateChildren of a Node, calling fn each time, using this as the scole..
21602      * @param {DomNode} node node to iterate children of.
21603      * @param {Function} fn method of this class to call on each item.
21604      */
21605     iterateChildren : function(node, fn)
21606     {
21607         if (!node.childNodes.length) {
21608                 return;
21609         }
21610         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21611            fn.call(this, node.childNodes[i])
21612         }
21613     },
21614     
21615     
21616     /**
21617      * cleanTableWidths.
21618      *
21619      * Quite often pasting from word etc.. results in tables with column and widths.
21620      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21621      *
21622      */
21623     cleanTableWidths : function(node)
21624     {
21625          
21626          
21627         if (!node) {
21628             this.cleanTableWidths(this.doc.body);
21629             return;
21630         }
21631         
21632         // ignore list...
21633         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21634             return; 
21635         }
21636         Roo.log(node.tagName);
21637         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21638             this.iterateChildren(node, this.cleanTableWidths);
21639             return;
21640         }
21641         if (node.hasAttribute('width')) {
21642             node.removeAttribute('width');
21643         }
21644         
21645          
21646         if (node.hasAttribute("style")) {
21647             // pretty basic...
21648             
21649             var styles = node.getAttribute("style").split(";");
21650             var nstyle = [];
21651             Roo.each(styles, function(s) {
21652                 if (!s.match(/:/)) {
21653                     return;
21654                 }
21655                 var kv = s.split(":");
21656                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21657                     return;
21658                 }
21659                 // what ever is left... we allow.
21660                 nstyle.push(s);
21661             });
21662             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21663             if (!nstyle.length) {
21664                 node.removeAttribute('style');
21665             }
21666         }
21667         
21668         this.iterateChildren(node, this.cleanTableWidths);
21669         
21670         
21671     },
21672     
21673     
21674     
21675     
21676     domToHTML : function(currentElement, depth, nopadtext) {
21677         
21678         depth = depth || 0;
21679         nopadtext = nopadtext || false;
21680     
21681         if (!currentElement) {
21682             return this.domToHTML(this.doc.body);
21683         }
21684         
21685         //Roo.log(currentElement);
21686         var j;
21687         var allText = false;
21688         var nodeName = currentElement.nodeName;
21689         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21690         
21691         if  (nodeName == '#text') {
21692             
21693             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21694         }
21695         
21696         
21697         var ret = '';
21698         if (nodeName != 'BODY') {
21699              
21700             var i = 0;
21701             // Prints the node tagName, such as <A>, <IMG>, etc
21702             if (tagName) {
21703                 var attr = [];
21704                 for(i = 0; i < currentElement.attributes.length;i++) {
21705                     // quoting?
21706                     var aname = currentElement.attributes.item(i).name;
21707                     if (!currentElement.attributes.item(i).value.length) {
21708                         continue;
21709                     }
21710                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21711                 }
21712                 
21713                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21714             } 
21715             else {
21716                 
21717                 // eack
21718             }
21719         } else {
21720             tagName = false;
21721         }
21722         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21723             return ret;
21724         }
21725         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21726             nopadtext = true;
21727         }
21728         
21729         
21730         // Traverse the tree
21731         i = 0;
21732         var currentElementChild = currentElement.childNodes.item(i);
21733         var allText = true;
21734         var innerHTML  = '';
21735         lastnode = '';
21736         while (currentElementChild) {
21737             // Formatting code (indent the tree so it looks nice on the screen)
21738             var nopad = nopadtext;
21739             if (lastnode == 'SPAN') {
21740                 nopad  = true;
21741             }
21742             // text
21743             if  (currentElementChild.nodeName == '#text') {
21744                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21745                 toadd = nopadtext ? toadd : toadd.trim();
21746                 if (!nopad && toadd.length > 80) {
21747                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21748                 }
21749                 innerHTML  += toadd;
21750                 
21751                 i++;
21752                 currentElementChild = currentElement.childNodes.item(i);
21753                 lastNode = '';
21754                 continue;
21755             }
21756             allText = false;
21757             
21758             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21759                 
21760             // Recursively traverse the tree structure of the child node
21761             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21762             lastnode = currentElementChild.nodeName;
21763             i++;
21764             currentElementChild=currentElement.childNodes.item(i);
21765         }
21766         
21767         ret += innerHTML;
21768         
21769         if (!allText) {
21770                 // The remaining code is mostly for formatting the tree
21771             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21772         }
21773         
21774         
21775         if (tagName) {
21776             ret+= "</"+tagName+">";
21777         }
21778         return ret;
21779         
21780     },
21781         
21782     applyBlacklists : function()
21783     {
21784         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21785         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21786         
21787         this.white = [];
21788         this.black = [];
21789         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21790             if (b.indexOf(tag) > -1) {
21791                 return;
21792             }
21793             this.white.push(tag);
21794             
21795         }, this);
21796         
21797         Roo.each(w, function(tag) {
21798             if (b.indexOf(tag) > -1) {
21799                 return;
21800             }
21801             if (this.white.indexOf(tag) > -1) {
21802                 return;
21803             }
21804             this.white.push(tag);
21805             
21806         }, this);
21807         
21808         
21809         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21810             if (w.indexOf(tag) > -1) {
21811                 return;
21812             }
21813             this.black.push(tag);
21814             
21815         }, this);
21816         
21817         Roo.each(b, function(tag) {
21818             if (w.indexOf(tag) > -1) {
21819                 return;
21820             }
21821             if (this.black.indexOf(tag) > -1) {
21822                 return;
21823             }
21824             this.black.push(tag);
21825             
21826         }, this);
21827         
21828         
21829         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21830         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21831         
21832         this.cwhite = [];
21833         this.cblack = [];
21834         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21835             if (b.indexOf(tag) > -1) {
21836                 return;
21837             }
21838             this.cwhite.push(tag);
21839             
21840         }, this);
21841         
21842         Roo.each(w, function(tag) {
21843             if (b.indexOf(tag) > -1) {
21844                 return;
21845             }
21846             if (this.cwhite.indexOf(tag) > -1) {
21847                 return;
21848             }
21849             this.cwhite.push(tag);
21850             
21851         }, this);
21852         
21853         
21854         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21855             if (w.indexOf(tag) > -1) {
21856                 return;
21857             }
21858             this.cblack.push(tag);
21859             
21860         }, this);
21861         
21862         Roo.each(b, function(tag) {
21863             if (w.indexOf(tag) > -1) {
21864                 return;
21865             }
21866             if (this.cblack.indexOf(tag) > -1) {
21867                 return;
21868             }
21869             this.cblack.push(tag);
21870             
21871         }, this);
21872     },
21873     
21874     setStylesheets : function(stylesheets)
21875     {
21876         if(typeof(stylesheets) == 'string'){
21877             Roo.get(this.iframe.contentDocument.head).createChild({
21878                 tag : 'link',
21879                 rel : 'stylesheet',
21880                 type : 'text/css',
21881                 href : stylesheets
21882             });
21883             
21884             return;
21885         }
21886         var _this = this;
21887      
21888         Roo.each(stylesheets, function(s) {
21889             if(!s.length){
21890                 return;
21891             }
21892             
21893             Roo.get(_this.iframe.contentDocument.head).createChild({
21894                 tag : 'link',
21895                 rel : 'stylesheet',
21896                 type : 'text/css',
21897                 href : s
21898             });
21899         });
21900
21901         
21902     },
21903     
21904     removeStylesheets : function()
21905     {
21906         var _this = this;
21907         
21908         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21909             s.remove();
21910         });
21911     },
21912     
21913     setStyle : function(style)
21914     {
21915         Roo.get(this.iframe.contentDocument.head).createChild({
21916             tag : 'style',
21917             type : 'text/css',
21918             html : style
21919         });
21920
21921         return;
21922     }
21923     
21924     // hide stuff that is not compatible
21925     /**
21926      * @event blur
21927      * @hide
21928      */
21929     /**
21930      * @event change
21931      * @hide
21932      */
21933     /**
21934      * @event focus
21935      * @hide
21936      */
21937     /**
21938      * @event specialkey
21939      * @hide
21940      */
21941     /**
21942      * @cfg {String} fieldClass @hide
21943      */
21944     /**
21945      * @cfg {String} focusClass @hide
21946      */
21947     /**
21948      * @cfg {String} autoCreate @hide
21949      */
21950     /**
21951      * @cfg {String} inputType @hide
21952      */
21953     /**
21954      * @cfg {String} invalidClass @hide
21955      */
21956     /**
21957      * @cfg {String} invalidText @hide
21958      */
21959     /**
21960      * @cfg {String} msgFx @hide
21961      */
21962     /**
21963      * @cfg {String} validateOnBlur @hide
21964      */
21965 });
21966
21967 Roo.HtmlEditorCore.white = [
21968         'area', 'br', 'img', 'input', 'hr', 'wbr',
21969         
21970        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21971        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21972        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21973        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21974        'table',   'ul',         'xmp', 
21975        
21976        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21977       'thead',   'tr', 
21978      
21979       'dir', 'menu', 'ol', 'ul', 'dl',
21980        
21981       'embed',  'object'
21982 ];
21983
21984
21985 Roo.HtmlEditorCore.black = [
21986     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21987         'applet', // 
21988         'base',   'basefont', 'bgsound', 'blink',  'body', 
21989         'frame',  'frameset', 'head',    'html',   'ilayer', 
21990         'iframe', 'layer',  'link',     'meta',    'object',   
21991         'script', 'style' ,'title',  'xml' // clean later..
21992 ];
21993 Roo.HtmlEditorCore.clean = [
21994     'script', 'style', 'title', 'xml'
21995 ];
21996 Roo.HtmlEditorCore.remove = [
21997     'font'
21998 ];
21999 // attributes..
22000
22001 Roo.HtmlEditorCore.ablack = [
22002     'on'
22003 ];
22004     
22005 Roo.HtmlEditorCore.aclean = [ 
22006     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22007 ];
22008
22009 // protocols..
22010 Roo.HtmlEditorCore.pwhite= [
22011         'http',  'https',  'mailto'
22012 ];
22013
22014 // white listed style attributes.
22015 Roo.HtmlEditorCore.cwhite= [
22016       //  'text-align', /// default is to allow most things..
22017       
22018          
22019 //        'font-size'//??
22020 ];
22021
22022 // black listed style attributes.
22023 Roo.HtmlEditorCore.cblack= [
22024       //  'font-size' -- this can be set by the project 
22025 ];
22026
22027
22028 Roo.HtmlEditorCore.swapCodes   =[ 
22029     [    8211, "--" ], 
22030     [    8212, "--" ], 
22031     [    8216,  "'" ],  
22032     [    8217, "'" ],  
22033     [    8220, '"' ],  
22034     [    8221, '"' ],  
22035     [    8226, "*" ],  
22036     [    8230, "..." ]
22037 ]; 
22038
22039     //<script type="text/javascript">
22040
22041 /*
22042  * Ext JS Library 1.1.1
22043  * Copyright(c) 2006-2007, Ext JS, LLC.
22044  * Licence LGPL
22045  * 
22046  */
22047  
22048  
22049 Roo.form.HtmlEditor = function(config){
22050     
22051     
22052     
22053     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22054     
22055     if (!this.toolbars) {
22056         this.toolbars = [];
22057     }
22058     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22059     
22060     
22061 };
22062
22063 /**
22064  * @class Roo.form.HtmlEditor
22065  * @extends Roo.form.Field
22066  * Provides a lightweight HTML Editor component.
22067  *
22068  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22069  * 
22070  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22071  * supported by this editor.</b><br/><br/>
22072  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22073  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22074  */
22075 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22076     /**
22077      * @cfg {Boolean} clearUp
22078      */
22079     clearUp : true,
22080       /**
22081      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22082      */
22083     toolbars : false,
22084    
22085      /**
22086      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22087      *                        Roo.resizable.
22088      */
22089     resizable : false,
22090      /**
22091      * @cfg {Number} height (in pixels)
22092      */   
22093     height: 300,
22094    /**
22095      * @cfg {Number} width (in pixels)
22096      */   
22097     width: 500,
22098     
22099     /**
22100      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22101      * 
22102      */
22103     stylesheets: false,
22104     
22105     
22106      /**
22107      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22108      * 
22109      */
22110     cblack: false,
22111     /**
22112      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22113      * 
22114      */
22115     cwhite: false,
22116     
22117      /**
22118      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22119      * 
22120      */
22121     black: false,
22122     /**
22123      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22124      * 
22125      */
22126     white: false,
22127     
22128     // id of frame..
22129     frameId: false,
22130     
22131     // private properties
22132     validationEvent : false,
22133     deferHeight: true,
22134     initialized : false,
22135     activated : false,
22136     
22137     onFocus : Roo.emptyFn,
22138     iframePad:3,
22139     hideMode:'offsets',
22140     
22141     actionMode : 'container', // defaults to hiding it...
22142     
22143     defaultAutoCreate : { // modified by initCompnoent..
22144         tag: "textarea",
22145         style:"width:500px;height:300px;",
22146         autocomplete: "new-password"
22147     },
22148
22149     // private
22150     initComponent : function(){
22151         this.addEvents({
22152             /**
22153              * @event initialize
22154              * Fires when the editor is fully initialized (including the iframe)
22155              * @param {HtmlEditor} this
22156              */
22157             initialize: true,
22158             /**
22159              * @event activate
22160              * Fires when the editor is first receives the focus. Any insertion must wait
22161              * until after this event.
22162              * @param {HtmlEditor} this
22163              */
22164             activate: true,
22165              /**
22166              * @event beforesync
22167              * Fires before the textarea is updated with content from the editor iframe. Return false
22168              * to cancel the sync.
22169              * @param {HtmlEditor} this
22170              * @param {String} html
22171              */
22172             beforesync: true,
22173              /**
22174              * @event beforepush
22175              * Fires before the iframe editor is updated with content from the textarea. Return false
22176              * to cancel the push.
22177              * @param {HtmlEditor} this
22178              * @param {String} html
22179              */
22180             beforepush: true,
22181              /**
22182              * @event sync
22183              * Fires when the textarea is updated with content from the editor iframe.
22184              * @param {HtmlEditor} this
22185              * @param {String} html
22186              */
22187             sync: true,
22188              /**
22189              * @event push
22190              * Fires when the iframe editor is updated with content from the textarea.
22191              * @param {HtmlEditor} this
22192              * @param {String} html
22193              */
22194             push: true,
22195              /**
22196              * @event editmodechange
22197              * Fires when the editor switches edit modes
22198              * @param {HtmlEditor} this
22199              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22200              */
22201             editmodechange: true,
22202             /**
22203              * @event editorevent
22204              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22205              * @param {HtmlEditor} this
22206              */
22207             editorevent: true,
22208             /**
22209              * @event firstfocus
22210              * Fires when on first focus - needed by toolbars..
22211              * @param {HtmlEditor} this
22212              */
22213             firstfocus: true,
22214             /**
22215              * @event autosave
22216              * Auto save the htmlEditor value as a file into Events
22217              * @param {HtmlEditor} this
22218              */
22219             autosave: true,
22220             /**
22221              * @event savedpreview
22222              * preview the saved version of htmlEditor
22223              * @param {HtmlEditor} this
22224              */
22225             savedpreview: true,
22226             
22227             /**
22228             * @event stylesheetsclick
22229             * Fires when press the Sytlesheets button
22230             * @param {Roo.HtmlEditorCore} this
22231             */
22232             stylesheetsclick: true
22233         });
22234         this.defaultAutoCreate =  {
22235             tag: "textarea",
22236             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22237             autocomplete: "new-password"
22238         };
22239     },
22240
22241     /**
22242      * Protected method that will not generally be called directly. It
22243      * is called when the editor creates its toolbar. Override this method if you need to
22244      * add custom toolbar buttons.
22245      * @param {HtmlEditor} editor
22246      */
22247     createToolbar : function(editor){
22248         Roo.log("create toolbars");
22249         if (!editor.toolbars || !editor.toolbars.length) {
22250             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22251         }
22252         
22253         for (var i =0 ; i < editor.toolbars.length;i++) {
22254             editor.toolbars[i] = Roo.factory(
22255                     typeof(editor.toolbars[i]) == 'string' ?
22256                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22257                 Roo.form.HtmlEditor);
22258             editor.toolbars[i].init(editor);
22259         }
22260          
22261         
22262     },
22263
22264      
22265     // private
22266     onRender : function(ct, position)
22267     {
22268         var _t = this;
22269         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22270         
22271         this.wrap = this.el.wrap({
22272             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22273         });
22274         
22275         this.editorcore.onRender(ct, position);
22276          
22277         if (this.resizable) {
22278             this.resizeEl = new Roo.Resizable(this.wrap, {
22279                 pinned : true,
22280                 wrap: true,
22281                 dynamic : true,
22282                 minHeight : this.height,
22283                 height: this.height,
22284                 handles : this.resizable,
22285                 width: this.width,
22286                 listeners : {
22287                     resize : function(r, w, h) {
22288                         _t.onResize(w,h); // -something
22289                     }
22290                 }
22291             });
22292             
22293         }
22294         this.createToolbar(this);
22295        
22296         
22297         if(!this.width){
22298             this.setSize(this.wrap.getSize());
22299         }
22300         if (this.resizeEl) {
22301             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22302             // should trigger onReize..
22303         }
22304         
22305         this.keyNav = new Roo.KeyNav(this.el, {
22306             
22307             "tab" : function(e){
22308                 e.preventDefault();
22309                 
22310                 var value = this.getValue();
22311                 
22312                 var start = this.el.dom.selectionStart;
22313                 var end = this.el.dom.selectionEnd;
22314                 
22315                 if(!e.shiftKey){
22316                     
22317                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22318                     this.el.dom.setSelectionRange(end + 1, end + 1);
22319                     return;
22320                 }
22321                 
22322                 var f = value.substring(0, start).split("\t");
22323                 
22324                 if(f.pop().length != 0){
22325                     return;
22326                 }
22327                 
22328                 this.setValue(f.join("\t") + value.substring(end));
22329                 this.el.dom.setSelectionRange(start - 1, start - 1);
22330                 
22331             },
22332             
22333             "home" : function(e){
22334                 e.preventDefault();
22335                 
22336                 var curr = this.el.dom.selectionStart;
22337                 var lines = this.getValue().split("\n");
22338                 
22339                 if(!lines.length){
22340                     return;
22341                 }
22342                 
22343                 if(e.ctrlKey){
22344                     this.el.dom.setSelectionRange(0, 0);
22345                     return;
22346                 }
22347                 
22348                 var pos = 0;
22349                 
22350                 for (var i = 0; i < lines.length;i++) {
22351                     pos += lines[i].length;
22352                     
22353                     if(i != 0){
22354                         pos += 1;
22355                     }
22356                     
22357                     if(pos < curr){
22358                         continue;
22359                     }
22360                     
22361                     pos -= lines[i].length;
22362                     
22363                     break;
22364                 }
22365                 
22366                 if(!e.shiftKey){
22367                     this.el.dom.setSelectionRange(pos, pos);
22368                     return;
22369                 }
22370                 
22371                 this.el.dom.selectionStart = pos;
22372                 this.el.dom.selectionEnd = curr;
22373             },
22374             
22375             "end" : function(e){
22376                 e.preventDefault();
22377                 
22378                 var curr = this.el.dom.selectionStart;
22379                 var lines = this.getValue().split("\n");
22380                 
22381                 if(!lines.length){
22382                     return;
22383                 }
22384                 
22385                 if(e.ctrlKey){
22386                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22387                     return;
22388                 }
22389                 
22390                 var pos = 0;
22391                 
22392                 for (var i = 0; i < lines.length;i++) {
22393                     
22394                     pos += lines[i].length;
22395                     
22396                     if(i != 0){
22397                         pos += 1;
22398                     }
22399                     
22400                     if(pos < curr){
22401                         continue;
22402                     }
22403                     
22404                     break;
22405                 }
22406                 
22407                 if(!e.shiftKey){
22408                     this.el.dom.setSelectionRange(pos, pos);
22409                     return;
22410                 }
22411                 
22412                 this.el.dom.selectionStart = curr;
22413                 this.el.dom.selectionEnd = pos;
22414             },
22415
22416             scope : this,
22417
22418             doRelay : function(foo, bar, hname){
22419                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22420             },
22421
22422             forceKeyDown: true
22423         });
22424         
22425 //        if(this.autosave && this.w){
22426 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22427 //        }
22428     },
22429
22430     // private
22431     onResize : function(w, h)
22432     {
22433         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22434         var ew = false;
22435         var eh = false;
22436         
22437         if(this.el ){
22438             if(typeof w == 'number'){
22439                 var aw = w - this.wrap.getFrameWidth('lr');
22440                 this.el.setWidth(this.adjustWidth('textarea', aw));
22441                 ew = aw;
22442             }
22443             if(typeof h == 'number'){
22444                 var tbh = 0;
22445                 for (var i =0; i < this.toolbars.length;i++) {
22446                     // fixme - ask toolbars for heights?
22447                     tbh += this.toolbars[i].tb.el.getHeight();
22448                     if (this.toolbars[i].footer) {
22449                         tbh += this.toolbars[i].footer.el.getHeight();
22450                     }
22451                 }
22452                 
22453                 
22454                 
22455                 
22456                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22457                 ah -= 5; // knock a few pixes off for look..
22458 //                Roo.log(ah);
22459                 this.el.setHeight(this.adjustWidth('textarea', ah));
22460                 var eh = ah;
22461             }
22462         }
22463         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22464         this.editorcore.onResize(ew,eh);
22465         
22466     },
22467
22468     /**
22469      * Toggles the editor between standard and source edit mode.
22470      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22471      */
22472     toggleSourceEdit : function(sourceEditMode)
22473     {
22474         this.editorcore.toggleSourceEdit(sourceEditMode);
22475         
22476         if(this.editorcore.sourceEditMode){
22477             Roo.log('editor - showing textarea');
22478             
22479 //            Roo.log('in');
22480 //            Roo.log(this.syncValue());
22481             this.editorcore.syncValue();
22482             this.el.removeClass('x-hidden');
22483             this.el.dom.removeAttribute('tabIndex');
22484             this.el.focus();
22485             
22486             for (var i = 0; i < this.toolbars.length; i++) {
22487                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22488                     this.toolbars[i].tb.hide();
22489                     this.toolbars[i].footer.hide();
22490                 }
22491             }
22492             
22493         }else{
22494             Roo.log('editor - hiding textarea');
22495 //            Roo.log('out')
22496 //            Roo.log(this.pushValue()); 
22497             this.editorcore.pushValue();
22498             
22499             this.el.addClass('x-hidden');
22500             this.el.dom.setAttribute('tabIndex', -1);
22501             
22502             for (var i = 0; i < this.toolbars.length; i++) {
22503                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22504                     this.toolbars[i].tb.show();
22505                     this.toolbars[i].footer.show();
22506                 }
22507             }
22508             
22509             //this.deferFocus();
22510         }
22511         
22512         this.setSize(this.wrap.getSize());
22513         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22514         
22515         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22516     },
22517  
22518     // private (for BoxComponent)
22519     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22520
22521     // private (for BoxComponent)
22522     getResizeEl : function(){
22523         return this.wrap;
22524     },
22525
22526     // private (for BoxComponent)
22527     getPositionEl : function(){
22528         return this.wrap;
22529     },
22530
22531     // private
22532     initEvents : function(){
22533         this.originalValue = this.getValue();
22534     },
22535
22536     /**
22537      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22538      * @method
22539      */
22540     markInvalid : Roo.emptyFn,
22541     /**
22542      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22543      * @method
22544      */
22545     clearInvalid : Roo.emptyFn,
22546
22547     setValue : function(v){
22548         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22549         this.editorcore.pushValue();
22550     },
22551
22552      
22553     // private
22554     deferFocus : function(){
22555         this.focus.defer(10, this);
22556     },
22557
22558     // doc'ed in Field
22559     focus : function(){
22560         this.editorcore.focus();
22561         
22562     },
22563       
22564
22565     // private
22566     onDestroy : function(){
22567         
22568         
22569         
22570         if(this.rendered){
22571             
22572             for (var i =0; i < this.toolbars.length;i++) {
22573                 // fixme - ask toolbars for heights?
22574                 this.toolbars[i].onDestroy();
22575             }
22576             
22577             this.wrap.dom.innerHTML = '';
22578             this.wrap.remove();
22579         }
22580     },
22581
22582     // private
22583     onFirstFocus : function(){
22584         //Roo.log("onFirstFocus");
22585         this.editorcore.onFirstFocus();
22586          for (var i =0; i < this.toolbars.length;i++) {
22587             this.toolbars[i].onFirstFocus();
22588         }
22589         
22590     },
22591     
22592     // private
22593     syncValue : function()
22594     {
22595         this.editorcore.syncValue();
22596     },
22597     
22598     pushValue : function()
22599     {
22600         this.editorcore.pushValue();
22601     },
22602     
22603     setStylesheets : function(stylesheets)
22604     {
22605         this.editorcore.setStylesheets(stylesheets);
22606     },
22607     
22608     removeStylesheets : function()
22609     {
22610         this.editorcore.removeStylesheets();
22611     }
22612      
22613     
22614     // hide stuff that is not compatible
22615     /**
22616      * @event blur
22617      * @hide
22618      */
22619     /**
22620      * @event change
22621      * @hide
22622      */
22623     /**
22624      * @event focus
22625      * @hide
22626      */
22627     /**
22628      * @event specialkey
22629      * @hide
22630      */
22631     /**
22632      * @cfg {String} fieldClass @hide
22633      */
22634     /**
22635      * @cfg {String} focusClass @hide
22636      */
22637     /**
22638      * @cfg {String} autoCreate @hide
22639      */
22640     /**
22641      * @cfg {String} inputType @hide
22642      */
22643     /**
22644      * @cfg {String} invalidClass @hide
22645      */
22646     /**
22647      * @cfg {String} invalidText @hide
22648      */
22649     /**
22650      * @cfg {String} msgFx @hide
22651      */
22652     /**
22653      * @cfg {String} validateOnBlur @hide
22654      */
22655 });
22656  
22657     // <script type="text/javascript">
22658 /*
22659  * Based on
22660  * Ext JS Library 1.1.1
22661  * Copyright(c) 2006-2007, Ext JS, LLC.
22662  *  
22663  
22664  */
22665
22666 /**
22667  * @class Roo.form.HtmlEditorToolbar1
22668  * Basic Toolbar
22669  * 
22670  * Usage:
22671  *
22672  new Roo.form.HtmlEditor({
22673     ....
22674     toolbars : [
22675         new Roo.form.HtmlEditorToolbar1({
22676             disable : { fonts: 1 , format: 1, ..., ... , ...],
22677             btns : [ .... ]
22678         })
22679     }
22680      
22681  * 
22682  * @cfg {Object} disable List of elements to disable..
22683  * @cfg {Array} btns List of additional buttons.
22684  * 
22685  * 
22686  * NEEDS Extra CSS? 
22687  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22688  */
22689  
22690 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22691 {
22692     
22693     Roo.apply(this, config);
22694     
22695     // default disabled, based on 'good practice'..
22696     this.disable = this.disable || {};
22697     Roo.applyIf(this.disable, {
22698         fontSize : true,
22699         colors : true,
22700         specialElements : true
22701     });
22702     
22703     
22704     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22705     // dont call parent... till later.
22706 }
22707
22708 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22709     
22710     tb: false,
22711     
22712     rendered: false,
22713     
22714     editor : false,
22715     editorcore : false,
22716     /**
22717      * @cfg {Object} disable  List of toolbar elements to disable
22718          
22719      */
22720     disable : false,
22721     
22722     
22723      /**
22724      * @cfg {String} createLinkText The default text for the create link prompt
22725      */
22726     createLinkText : 'Please enter the URL for the link:',
22727     /**
22728      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22729      */
22730     defaultLinkValue : 'http:/'+'/',
22731    
22732     
22733       /**
22734      * @cfg {Array} fontFamilies An array of available font families
22735      */
22736     fontFamilies : [
22737         'Arial',
22738         'Courier New',
22739         'Tahoma',
22740         'Times New Roman',
22741         'Verdana'
22742     ],
22743     
22744     specialChars : [
22745            "&#169;",
22746           "&#174;",     
22747           "&#8482;",    
22748           "&#163;" ,    
22749          // "&#8212;",    
22750           "&#8230;",    
22751           "&#247;" ,    
22752         //  "&#225;" ,     ?? a acute?
22753            "&#8364;"    , //Euro
22754        //   "&#8220;"    ,
22755         //  "&#8221;"    ,
22756         //  "&#8226;"    ,
22757           "&#176;"  //   , // degrees
22758
22759          // "&#233;"     , // e ecute
22760          // "&#250;"     , // u ecute?
22761     ],
22762     
22763     specialElements : [
22764         {
22765             text: "Insert Table",
22766             xtype: 'MenuItem',
22767             xns : Roo.Menu,
22768             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22769                 
22770         },
22771         {    
22772             text: "Insert Image",
22773             xtype: 'MenuItem',
22774             xns : Roo.Menu,
22775             ihtml : '<img src="about:blank"/>'
22776             
22777         }
22778         
22779          
22780     ],
22781     
22782     
22783     inputElements : [ 
22784             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22785             "input:submit", "input:button", "select", "textarea", "label" ],
22786     formats : [
22787         ["p"] ,  
22788         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22789         ["pre"],[ "code"], 
22790         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22791         ['div'],['span']
22792     ],
22793     
22794     cleanStyles : [
22795         "font-size"
22796     ],
22797      /**
22798      * @cfg {String} defaultFont default font to use.
22799      */
22800     defaultFont: 'tahoma',
22801    
22802     fontSelect : false,
22803     
22804     
22805     formatCombo : false,
22806     
22807     init : function(editor)
22808     {
22809         this.editor = editor;
22810         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22811         var editorcore = this.editorcore;
22812         
22813         var _t = this;
22814         
22815         var fid = editorcore.frameId;
22816         var etb = this;
22817         function btn(id, toggle, handler){
22818             var xid = fid + '-'+ id ;
22819             return {
22820                 id : xid,
22821                 cmd : id,
22822                 cls : 'x-btn-icon x-edit-'+id,
22823                 enableToggle:toggle !== false,
22824                 scope: _t, // was editor...
22825                 handler:handler||_t.relayBtnCmd,
22826                 clickEvent:'mousedown',
22827                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22828                 tabIndex:-1
22829             };
22830         }
22831         
22832         
22833         
22834         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22835         this.tb = tb;
22836          // stop form submits
22837         tb.el.on('click', function(e){
22838             e.preventDefault(); // what does this do?
22839         });
22840
22841         if(!this.disable.font) { // && !Roo.isSafari){
22842             /* why no safari for fonts 
22843             editor.fontSelect = tb.el.createChild({
22844                 tag:'select',
22845                 tabIndex: -1,
22846                 cls:'x-font-select',
22847                 html: this.createFontOptions()
22848             });
22849             
22850             editor.fontSelect.on('change', function(){
22851                 var font = editor.fontSelect.dom.value;
22852                 editor.relayCmd('fontname', font);
22853                 editor.deferFocus();
22854             }, editor);
22855             
22856             tb.add(
22857                 editor.fontSelect.dom,
22858                 '-'
22859             );
22860             */
22861             
22862         };
22863         if(!this.disable.formats){
22864             this.formatCombo = new Roo.form.ComboBox({
22865                 store: new Roo.data.SimpleStore({
22866                     id : 'tag',
22867                     fields: ['tag'],
22868                     data : this.formats // from states.js
22869                 }),
22870                 blockFocus : true,
22871                 name : '',
22872                 //autoCreate : {tag: "div",  size: "20"},
22873                 displayField:'tag',
22874                 typeAhead: false,
22875                 mode: 'local',
22876                 editable : false,
22877                 triggerAction: 'all',
22878                 emptyText:'Add tag',
22879                 selectOnFocus:true,
22880                 width:135,
22881                 listeners : {
22882                     'select': function(c, r, i) {
22883                         editorcore.insertTag(r.get('tag'));
22884                         editor.focus();
22885                     }
22886                 }
22887
22888             });
22889             tb.addField(this.formatCombo);
22890             
22891         }
22892         
22893         if(!this.disable.format){
22894             tb.add(
22895                 btn('bold'),
22896                 btn('italic'),
22897                 btn('underline'),
22898                 btn('strikethrough')
22899             );
22900         };
22901         if(!this.disable.fontSize){
22902             tb.add(
22903                 '-',
22904                 
22905                 
22906                 btn('increasefontsize', false, editorcore.adjustFont),
22907                 btn('decreasefontsize', false, editorcore.adjustFont)
22908             );
22909         };
22910         
22911         
22912         if(!this.disable.colors){
22913             tb.add(
22914                 '-', {
22915                     id:editorcore.frameId +'-forecolor',
22916                     cls:'x-btn-icon x-edit-forecolor',
22917                     clickEvent:'mousedown',
22918                     tooltip: this.buttonTips['forecolor'] || undefined,
22919                     tabIndex:-1,
22920                     menu : new Roo.menu.ColorMenu({
22921                         allowReselect: true,
22922                         focus: Roo.emptyFn,
22923                         value:'000000',
22924                         plain:true,
22925                         selectHandler: function(cp, color){
22926                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22927                             editor.deferFocus();
22928                         },
22929                         scope: editorcore,
22930                         clickEvent:'mousedown'
22931                     })
22932                 }, {
22933                     id:editorcore.frameId +'backcolor',
22934                     cls:'x-btn-icon x-edit-backcolor',
22935                     clickEvent:'mousedown',
22936                     tooltip: this.buttonTips['backcolor'] || undefined,
22937                     tabIndex:-1,
22938                     menu : new Roo.menu.ColorMenu({
22939                         focus: Roo.emptyFn,
22940                         value:'FFFFFF',
22941                         plain:true,
22942                         allowReselect: true,
22943                         selectHandler: function(cp, color){
22944                             if(Roo.isGecko){
22945                                 editorcore.execCmd('useCSS', false);
22946                                 editorcore.execCmd('hilitecolor', color);
22947                                 editorcore.execCmd('useCSS', true);
22948                                 editor.deferFocus();
22949                             }else{
22950                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22951                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22952                                 editor.deferFocus();
22953                             }
22954                         },
22955                         scope:editorcore,
22956                         clickEvent:'mousedown'
22957                     })
22958                 }
22959             );
22960         };
22961         // now add all the items...
22962         
22963
22964         if(!this.disable.alignments){
22965             tb.add(
22966                 '-',
22967                 btn('justifyleft'),
22968                 btn('justifycenter'),
22969                 btn('justifyright')
22970             );
22971         };
22972
22973         //if(!Roo.isSafari){
22974             if(!this.disable.links){
22975                 tb.add(
22976                     '-',
22977                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22978                 );
22979             };
22980
22981             if(!this.disable.lists){
22982                 tb.add(
22983                     '-',
22984                     btn('insertorderedlist'),
22985                     btn('insertunorderedlist')
22986                 );
22987             }
22988             if(!this.disable.sourceEdit){
22989                 tb.add(
22990                     '-',
22991                     btn('sourceedit', true, function(btn){
22992                         this.toggleSourceEdit(btn.pressed);
22993                     })
22994                 );
22995             }
22996         //}
22997         
22998         var smenu = { };
22999         // special menu.. - needs to be tidied up..
23000         if (!this.disable.special) {
23001             smenu = {
23002                 text: "&#169;",
23003                 cls: 'x-edit-none',
23004                 
23005                 menu : {
23006                     items : []
23007                 }
23008             };
23009             for (var i =0; i < this.specialChars.length; i++) {
23010                 smenu.menu.items.push({
23011                     
23012                     html: this.specialChars[i],
23013                     handler: function(a,b) {
23014                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23015                         //editor.insertAtCursor(a.html);
23016                         
23017                     },
23018                     tabIndex:-1
23019                 });
23020             }
23021             
23022             
23023             tb.add(smenu);
23024             
23025             
23026         }
23027         
23028         var cmenu = { };
23029         if (!this.disable.cleanStyles) {
23030             cmenu = {
23031                 cls: 'x-btn-icon x-btn-clear',
23032                 
23033                 menu : {
23034                     items : []
23035                 }
23036             };
23037             for (var i =0; i < this.cleanStyles.length; i++) {
23038                 cmenu.menu.items.push({
23039                     actiontype : this.cleanStyles[i],
23040                     html: 'Remove ' + this.cleanStyles[i],
23041                     handler: function(a,b) {
23042 //                        Roo.log(a);
23043 //                        Roo.log(b);
23044                         var c = Roo.get(editorcore.doc.body);
23045                         c.select('[style]').each(function(s) {
23046                             s.dom.style.removeProperty(a.actiontype);
23047                         });
23048                         editorcore.syncValue();
23049                     },
23050                     tabIndex:-1
23051                 });
23052             }
23053              cmenu.menu.items.push({
23054                 actiontype : 'tablewidths',
23055                 html: 'Remove Table Widths',
23056                 handler: function(a,b) {
23057                     editorcore.cleanTableWidths();
23058                     editorcore.syncValue();
23059                 },
23060                 tabIndex:-1
23061             });
23062             cmenu.menu.items.push({
23063                 actiontype : 'word',
23064                 html: 'Remove MS Word Formating',
23065                 handler: function(a,b) {
23066                     editorcore.cleanWord();
23067                     editorcore.syncValue();
23068                 },
23069                 tabIndex:-1
23070             });
23071             
23072             cmenu.menu.items.push({
23073                 actiontype : 'all',
23074                 html: 'Remove All Styles',
23075                 handler: function(a,b) {
23076                     
23077                     var c = Roo.get(editorcore.doc.body);
23078                     c.select('[style]').each(function(s) {
23079                         s.dom.removeAttribute('style');
23080                     });
23081                     editorcore.syncValue();
23082                 },
23083                 tabIndex:-1
23084             });
23085             
23086             cmenu.menu.items.push({
23087                 actiontype : 'all',
23088                 html: 'Remove All CSS Classes',
23089                 handler: function(a,b) {
23090                     
23091                     var c = Roo.get(editorcore.doc.body);
23092                     c.select('[class]').each(function(s) {
23093                         s.dom.className = '';
23094                     });
23095                     editorcore.syncValue();
23096                 },
23097                 tabIndex:-1
23098             });
23099             
23100              cmenu.menu.items.push({
23101                 actiontype : 'tidy',
23102                 html: 'Tidy HTML Source',
23103                 handler: function(a,b) {
23104                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23105                     editorcore.syncValue();
23106                 },
23107                 tabIndex:-1
23108             });
23109             
23110             
23111             tb.add(cmenu);
23112         }
23113          
23114         if (!this.disable.specialElements) {
23115             var semenu = {
23116                 text: "Other;",
23117                 cls: 'x-edit-none',
23118                 menu : {
23119                     items : []
23120                 }
23121             };
23122             for (var i =0; i < this.specialElements.length; i++) {
23123                 semenu.menu.items.push(
23124                     Roo.apply({ 
23125                         handler: function(a,b) {
23126                             editor.insertAtCursor(this.ihtml);
23127                         }
23128                     }, this.specialElements[i])
23129                 );
23130                     
23131             }
23132             
23133             tb.add(semenu);
23134             
23135             
23136         }
23137          
23138         
23139         if (this.btns) {
23140             for(var i =0; i< this.btns.length;i++) {
23141                 var b = Roo.factory(this.btns[i],Roo.form);
23142                 b.cls =  'x-edit-none';
23143                 
23144                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23145                     b.cls += ' x-init-enable';
23146                 }
23147                 
23148                 b.scope = editorcore;
23149                 tb.add(b);
23150             }
23151         
23152         }
23153         
23154         
23155         
23156         // disable everything...
23157         
23158         this.tb.items.each(function(item){
23159             
23160            if(
23161                 item.id != editorcore.frameId+ '-sourceedit' && 
23162                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23163             ){
23164                 
23165                 item.disable();
23166             }
23167         });
23168         this.rendered = true;
23169         
23170         // the all the btns;
23171         editor.on('editorevent', this.updateToolbar, this);
23172         // other toolbars need to implement this..
23173         //editor.on('editmodechange', this.updateToolbar, this);
23174     },
23175     
23176     
23177     relayBtnCmd : function(btn) {
23178         this.editorcore.relayCmd(btn.cmd);
23179     },
23180     // private used internally
23181     createLink : function(){
23182         Roo.log("create link?");
23183         var url = prompt(this.createLinkText, this.defaultLinkValue);
23184         if(url && url != 'http:/'+'/'){
23185             this.editorcore.relayCmd('createlink', url);
23186         }
23187     },
23188
23189     
23190     /**
23191      * Protected method that will not generally be called directly. It triggers
23192      * a toolbar update by reading the markup state of the current selection in the editor.
23193      */
23194     updateToolbar: function(){
23195
23196         if(!this.editorcore.activated){
23197             this.editor.onFirstFocus();
23198             return;
23199         }
23200
23201         var btns = this.tb.items.map, 
23202             doc = this.editorcore.doc,
23203             frameId = this.editorcore.frameId;
23204
23205         if(!this.disable.font && !Roo.isSafari){
23206             /*
23207             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23208             if(name != this.fontSelect.dom.value){
23209                 this.fontSelect.dom.value = name;
23210             }
23211             */
23212         }
23213         if(!this.disable.format){
23214             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23215             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23216             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23217             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23218         }
23219         if(!this.disable.alignments){
23220             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23221             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23222             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23223         }
23224         if(!Roo.isSafari && !this.disable.lists){
23225             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23226             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23227         }
23228         
23229         var ans = this.editorcore.getAllAncestors();
23230         if (this.formatCombo) {
23231             
23232             
23233             var store = this.formatCombo.store;
23234             this.formatCombo.setValue("");
23235             for (var i =0; i < ans.length;i++) {
23236                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23237                     // select it..
23238                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23239                     break;
23240                 }
23241             }
23242         }
23243         
23244         
23245         
23246         // hides menus... - so this cant be on a menu...
23247         Roo.menu.MenuMgr.hideAll();
23248
23249         //this.editorsyncValue();
23250     },
23251    
23252     
23253     createFontOptions : function(){
23254         var buf = [], fs = this.fontFamilies, ff, lc;
23255         
23256         
23257         
23258         for(var i = 0, len = fs.length; i< len; i++){
23259             ff = fs[i];
23260             lc = ff.toLowerCase();
23261             buf.push(
23262                 '<option value="',lc,'" style="font-family:',ff,';"',
23263                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23264                     ff,
23265                 '</option>'
23266             );
23267         }
23268         return buf.join('');
23269     },
23270     
23271     toggleSourceEdit : function(sourceEditMode){
23272         
23273         Roo.log("toolbar toogle");
23274         if(sourceEditMode === undefined){
23275             sourceEditMode = !this.sourceEditMode;
23276         }
23277         this.sourceEditMode = sourceEditMode === true;
23278         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23279         // just toggle the button?
23280         if(btn.pressed !== this.sourceEditMode){
23281             btn.toggle(this.sourceEditMode);
23282             return;
23283         }
23284         
23285         if(sourceEditMode){
23286             Roo.log("disabling buttons");
23287             this.tb.items.each(function(item){
23288                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23289                     item.disable();
23290                 }
23291             });
23292           
23293         }else{
23294             Roo.log("enabling buttons");
23295             if(this.editorcore.initialized){
23296                 this.tb.items.each(function(item){
23297                     item.enable();
23298                 });
23299             }
23300             
23301         }
23302         Roo.log("calling toggole on editor");
23303         // tell the editor that it's been pressed..
23304         this.editor.toggleSourceEdit(sourceEditMode);
23305        
23306     },
23307      /**
23308      * Object collection of toolbar tooltips for the buttons in the editor. The key
23309      * is the command id associated with that button and the value is a valid QuickTips object.
23310      * For example:
23311 <pre><code>
23312 {
23313     bold : {
23314         title: 'Bold (Ctrl+B)',
23315         text: 'Make the selected text bold.',
23316         cls: 'x-html-editor-tip'
23317     },
23318     italic : {
23319         title: 'Italic (Ctrl+I)',
23320         text: 'Make the selected text italic.',
23321         cls: 'x-html-editor-tip'
23322     },
23323     ...
23324 </code></pre>
23325     * @type Object
23326      */
23327     buttonTips : {
23328         bold : {
23329             title: 'Bold (Ctrl+B)',
23330             text: 'Make the selected text bold.',
23331             cls: 'x-html-editor-tip'
23332         },
23333         italic : {
23334             title: 'Italic (Ctrl+I)',
23335             text: 'Make the selected text italic.',
23336             cls: 'x-html-editor-tip'
23337         },
23338         underline : {
23339             title: 'Underline (Ctrl+U)',
23340             text: 'Underline the selected text.',
23341             cls: 'x-html-editor-tip'
23342         },
23343         strikethrough : {
23344             title: 'Strikethrough',
23345             text: 'Strikethrough the selected text.',
23346             cls: 'x-html-editor-tip'
23347         },
23348         increasefontsize : {
23349             title: 'Grow Text',
23350             text: 'Increase the font size.',
23351             cls: 'x-html-editor-tip'
23352         },
23353         decreasefontsize : {
23354             title: 'Shrink Text',
23355             text: 'Decrease the font size.',
23356             cls: 'x-html-editor-tip'
23357         },
23358         backcolor : {
23359             title: 'Text Highlight Color',
23360             text: 'Change the background color of the selected text.',
23361             cls: 'x-html-editor-tip'
23362         },
23363         forecolor : {
23364             title: 'Font Color',
23365             text: 'Change the color of the selected text.',
23366             cls: 'x-html-editor-tip'
23367         },
23368         justifyleft : {
23369             title: 'Align Text Left',
23370             text: 'Align text to the left.',
23371             cls: 'x-html-editor-tip'
23372         },
23373         justifycenter : {
23374             title: 'Center Text',
23375             text: 'Center text in the editor.',
23376             cls: 'x-html-editor-tip'
23377         },
23378         justifyright : {
23379             title: 'Align Text Right',
23380             text: 'Align text to the right.',
23381             cls: 'x-html-editor-tip'
23382         },
23383         insertunorderedlist : {
23384             title: 'Bullet List',
23385             text: 'Start a bulleted list.',
23386             cls: 'x-html-editor-tip'
23387         },
23388         insertorderedlist : {
23389             title: 'Numbered List',
23390             text: 'Start a numbered list.',
23391             cls: 'x-html-editor-tip'
23392         },
23393         createlink : {
23394             title: 'Hyperlink',
23395             text: 'Make the selected text a hyperlink.',
23396             cls: 'x-html-editor-tip'
23397         },
23398         sourceedit : {
23399             title: 'Source Edit',
23400             text: 'Switch to source editing mode.',
23401             cls: 'x-html-editor-tip'
23402         }
23403     },
23404     // private
23405     onDestroy : function(){
23406         if(this.rendered){
23407             
23408             this.tb.items.each(function(item){
23409                 if(item.menu){
23410                     item.menu.removeAll();
23411                     if(item.menu.el){
23412                         item.menu.el.destroy();
23413                     }
23414                 }
23415                 item.destroy();
23416             });
23417              
23418         }
23419     },
23420     onFirstFocus: function() {
23421         this.tb.items.each(function(item){
23422            item.enable();
23423         });
23424     }
23425 });
23426
23427
23428
23429
23430 // <script type="text/javascript">
23431 /*
23432  * Based on
23433  * Ext JS Library 1.1.1
23434  * Copyright(c) 2006-2007, Ext JS, LLC.
23435  *  
23436  
23437  */
23438
23439  
23440 /**
23441  * @class Roo.form.HtmlEditor.ToolbarContext
23442  * Context Toolbar
23443  * 
23444  * Usage:
23445  *
23446  new Roo.form.HtmlEditor({
23447     ....
23448     toolbars : [
23449         { xtype: 'ToolbarStandard', styles : {} }
23450         { xtype: 'ToolbarContext', disable : {} }
23451     ]
23452 })
23453
23454      
23455  * 
23456  * @config : {Object} disable List of elements to disable.. (not done yet.)
23457  * @config : {Object} styles  Map of styles available.
23458  * 
23459  */
23460
23461 Roo.form.HtmlEditor.ToolbarContext = function(config)
23462 {
23463     
23464     Roo.apply(this, config);
23465     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23466     // dont call parent... till later.
23467     this.styles = this.styles || {};
23468 }
23469
23470  
23471
23472 Roo.form.HtmlEditor.ToolbarContext.types = {
23473     'IMG' : {
23474         width : {
23475             title: "Width",
23476             width: 40
23477         },
23478         height:  {
23479             title: "Height",
23480             width: 40
23481         },
23482         align: {
23483             title: "Align",
23484             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23485             width : 80
23486             
23487         },
23488         border: {
23489             title: "Border",
23490             width: 40
23491         },
23492         alt: {
23493             title: "Alt",
23494             width: 120
23495         },
23496         src : {
23497             title: "Src",
23498             width: 220
23499         }
23500         
23501     },
23502     'A' : {
23503         name : {
23504             title: "Name",
23505             width: 50
23506         },
23507         target:  {
23508             title: "Target",
23509             width: 120
23510         },
23511         href:  {
23512             title: "Href",
23513             width: 220
23514         } // border?
23515         
23516     },
23517     'TABLE' : {
23518         rows : {
23519             title: "Rows",
23520             width: 20
23521         },
23522         cols : {
23523             title: "Cols",
23524             width: 20
23525         },
23526         width : {
23527             title: "Width",
23528             width: 40
23529         },
23530         height : {
23531             title: "Height",
23532             width: 40
23533         },
23534         border : {
23535             title: "Border",
23536             width: 20
23537         }
23538     },
23539     'TD' : {
23540         width : {
23541             title: "Width",
23542             width: 40
23543         },
23544         height : {
23545             title: "Height",
23546             width: 40
23547         },   
23548         align: {
23549             title: "Align",
23550             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23551             width: 80
23552         },
23553         valign: {
23554             title: "Valign",
23555             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23556             width: 80
23557         },
23558         colspan: {
23559             title: "Colspan",
23560             width: 20
23561             
23562         },
23563          'font-family'  : {
23564             title : "Font",
23565             style : 'fontFamily',
23566             displayField: 'display',
23567             optname : 'font-family',
23568             width: 140
23569         }
23570     },
23571     'INPUT' : {
23572         name : {
23573             title: "name",
23574             width: 120
23575         },
23576         value : {
23577             title: "Value",
23578             width: 120
23579         },
23580         width : {
23581             title: "Width",
23582             width: 40
23583         }
23584     },
23585     'LABEL' : {
23586         'for' : {
23587             title: "For",
23588             width: 120
23589         }
23590     },
23591     'TEXTAREA' : {
23592           name : {
23593             title: "name",
23594             width: 120
23595         },
23596         rows : {
23597             title: "Rows",
23598             width: 20
23599         },
23600         cols : {
23601             title: "Cols",
23602             width: 20
23603         }
23604     },
23605     'SELECT' : {
23606         name : {
23607             title: "name",
23608             width: 120
23609         },
23610         selectoptions : {
23611             title: "Options",
23612             width: 200
23613         }
23614     },
23615     
23616     // should we really allow this??
23617     // should this just be 
23618     'BODY' : {
23619         title : {
23620             title: "Title",
23621             width: 200,
23622             disabled : true
23623         }
23624     },
23625     'SPAN' : {
23626         'font-family'  : {
23627             title : "Font",
23628             style : 'fontFamily',
23629             displayField: 'display',
23630             optname : 'font-family',
23631             width: 140
23632         }
23633     },
23634     'DIV' : {
23635         'font-family'  : {
23636             title : "Font",
23637             style : 'fontFamily',
23638             displayField: 'display',
23639             optname : 'font-family',
23640             width: 140
23641         }
23642     },
23643      'P' : {
23644         'font-family'  : {
23645             title : "Font",
23646             style : 'fontFamily',
23647             displayField: 'display',
23648             optname : 'font-family',
23649             width: 140
23650         }
23651     },
23652     
23653     '*' : {
23654         // empty..
23655     }
23656
23657 };
23658
23659 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23660 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23661
23662 Roo.form.HtmlEditor.ToolbarContext.options = {
23663         'font-family'  : [ 
23664                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23665                 [ 'Courier New', 'Courier New'],
23666                 [ 'Tahoma', 'Tahoma'],
23667                 [ 'Times New Roman,serif', 'Times'],
23668                 [ 'Verdana','Verdana' ]
23669         ]
23670 };
23671
23672 // fixme - these need to be configurable..
23673  
23674
23675 //Roo.form.HtmlEditor.ToolbarContext.types
23676
23677
23678 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23679     
23680     tb: false,
23681     
23682     rendered: false,
23683     
23684     editor : false,
23685     editorcore : false,
23686     /**
23687      * @cfg {Object} disable  List of toolbar elements to disable
23688          
23689      */
23690     disable : false,
23691     /**
23692      * @cfg {Object} styles List of styles 
23693      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23694      *
23695      * These must be defined in the page, so they get rendered correctly..
23696      * .headline { }
23697      * TD.underline { }
23698      * 
23699      */
23700     styles : false,
23701     
23702     options: false,
23703     
23704     toolbars : false,
23705     
23706     init : function(editor)
23707     {
23708         this.editor = editor;
23709         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23710         var editorcore = this.editorcore;
23711         
23712         var fid = editorcore.frameId;
23713         var etb = this;
23714         function btn(id, toggle, handler){
23715             var xid = fid + '-'+ id ;
23716             return {
23717                 id : xid,
23718                 cmd : id,
23719                 cls : 'x-btn-icon x-edit-'+id,
23720                 enableToggle:toggle !== false,
23721                 scope: editorcore, // was editor...
23722                 handler:handler||editorcore.relayBtnCmd,
23723                 clickEvent:'mousedown',
23724                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23725                 tabIndex:-1
23726             };
23727         }
23728         // create a new element.
23729         var wdiv = editor.wrap.createChild({
23730                 tag: 'div'
23731             }, editor.wrap.dom.firstChild.nextSibling, true);
23732         
23733         // can we do this more than once??
23734         
23735          // stop form submits
23736       
23737  
23738         // disable everything...
23739         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23740         this.toolbars = {};
23741            
23742         for (var i in  ty) {
23743           
23744             this.toolbars[i] = this.buildToolbar(ty[i],i);
23745         }
23746         this.tb = this.toolbars.BODY;
23747         this.tb.el.show();
23748         this.buildFooter();
23749         this.footer.show();
23750         editor.on('hide', function( ) { this.footer.hide() }, this);
23751         editor.on('show', function( ) { this.footer.show() }, this);
23752         
23753          
23754         this.rendered = true;
23755         
23756         // the all the btns;
23757         editor.on('editorevent', this.updateToolbar, this);
23758         // other toolbars need to implement this..
23759         //editor.on('editmodechange', this.updateToolbar, this);
23760     },
23761     
23762     
23763     
23764     /**
23765      * Protected method that will not generally be called directly. It triggers
23766      * a toolbar update by reading the markup state of the current selection in the editor.
23767      *
23768      * Note you can force an update by calling on('editorevent', scope, false)
23769      */
23770     updateToolbar: function(editor,ev,sel){
23771
23772         //Roo.log(ev);
23773         // capture mouse up - this is handy for selecting images..
23774         // perhaps should go somewhere else...
23775         if(!this.editorcore.activated){
23776              this.editor.onFirstFocus();
23777             return;
23778         }
23779         
23780         
23781         
23782         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23783         // selectNode - might want to handle IE?
23784         if (ev &&
23785             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23786             ev.target && ev.target.tagName == 'IMG') {
23787             // they have click on an image...
23788             // let's see if we can change the selection...
23789             sel = ev.target;
23790          
23791               var nodeRange = sel.ownerDocument.createRange();
23792             try {
23793                 nodeRange.selectNode(sel);
23794             } catch (e) {
23795                 nodeRange.selectNodeContents(sel);
23796             }
23797             //nodeRange.collapse(true);
23798             var s = this.editorcore.win.getSelection();
23799             s.removeAllRanges();
23800             s.addRange(nodeRange);
23801         }  
23802         
23803       
23804         var updateFooter = sel ? false : true;
23805         
23806         
23807         var ans = this.editorcore.getAllAncestors();
23808         
23809         // pick
23810         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23811         
23812         if (!sel) { 
23813             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23814             sel = sel ? sel : this.editorcore.doc.body;
23815             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23816             
23817         }
23818         // pick a menu that exists..
23819         var tn = sel.tagName.toUpperCase();
23820         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23821         
23822         tn = sel.tagName.toUpperCase();
23823         
23824         var lastSel = this.tb.selectedNode;
23825         
23826         this.tb.selectedNode = sel;
23827         
23828         // if current menu does not match..
23829         
23830         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23831                 
23832             this.tb.el.hide();
23833             ///console.log("show: " + tn);
23834             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23835             this.tb.el.show();
23836             // update name
23837             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23838             
23839             
23840             // update attributes
23841             if (this.tb.fields) {
23842                 this.tb.fields.each(function(e) {
23843                     if (e.stylename) {
23844                         e.setValue(sel.style[e.stylename]);
23845                         return;
23846                     } 
23847                    e.setValue(sel.getAttribute(e.attrname));
23848                 });
23849             }
23850             
23851             var hasStyles = false;
23852             for(var i in this.styles) {
23853                 hasStyles = true;
23854                 break;
23855             }
23856             
23857             // update styles
23858             if (hasStyles) { 
23859                 var st = this.tb.fields.item(0);
23860                 
23861                 st.store.removeAll();
23862                
23863                 
23864                 var cn = sel.className.split(/\s+/);
23865                 
23866                 var avs = [];
23867                 if (this.styles['*']) {
23868                     
23869                     Roo.each(this.styles['*'], function(v) {
23870                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23871                     });
23872                 }
23873                 if (this.styles[tn]) { 
23874                     Roo.each(this.styles[tn], function(v) {
23875                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23876                     });
23877                 }
23878                 
23879                 st.store.loadData(avs);
23880                 st.collapse();
23881                 st.setValue(cn);
23882             }
23883             // flag our selected Node.
23884             this.tb.selectedNode = sel;
23885            
23886            
23887             Roo.menu.MenuMgr.hideAll();
23888
23889         }
23890         
23891         if (!updateFooter) {
23892             //this.footDisp.dom.innerHTML = ''; 
23893             return;
23894         }
23895         // update the footer
23896         //
23897         var html = '';
23898         
23899         this.footerEls = ans.reverse();
23900         Roo.each(this.footerEls, function(a,i) {
23901             if (!a) { return; }
23902             html += html.length ? ' &gt; '  :  '';
23903             
23904             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23905             
23906         });
23907        
23908         // 
23909         var sz = this.footDisp.up('td').getSize();
23910         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23911         this.footDisp.dom.style.marginLeft = '5px';
23912         
23913         this.footDisp.dom.style.overflow = 'hidden';
23914         
23915         this.footDisp.dom.innerHTML = html;
23916             
23917         //this.editorsyncValue();
23918     },
23919      
23920     
23921    
23922        
23923     // private
23924     onDestroy : function(){
23925         if(this.rendered){
23926             
23927             this.tb.items.each(function(item){
23928                 if(item.menu){
23929                     item.menu.removeAll();
23930                     if(item.menu.el){
23931                         item.menu.el.destroy();
23932                     }
23933                 }
23934                 item.destroy();
23935             });
23936              
23937         }
23938     },
23939     onFirstFocus: function() {
23940         // need to do this for all the toolbars..
23941         this.tb.items.each(function(item){
23942            item.enable();
23943         });
23944     },
23945     buildToolbar: function(tlist, nm)
23946     {
23947         var editor = this.editor;
23948         var editorcore = this.editorcore;
23949          // create a new element.
23950         var wdiv = editor.wrap.createChild({
23951                 tag: 'div'
23952             }, editor.wrap.dom.firstChild.nextSibling, true);
23953         
23954        
23955         var tb = new Roo.Toolbar(wdiv);
23956         // add the name..
23957         
23958         tb.add(nm+ ":&nbsp;");
23959         
23960         var styles = [];
23961         for(var i in this.styles) {
23962             styles.push(i);
23963         }
23964         
23965         // styles...
23966         if (styles && styles.length) {
23967             
23968             // this needs a multi-select checkbox...
23969             tb.addField( new Roo.form.ComboBox({
23970                 store: new Roo.data.SimpleStore({
23971                     id : 'val',
23972                     fields: ['val', 'selected'],
23973                     data : [] 
23974                 }),
23975                 name : '-roo-edit-className',
23976                 attrname : 'className',
23977                 displayField: 'val',
23978                 typeAhead: false,
23979                 mode: 'local',
23980                 editable : false,
23981                 triggerAction: 'all',
23982                 emptyText:'Select Style',
23983                 selectOnFocus:true,
23984                 width: 130,
23985                 listeners : {
23986                     'select': function(c, r, i) {
23987                         // initial support only for on class per el..
23988                         tb.selectedNode.className =  r ? r.get('val') : '';
23989                         editorcore.syncValue();
23990                     }
23991                 }
23992     
23993             }));
23994         }
23995         
23996         var tbc = Roo.form.HtmlEditor.ToolbarContext;
23997         var tbops = tbc.options;
23998         
23999         for (var i in tlist) {
24000             
24001             var item = tlist[i];
24002             tb.add(item.title + ":&nbsp;");
24003             
24004             
24005             //optname == used so you can configure the options available..
24006             var opts = item.opts ? item.opts : false;
24007             if (item.optname) {
24008                 opts = tbops[item.optname];
24009            
24010             }
24011             
24012             if (opts) {
24013                 // opts == pulldown..
24014                 tb.addField( new Roo.form.ComboBox({
24015                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24016                         id : 'val',
24017                         fields: ['val', 'display'],
24018                         data : opts  
24019                     }),
24020                     name : '-roo-edit-' + i,
24021                     attrname : i,
24022                     stylename : item.style ? item.style : false,
24023                     displayField: item.displayField ? item.displayField : 'val',
24024                     valueField :  'val',
24025                     typeAhead: false,
24026                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24027                     editable : false,
24028                     triggerAction: 'all',
24029                     emptyText:'Select',
24030                     selectOnFocus:true,
24031                     width: item.width ? item.width  : 130,
24032                     listeners : {
24033                         'select': function(c, r, i) {
24034                             if (c.stylename) {
24035                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24036                                 return;
24037                             }
24038                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24039                         }
24040                     }
24041
24042                 }));
24043                 continue;
24044                     
24045                  
24046                 
24047                 tb.addField( new Roo.form.TextField({
24048                     name: i,
24049                     width: 100,
24050                     //allowBlank:false,
24051                     value: ''
24052                 }));
24053                 continue;
24054             }
24055             tb.addField( new Roo.form.TextField({
24056                 name: '-roo-edit-' + i,
24057                 attrname : i,
24058                 
24059                 width: item.width,
24060                 //allowBlank:true,
24061                 value: '',
24062                 listeners: {
24063                     'change' : function(f, nv, ov) {
24064                         tb.selectedNode.setAttribute(f.attrname, nv);
24065                         editorcore.syncValue();
24066                     }
24067                 }
24068             }));
24069              
24070         }
24071         
24072         var _this = this;
24073         
24074         if(nm == 'BODY'){
24075             tb.addSeparator();
24076         
24077             tb.addButton( {
24078                 text: 'Stylesheets',
24079
24080                 listeners : {
24081                     click : function ()
24082                     {
24083                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24084                     }
24085                 }
24086             });
24087         }
24088         
24089         tb.addFill();
24090         tb.addButton( {
24091             text: 'Remove Tag',
24092     
24093             listeners : {
24094                 click : function ()
24095                 {
24096                     // remove
24097                     // undo does not work.
24098                      
24099                     var sn = tb.selectedNode;
24100                     
24101                     var pn = sn.parentNode;
24102                     
24103                     var stn =  sn.childNodes[0];
24104                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24105                     while (sn.childNodes.length) {
24106                         var node = sn.childNodes[0];
24107                         sn.removeChild(node);
24108                         //Roo.log(node);
24109                         pn.insertBefore(node, sn);
24110                         
24111                     }
24112                     pn.removeChild(sn);
24113                     var range = editorcore.createRange();
24114         
24115                     range.setStart(stn,0);
24116                     range.setEnd(en,0); //????
24117                     //range.selectNode(sel);
24118                     
24119                     
24120                     var selection = editorcore.getSelection();
24121                     selection.removeAllRanges();
24122                     selection.addRange(range);
24123                     
24124                     
24125                     
24126                     //_this.updateToolbar(null, null, pn);
24127                     _this.updateToolbar(null, null, null);
24128                     _this.footDisp.dom.innerHTML = ''; 
24129                 }
24130             }
24131             
24132                     
24133                 
24134             
24135         });
24136         
24137         
24138         tb.el.on('click', function(e){
24139             e.preventDefault(); // what does this do?
24140         });
24141         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24142         tb.el.hide();
24143         tb.name = nm;
24144         // dont need to disable them... as they will get hidden
24145         return tb;
24146          
24147         
24148     },
24149     buildFooter : function()
24150     {
24151         
24152         var fel = this.editor.wrap.createChild();
24153         this.footer = new Roo.Toolbar(fel);
24154         // toolbar has scrolly on left / right?
24155         var footDisp= new Roo.Toolbar.Fill();
24156         var _t = this;
24157         this.footer.add(
24158             {
24159                 text : '&lt;',
24160                 xtype: 'Button',
24161                 handler : function() {
24162                     _t.footDisp.scrollTo('left',0,true)
24163                 }
24164             }
24165         );
24166         this.footer.add( footDisp );
24167         this.footer.add( 
24168             {
24169                 text : '&gt;',
24170                 xtype: 'Button',
24171                 handler : function() {
24172                     // no animation..
24173                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24174                 }
24175             }
24176         );
24177         var fel = Roo.get(footDisp.el);
24178         fel.addClass('x-editor-context');
24179         this.footDispWrap = fel; 
24180         this.footDispWrap.overflow  = 'hidden';
24181         
24182         this.footDisp = fel.createChild();
24183         this.footDispWrap.on('click', this.onContextClick, this)
24184         
24185         
24186     },
24187     onContextClick : function (ev,dom)
24188     {
24189         ev.preventDefault();
24190         var  cn = dom.className;
24191         //Roo.log(cn);
24192         if (!cn.match(/x-ed-loc-/)) {
24193             return;
24194         }
24195         var n = cn.split('-').pop();
24196         var ans = this.footerEls;
24197         var sel = ans[n];
24198         
24199          // pick
24200         var range = this.editorcore.createRange();
24201         
24202         range.selectNodeContents(sel);
24203         //range.selectNode(sel);
24204         
24205         
24206         var selection = this.editorcore.getSelection();
24207         selection.removeAllRanges();
24208         selection.addRange(range);
24209         
24210         
24211         
24212         this.updateToolbar(null, null, sel);
24213         
24214         
24215     }
24216     
24217     
24218     
24219     
24220     
24221 });
24222
24223
24224
24225
24226
24227 /*
24228  * Based on:
24229  * Ext JS Library 1.1.1
24230  * Copyright(c) 2006-2007, Ext JS, LLC.
24231  *
24232  * Originally Released Under LGPL - original licence link has changed is not relivant.
24233  *
24234  * Fork - LGPL
24235  * <script type="text/javascript">
24236  */
24237  
24238 /**
24239  * @class Roo.form.BasicForm
24240  * @extends Roo.util.Observable
24241  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24242  * @constructor
24243  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24244  * @param {Object} config Configuration options
24245  */
24246 Roo.form.BasicForm = function(el, config){
24247     this.allItems = [];
24248     this.childForms = [];
24249     Roo.apply(this, config);
24250     /*
24251      * The Roo.form.Field items in this form.
24252      * @type MixedCollection
24253      */
24254      
24255      
24256     this.items = new Roo.util.MixedCollection(false, function(o){
24257         return o.id || (o.id = Roo.id());
24258     });
24259     this.addEvents({
24260         /**
24261          * @event beforeaction
24262          * Fires before any action is performed. Return false to cancel the action.
24263          * @param {Form} this
24264          * @param {Action} action The action to be performed
24265          */
24266         beforeaction: true,
24267         /**
24268          * @event actionfailed
24269          * Fires when an action fails.
24270          * @param {Form} this
24271          * @param {Action} action The action that failed
24272          */
24273         actionfailed : true,
24274         /**
24275          * @event actioncomplete
24276          * Fires when an action is completed.
24277          * @param {Form} this
24278          * @param {Action} action The action that completed
24279          */
24280         actioncomplete : true
24281     });
24282     if(el){
24283         this.initEl(el);
24284     }
24285     Roo.form.BasicForm.superclass.constructor.call(this);
24286 };
24287
24288 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24289     /**
24290      * @cfg {String} method
24291      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24292      */
24293     /**
24294      * @cfg {DataReader} reader
24295      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24296      * This is optional as there is built-in support for processing JSON.
24297      */
24298     /**
24299      * @cfg {DataReader} errorReader
24300      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24301      * This is completely optional as there is built-in support for processing JSON.
24302      */
24303     /**
24304      * @cfg {String} url
24305      * The URL to use for form actions if one isn't supplied in the action options.
24306      */
24307     /**
24308      * @cfg {Boolean} fileUpload
24309      * Set to true if this form is a file upload.
24310      */
24311      
24312     /**
24313      * @cfg {Object} baseParams
24314      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24315      */
24316      /**
24317      
24318     /**
24319      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24320      */
24321     timeout: 30,
24322
24323     // private
24324     activeAction : null,
24325
24326     /**
24327      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24328      * or setValues() data instead of when the form was first created.
24329      */
24330     trackResetOnLoad : false,
24331     
24332     
24333     /**
24334      * childForms - used for multi-tab forms
24335      * @type {Array}
24336      */
24337     childForms : false,
24338     
24339     /**
24340      * allItems - full list of fields.
24341      * @type {Array}
24342      */
24343     allItems : false,
24344     
24345     /**
24346      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24347      * element by passing it or its id or mask the form itself by passing in true.
24348      * @type Mixed
24349      */
24350     waitMsgTarget : false,
24351
24352     // private
24353     initEl : function(el){
24354         this.el = Roo.get(el);
24355         this.id = this.el.id || Roo.id();
24356         this.el.on('submit', this.onSubmit, this);
24357         this.el.addClass('x-form');
24358     },
24359
24360     // private
24361     onSubmit : function(e){
24362         e.stopEvent();
24363     },
24364
24365     /**
24366      * Returns true if client-side validation on the form is successful.
24367      * @return Boolean
24368      */
24369     isValid : function(){
24370         var valid = true;
24371         this.items.each(function(f){
24372            if(!f.validate()){
24373                valid = false;
24374            }
24375         });
24376         return valid;
24377     },
24378
24379     /**
24380      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24381      * @return Boolean
24382      */
24383     isDirty : function(){
24384         var dirty = false;
24385         this.items.each(function(f){
24386            if(f.isDirty()){
24387                dirty = true;
24388                return false;
24389            }
24390         });
24391         return dirty;
24392     },
24393     
24394     /**
24395      * Returns true if any fields in this form have changed since their original load. (New version)
24396      * @return Boolean
24397      */
24398     
24399     hasChanged : function()
24400     {
24401         var dirty = false;
24402         this.items.each(function(f){
24403            if(f.hasChanged()){
24404                dirty = true;
24405                return false;
24406            }
24407         });
24408         return dirty;
24409         
24410     },
24411     /**
24412      * Resets all hasChanged to 'false' -
24413      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24414      * So hasChanged storage is only to be used for this purpose
24415      * @return Boolean
24416      */
24417     resetHasChanged : function()
24418     {
24419         this.items.each(function(f){
24420            f.resetHasChanged();
24421         });
24422         
24423     },
24424     
24425     
24426     /**
24427      * Performs a predefined action (submit or load) or custom actions you define on this form.
24428      * @param {String} actionName The name of the action type
24429      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24430      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24431      * accept other config options):
24432      * <pre>
24433 Property          Type             Description
24434 ----------------  ---------------  ----------------------------------------------------------------------------------
24435 url               String           The url for the action (defaults to the form's url)
24436 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24437 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24438 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24439                                    validate the form on the client (defaults to false)
24440      * </pre>
24441      * @return {BasicForm} this
24442      */
24443     doAction : function(action, options){
24444         if(typeof action == 'string'){
24445             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24446         }
24447         if(this.fireEvent('beforeaction', this, action) !== false){
24448             this.beforeAction(action);
24449             action.run.defer(100, action);
24450         }
24451         return this;
24452     },
24453
24454     /**
24455      * Shortcut to do a submit action.
24456      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24457      * @return {BasicForm} this
24458      */
24459     submit : function(options){
24460         this.doAction('submit', options);
24461         return this;
24462     },
24463
24464     /**
24465      * Shortcut to do a load action.
24466      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24467      * @return {BasicForm} this
24468      */
24469     load : function(options){
24470         this.doAction('load', options);
24471         return this;
24472     },
24473
24474     /**
24475      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24476      * @param {Record} record The record to edit
24477      * @return {BasicForm} this
24478      */
24479     updateRecord : function(record){
24480         record.beginEdit();
24481         var fs = record.fields;
24482         fs.each(function(f){
24483             var field = this.findField(f.name);
24484             if(field){
24485                 record.set(f.name, field.getValue());
24486             }
24487         }, this);
24488         record.endEdit();
24489         return this;
24490     },
24491
24492     /**
24493      * Loads an Roo.data.Record into this form.
24494      * @param {Record} record The record to load
24495      * @return {BasicForm} this
24496      */
24497     loadRecord : function(record){
24498         this.setValues(record.data);
24499         return this;
24500     },
24501
24502     // private
24503     beforeAction : function(action){
24504         var o = action.options;
24505         
24506        
24507         if(this.waitMsgTarget === true){
24508             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24509         }else if(this.waitMsgTarget){
24510             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24511             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24512         }else {
24513             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24514         }
24515          
24516     },
24517
24518     // private
24519     afterAction : function(action, success){
24520         this.activeAction = null;
24521         var o = action.options;
24522         
24523         if(this.waitMsgTarget === true){
24524             this.el.unmask();
24525         }else if(this.waitMsgTarget){
24526             this.waitMsgTarget.unmask();
24527         }else{
24528             Roo.MessageBox.updateProgress(1);
24529             Roo.MessageBox.hide();
24530         }
24531          
24532         if(success){
24533             if(o.reset){
24534                 this.reset();
24535             }
24536             Roo.callback(o.success, o.scope, [this, action]);
24537             this.fireEvent('actioncomplete', this, action);
24538             
24539         }else{
24540             
24541             // failure condition..
24542             // we have a scenario where updates need confirming.
24543             // eg. if a locking scenario exists..
24544             // we look for { errors : { needs_confirm : true }} in the response.
24545             if (
24546                 (typeof(action.result) != 'undefined')  &&
24547                 (typeof(action.result.errors) != 'undefined')  &&
24548                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24549            ){
24550                 var _t = this;
24551                 Roo.MessageBox.confirm(
24552                     "Change requires confirmation",
24553                     action.result.errorMsg,
24554                     function(r) {
24555                         if (r != 'yes') {
24556                             return;
24557                         }
24558                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24559                     }
24560                     
24561                 );
24562                 
24563                 
24564                 
24565                 return;
24566             }
24567             
24568             Roo.callback(o.failure, o.scope, [this, action]);
24569             // show an error message if no failed handler is set..
24570             if (!this.hasListener('actionfailed')) {
24571                 Roo.MessageBox.alert("Error",
24572                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24573                         action.result.errorMsg :
24574                         "Saving Failed, please check your entries or try again"
24575                 );
24576             }
24577             
24578             this.fireEvent('actionfailed', this, action);
24579         }
24580         
24581     },
24582
24583     /**
24584      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24585      * @param {String} id The value to search for
24586      * @return Field
24587      */
24588     findField : function(id){
24589         var field = this.items.get(id);
24590         if(!field){
24591             this.items.each(function(f){
24592                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24593                     field = f;
24594                     return false;
24595                 }
24596             });
24597         }
24598         return field || null;
24599     },
24600
24601     /**
24602      * Add a secondary form to this one, 
24603      * Used to provide tabbed forms. One form is primary, with hidden values 
24604      * which mirror the elements from the other forms.
24605      * 
24606      * @param {Roo.form.Form} form to add.
24607      * 
24608      */
24609     addForm : function(form)
24610     {
24611        
24612         if (this.childForms.indexOf(form) > -1) {
24613             // already added..
24614             return;
24615         }
24616         this.childForms.push(form);
24617         var n = '';
24618         Roo.each(form.allItems, function (fe) {
24619             
24620             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24621             if (this.findField(n)) { // already added..
24622                 return;
24623             }
24624             var add = new Roo.form.Hidden({
24625                 name : n
24626             });
24627             add.render(this.el);
24628             
24629             this.add( add );
24630         }, this);
24631         
24632     },
24633     /**
24634      * Mark fields in this form invalid in bulk.
24635      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24636      * @return {BasicForm} this
24637      */
24638     markInvalid : function(errors){
24639         if(errors instanceof Array){
24640             for(var i = 0, len = errors.length; i < len; i++){
24641                 var fieldError = errors[i];
24642                 var f = this.findField(fieldError.id);
24643                 if(f){
24644                     f.markInvalid(fieldError.msg);
24645                 }
24646             }
24647         }else{
24648             var field, id;
24649             for(id in errors){
24650                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24651                     field.markInvalid(errors[id]);
24652                 }
24653             }
24654         }
24655         Roo.each(this.childForms || [], function (f) {
24656             f.markInvalid(errors);
24657         });
24658         
24659         return this;
24660     },
24661
24662     /**
24663      * Set values for fields in this form in bulk.
24664      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24665      * @return {BasicForm} this
24666      */
24667     setValues : function(values){
24668         if(values instanceof Array){ // array of objects
24669             for(var i = 0, len = values.length; i < len; i++){
24670                 var v = values[i];
24671                 var f = this.findField(v.id);
24672                 if(f){
24673                     f.setValue(v.value);
24674                     if(this.trackResetOnLoad){
24675                         f.originalValue = f.getValue();
24676                     }
24677                 }
24678             }
24679         }else{ // object hash
24680             var field, id;
24681             for(id in values){
24682                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24683                     
24684                     if (field.setFromData && 
24685                         field.valueField && 
24686                         field.displayField &&
24687                         // combos' with local stores can 
24688                         // be queried via setValue()
24689                         // to set their value..
24690                         (field.store && !field.store.isLocal)
24691                         ) {
24692                         // it's a combo
24693                         var sd = { };
24694                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24695                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24696                         field.setFromData(sd);
24697                         
24698                     } else {
24699                         field.setValue(values[id]);
24700                     }
24701                     
24702                     
24703                     if(this.trackResetOnLoad){
24704                         field.originalValue = field.getValue();
24705                     }
24706                 }
24707             }
24708         }
24709         this.resetHasChanged();
24710         
24711         
24712         Roo.each(this.childForms || [], function (f) {
24713             f.setValues(values);
24714             f.resetHasChanged();
24715         });
24716                 
24717         return this;
24718     },
24719
24720     /**
24721      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24722      * they are returned as an array.
24723      * @param {Boolean} asString
24724      * @return {Object}
24725      */
24726     getValues : function(asString){
24727         if (this.childForms) {
24728             // copy values from the child forms
24729             Roo.each(this.childForms, function (f) {
24730                 this.setValues(f.getValues());
24731             }, this);
24732         }
24733         
24734         
24735         
24736         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24737         if(asString === true){
24738             return fs;
24739         }
24740         return Roo.urlDecode(fs);
24741     },
24742     
24743     /**
24744      * Returns the fields in this form as an object with key/value pairs. 
24745      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24746      * @return {Object}
24747      */
24748     getFieldValues : function(with_hidden)
24749     {
24750         if (this.childForms) {
24751             // copy values from the child forms
24752             // should this call getFieldValues - probably not as we do not currently copy
24753             // hidden fields when we generate..
24754             Roo.each(this.childForms, function (f) {
24755                 this.setValues(f.getValues());
24756             }, this);
24757         }
24758         
24759         var ret = {};
24760         this.items.each(function(f){
24761             if (!f.getName()) {
24762                 return;
24763             }
24764             var v = f.getValue();
24765             if (f.inputType =='radio') {
24766                 if (typeof(ret[f.getName()]) == 'undefined') {
24767                     ret[f.getName()] = ''; // empty..
24768                 }
24769                 
24770                 if (!f.el.dom.checked) {
24771                     return;
24772                     
24773                 }
24774                 v = f.el.dom.value;
24775                 
24776             }
24777             
24778             // not sure if this supported any more..
24779             if ((typeof(v) == 'object') && f.getRawValue) {
24780                 v = f.getRawValue() ; // dates..
24781             }
24782             // combo boxes where name != hiddenName...
24783             if (f.name != f.getName()) {
24784                 ret[f.name] = f.getRawValue();
24785             }
24786             ret[f.getName()] = v;
24787         });
24788         
24789         return ret;
24790     },
24791
24792     /**
24793      * Clears all invalid messages in this form.
24794      * @return {BasicForm} this
24795      */
24796     clearInvalid : function(){
24797         this.items.each(function(f){
24798            f.clearInvalid();
24799         });
24800         
24801         Roo.each(this.childForms || [], function (f) {
24802             f.clearInvalid();
24803         });
24804         
24805         
24806         return this;
24807     },
24808
24809     /**
24810      * Resets this form.
24811      * @return {BasicForm} this
24812      */
24813     reset : function(){
24814         this.items.each(function(f){
24815             f.reset();
24816         });
24817         
24818         Roo.each(this.childForms || [], function (f) {
24819             f.reset();
24820         });
24821         this.resetHasChanged();
24822         
24823         return this;
24824     },
24825
24826     /**
24827      * Add Roo.form components to this form.
24828      * @param {Field} field1
24829      * @param {Field} field2 (optional)
24830      * @param {Field} etc (optional)
24831      * @return {BasicForm} this
24832      */
24833     add : function(){
24834         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24835         return this;
24836     },
24837
24838
24839     /**
24840      * Removes a field from the items collection (does NOT remove its markup).
24841      * @param {Field} field
24842      * @return {BasicForm} this
24843      */
24844     remove : function(field){
24845         this.items.remove(field);
24846         return this;
24847     },
24848
24849     /**
24850      * Looks at the fields in this form, checks them for an id attribute,
24851      * and calls applyTo on the existing dom element with that id.
24852      * @return {BasicForm} this
24853      */
24854     render : function(){
24855         this.items.each(function(f){
24856             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24857                 f.applyTo(f.id);
24858             }
24859         });
24860         return this;
24861     },
24862
24863     /**
24864      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24865      * @param {Object} values
24866      * @return {BasicForm} this
24867      */
24868     applyToFields : function(o){
24869         this.items.each(function(f){
24870            Roo.apply(f, o);
24871         });
24872         return this;
24873     },
24874
24875     /**
24876      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24877      * @param {Object} values
24878      * @return {BasicForm} this
24879      */
24880     applyIfToFields : function(o){
24881         this.items.each(function(f){
24882            Roo.applyIf(f, o);
24883         });
24884         return this;
24885     }
24886 });
24887
24888 // back compat
24889 Roo.BasicForm = Roo.form.BasicForm;/*
24890  * Based on:
24891  * Ext JS Library 1.1.1
24892  * Copyright(c) 2006-2007, Ext JS, LLC.
24893  *
24894  * Originally Released Under LGPL - original licence link has changed is not relivant.
24895  *
24896  * Fork - LGPL
24897  * <script type="text/javascript">
24898  */
24899
24900 /**
24901  * @class Roo.form.Form
24902  * @extends Roo.form.BasicForm
24903  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24904  * @constructor
24905  * @param {Object} config Configuration options
24906  */
24907 Roo.form.Form = function(config){
24908     var xitems =  [];
24909     if (config.items) {
24910         xitems = config.items;
24911         delete config.items;
24912     }
24913    
24914     
24915     Roo.form.Form.superclass.constructor.call(this, null, config);
24916     this.url = this.url || this.action;
24917     if(!this.root){
24918         this.root = new Roo.form.Layout(Roo.applyIf({
24919             id: Roo.id()
24920         }, config));
24921     }
24922     this.active = this.root;
24923     /**
24924      * Array of all the buttons that have been added to this form via {@link addButton}
24925      * @type Array
24926      */
24927     this.buttons = [];
24928     this.allItems = [];
24929     this.addEvents({
24930         /**
24931          * @event clientvalidation
24932          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24933          * @param {Form} this
24934          * @param {Boolean} valid true if the form has passed client-side validation
24935          */
24936         clientvalidation: true,
24937         /**
24938          * @event rendered
24939          * Fires when the form is rendered
24940          * @param {Roo.form.Form} form
24941          */
24942         rendered : true
24943     });
24944     
24945     if (this.progressUrl) {
24946             // push a hidden field onto the list of fields..
24947             this.addxtype( {
24948                     xns: Roo.form, 
24949                     xtype : 'Hidden', 
24950                     name : 'UPLOAD_IDENTIFIER' 
24951             });
24952         }
24953         
24954     
24955     Roo.each(xitems, this.addxtype, this);
24956     
24957     
24958     
24959 };
24960
24961 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24962     /**
24963      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24964      */
24965     /**
24966      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24967      */
24968     /**
24969      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24970      */
24971     buttonAlign:'center',
24972
24973     /**
24974      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24975      */
24976     minButtonWidth:75,
24977
24978     /**
24979      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24980      * This property cascades to child containers if not set.
24981      */
24982     labelAlign:'left',
24983
24984     /**
24985      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24986      * fires a looping event with that state. This is required to bind buttons to the valid
24987      * state using the config value formBind:true on the button.
24988      */
24989     monitorValid : false,
24990
24991     /**
24992      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24993      */
24994     monitorPoll : 200,
24995     
24996     /**
24997      * @cfg {String} progressUrl - Url to return progress data 
24998      */
24999     
25000     progressUrl : false,
25001   
25002     /**
25003      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25004      * fields are added and the column is closed. If no fields are passed the column remains open
25005      * until end() is called.
25006      * @param {Object} config The config to pass to the column
25007      * @param {Field} field1 (optional)
25008      * @param {Field} field2 (optional)
25009      * @param {Field} etc (optional)
25010      * @return Column The column container object
25011      */
25012     column : function(c){
25013         var col = new Roo.form.Column(c);
25014         this.start(col);
25015         if(arguments.length > 1){ // duplicate code required because of Opera
25016             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25017             this.end();
25018         }
25019         return col;
25020     },
25021
25022     /**
25023      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25024      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25025      * until end() is called.
25026      * @param {Object} config The config to pass to the fieldset
25027      * @param {Field} field1 (optional)
25028      * @param {Field} field2 (optional)
25029      * @param {Field} etc (optional)
25030      * @return FieldSet The fieldset container object
25031      */
25032     fieldset : function(c){
25033         var fs = new Roo.form.FieldSet(c);
25034         this.start(fs);
25035         if(arguments.length > 1){ // duplicate code required because of Opera
25036             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25037             this.end();
25038         }
25039         return fs;
25040     },
25041
25042     /**
25043      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25044      * fields are added and the container is closed. If no fields are passed the container remains open
25045      * until end() is called.
25046      * @param {Object} config The config to pass to the Layout
25047      * @param {Field} field1 (optional)
25048      * @param {Field} field2 (optional)
25049      * @param {Field} etc (optional)
25050      * @return Layout The container object
25051      */
25052     container : function(c){
25053         var l = new Roo.form.Layout(c);
25054         this.start(l);
25055         if(arguments.length > 1){ // duplicate code required because of Opera
25056             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25057             this.end();
25058         }
25059         return l;
25060     },
25061
25062     /**
25063      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25064      * @param {Object} container A Roo.form.Layout or subclass of Layout
25065      * @return {Form} this
25066      */
25067     start : function(c){
25068         // cascade label info
25069         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25070         this.active.stack.push(c);
25071         c.ownerCt = this.active;
25072         this.active = c;
25073         return this;
25074     },
25075
25076     /**
25077      * Closes the current open container
25078      * @return {Form} this
25079      */
25080     end : function(){
25081         if(this.active == this.root){
25082             return this;
25083         }
25084         this.active = this.active.ownerCt;
25085         return this;
25086     },
25087
25088     /**
25089      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25090      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25091      * as the label of the field.
25092      * @param {Field} field1
25093      * @param {Field} field2 (optional)
25094      * @param {Field} etc. (optional)
25095      * @return {Form} this
25096      */
25097     add : function(){
25098         this.active.stack.push.apply(this.active.stack, arguments);
25099         this.allItems.push.apply(this.allItems,arguments);
25100         var r = [];
25101         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25102             if(a[i].isFormField){
25103                 r.push(a[i]);
25104             }
25105         }
25106         if(r.length > 0){
25107             Roo.form.Form.superclass.add.apply(this, r);
25108         }
25109         return this;
25110     },
25111     
25112
25113     
25114     
25115     
25116      /**
25117      * Find any element that has been added to a form, using it's ID or name
25118      * This can include framesets, columns etc. along with regular fields..
25119      * @param {String} id - id or name to find.
25120      
25121      * @return {Element} e - or false if nothing found.
25122      */
25123     findbyId : function(id)
25124     {
25125         var ret = false;
25126         if (!id) {
25127             return ret;
25128         }
25129         Roo.each(this.allItems, function(f){
25130             if (f.id == id || f.name == id ){
25131                 ret = f;
25132                 return false;
25133             }
25134         });
25135         return ret;
25136     },
25137
25138     
25139     
25140     /**
25141      * Render this form into the passed container. This should only be called once!
25142      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25143      * @return {Form} this
25144      */
25145     render : function(ct)
25146     {
25147         
25148         
25149         
25150         ct = Roo.get(ct);
25151         var o = this.autoCreate || {
25152             tag: 'form',
25153             method : this.method || 'POST',
25154             id : this.id || Roo.id()
25155         };
25156         this.initEl(ct.createChild(o));
25157
25158         this.root.render(this.el);
25159         
25160        
25161              
25162         this.items.each(function(f){
25163             f.render('x-form-el-'+f.id);
25164         });
25165
25166         if(this.buttons.length > 0){
25167             // tables are required to maintain order and for correct IE layout
25168             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25169                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25170                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25171             }}, null, true);
25172             var tr = tb.getElementsByTagName('tr')[0];
25173             for(var i = 0, len = this.buttons.length; i < len; i++) {
25174                 var b = this.buttons[i];
25175                 var td = document.createElement('td');
25176                 td.className = 'x-form-btn-td';
25177                 b.render(tr.appendChild(td));
25178             }
25179         }
25180         if(this.monitorValid){ // initialize after render
25181             this.startMonitoring();
25182         }
25183         this.fireEvent('rendered', this);
25184         return this;
25185     },
25186
25187     /**
25188      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25189      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25190      * object or a valid Roo.DomHelper element config
25191      * @param {Function} handler The function called when the button is clicked
25192      * @param {Object} scope (optional) The scope of the handler function
25193      * @return {Roo.Button}
25194      */
25195     addButton : function(config, handler, scope){
25196         var bc = {
25197             handler: handler,
25198             scope: scope,
25199             minWidth: this.minButtonWidth,
25200             hideParent:true
25201         };
25202         if(typeof config == "string"){
25203             bc.text = config;
25204         }else{
25205             Roo.apply(bc, config);
25206         }
25207         var btn = new Roo.Button(null, bc);
25208         this.buttons.push(btn);
25209         return btn;
25210     },
25211
25212      /**
25213      * Adds a series of form elements (using the xtype property as the factory method.
25214      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25215      * @param {Object} config 
25216      */
25217     
25218     addxtype : function()
25219     {
25220         var ar = Array.prototype.slice.call(arguments, 0);
25221         var ret = false;
25222         for(var i = 0; i < ar.length; i++) {
25223             if (!ar[i]) {
25224                 continue; // skip -- if this happends something invalid got sent, we 
25225                 // should ignore it, as basically that interface element will not show up
25226                 // and that should be pretty obvious!!
25227             }
25228             
25229             if (Roo.form[ar[i].xtype]) {
25230                 ar[i].form = this;
25231                 var fe = Roo.factory(ar[i], Roo.form);
25232                 if (!ret) {
25233                     ret = fe;
25234                 }
25235                 fe.form = this;
25236                 if (fe.store) {
25237                     fe.store.form = this;
25238                 }
25239                 if (fe.isLayout) {  
25240                          
25241                     this.start(fe);
25242                     this.allItems.push(fe);
25243                     if (fe.items && fe.addxtype) {
25244                         fe.addxtype.apply(fe, fe.items);
25245                         delete fe.items;
25246                     }
25247                      this.end();
25248                     continue;
25249                 }
25250                 
25251                 
25252                  
25253                 this.add(fe);
25254               //  console.log('adding ' + ar[i].xtype);
25255             }
25256             if (ar[i].xtype == 'Button') {  
25257                 //console.log('adding button');
25258                 //console.log(ar[i]);
25259                 this.addButton(ar[i]);
25260                 this.allItems.push(fe);
25261                 continue;
25262             }
25263             
25264             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25265                 alert('end is not supported on xtype any more, use items');
25266             //    this.end();
25267             //    //console.log('adding end');
25268             }
25269             
25270         }
25271         return ret;
25272     },
25273     
25274     /**
25275      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25276      * option "monitorValid"
25277      */
25278     startMonitoring : function(){
25279         if(!this.bound){
25280             this.bound = true;
25281             Roo.TaskMgr.start({
25282                 run : this.bindHandler,
25283                 interval : this.monitorPoll || 200,
25284                 scope: this
25285             });
25286         }
25287     },
25288
25289     /**
25290      * Stops monitoring of the valid state of this form
25291      */
25292     stopMonitoring : function(){
25293         this.bound = false;
25294     },
25295
25296     // private
25297     bindHandler : function(){
25298         if(!this.bound){
25299             return false; // stops binding
25300         }
25301         var valid = true;
25302         this.items.each(function(f){
25303             if(!f.isValid(true)){
25304                 valid = false;
25305                 return false;
25306             }
25307         });
25308         for(var i = 0, len = this.buttons.length; i < len; i++){
25309             var btn = this.buttons[i];
25310             if(btn.formBind === true && btn.disabled === valid){
25311                 btn.setDisabled(!valid);
25312             }
25313         }
25314         this.fireEvent('clientvalidation', this, valid);
25315     }
25316     
25317     
25318     
25319     
25320     
25321     
25322     
25323     
25324 });
25325
25326
25327 // back compat
25328 Roo.Form = Roo.form.Form;
25329 /*
25330  * Based on:
25331  * Ext JS Library 1.1.1
25332  * Copyright(c) 2006-2007, Ext JS, LLC.
25333  *
25334  * Originally Released Under LGPL - original licence link has changed is not relivant.
25335  *
25336  * Fork - LGPL
25337  * <script type="text/javascript">
25338  */
25339
25340 // as we use this in bootstrap.
25341 Roo.namespace('Roo.form');
25342  /**
25343  * @class Roo.form.Action
25344  * Internal Class used to handle form actions
25345  * @constructor
25346  * @param {Roo.form.BasicForm} el The form element or its id
25347  * @param {Object} config Configuration options
25348  */
25349
25350  
25351  
25352 // define the action interface
25353 Roo.form.Action = function(form, options){
25354     this.form = form;
25355     this.options = options || {};
25356 };
25357 /**
25358  * Client Validation Failed
25359  * @const 
25360  */
25361 Roo.form.Action.CLIENT_INVALID = 'client';
25362 /**
25363  * Server Validation Failed
25364  * @const 
25365  */
25366 Roo.form.Action.SERVER_INVALID = 'server';
25367  /**
25368  * Connect to Server Failed
25369  * @const 
25370  */
25371 Roo.form.Action.CONNECT_FAILURE = 'connect';
25372 /**
25373  * Reading Data from Server Failed
25374  * @const 
25375  */
25376 Roo.form.Action.LOAD_FAILURE = 'load';
25377
25378 Roo.form.Action.prototype = {
25379     type : 'default',
25380     failureType : undefined,
25381     response : undefined,
25382     result : undefined,
25383
25384     // interface method
25385     run : function(options){
25386
25387     },
25388
25389     // interface method
25390     success : function(response){
25391
25392     },
25393
25394     // interface method
25395     handleResponse : function(response){
25396
25397     },
25398
25399     // default connection failure
25400     failure : function(response){
25401         
25402         this.response = response;
25403         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25404         this.form.afterAction(this, false);
25405     },
25406
25407     processResponse : function(response){
25408         this.response = response;
25409         if(!response.responseText){
25410             return true;
25411         }
25412         this.result = this.handleResponse(response);
25413         return this.result;
25414     },
25415
25416     // utility functions used internally
25417     getUrl : function(appendParams){
25418         var url = this.options.url || this.form.url || this.form.el.dom.action;
25419         if(appendParams){
25420             var p = this.getParams();
25421             if(p){
25422                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25423             }
25424         }
25425         return url;
25426     },
25427
25428     getMethod : function(){
25429         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25430     },
25431
25432     getParams : function(){
25433         var bp = this.form.baseParams;
25434         var p = this.options.params;
25435         if(p){
25436             if(typeof p == "object"){
25437                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25438             }else if(typeof p == 'string' && bp){
25439                 p += '&' + Roo.urlEncode(bp);
25440             }
25441         }else if(bp){
25442             p = Roo.urlEncode(bp);
25443         }
25444         return p;
25445     },
25446
25447     createCallback : function(){
25448         return {
25449             success: this.success,
25450             failure: this.failure,
25451             scope: this,
25452             timeout: (this.form.timeout*1000),
25453             upload: this.form.fileUpload ? this.success : undefined
25454         };
25455     }
25456 };
25457
25458 Roo.form.Action.Submit = function(form, options){
25459     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25460 };
25461
25462 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25463     type : 'submit',
25464
25465     haveProgress : false,
25466     uploadComplete : false,
25467     
25468     // uploadProgress indicator.
25469     uploadProgress : function()
25470     {
25471         if (!this.form.progressUrl) {
25472             return;
25473         }
25474         
25475         if (!this.haveProgress) {
25476             Roo.MessageBox.progress("Uploading", "Uploading");
25477         }
25478         if (this.uploadComplete) {
25479            Roo.MessageBox.hide();
25480            return;
25481         }
25482         
25483         this.haveProgress = true;
25484    
25485         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25486         
25487         var c = new Roo.data.Connection();
25488         c.request({
25489             url : this.form.progressUrl,
25490             params: {
25491                 id : uid
25492             },
25493             method: 'GET',
25494             success : function(req){
25495                //console.log(data);
25496                 var rdata = false;
25497                 var edata;
25498                 try  {
25499                    rdata = Roo.decode(req.responseText)
25500                 } catch (e) {
25501                     Roo.log("Invalid data from server..");
25502                     Roo.log(edata);
25503                     return;
25504                 }
25505                 if (!rdata || !rdata.success) {
25506                     Roo.log(rdata);
25507                     Roo.MessageBox.alert(Roo.encode(rdata));
25508                     return;
25509                 }
25510                 var data = rdata.data;
25511                 
25512                 if (this.uploadComplete) {
25513                    Roo.MessageBox.hide();
25514                    return;
25515                 }
25516                    
25517                 if (data){
25518                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25519                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25520                     );
25521                 }
25522                 this.uploadProgress.defer(2000,this);
25523             },
25524        
25525             failure: function(data) {
25526                 Roo.log('progress url failed ');
25527                 Roo.log(data);
25528             },
25529             scope : this
25530         });
25531            
25532     },
25533     
25534     
25535     run : function()
25536     {
25537         // run get Values on the form, so it syncs any secondary forms.
25538         this.form.getValues();
25539         
25540         var o = this.options;
25541         var method = this.getMethod();
25542         var isPost = method == 'POST';
25543         if(o.clientValidation === false || this.form.isValid()){
25544             
25545             if (this.form.progressUrl) {
25546                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25547                     (new Date() * 1) + '' + Math.random());
25548                     
25549             } 
25550             
25551             
25552             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25553                 form:this.form.el.dom,
25554                 url:this.getUrl(!isPost),
25555                 method: method,
25556                 params:isPost ? this.getParams() : null,
25557                 isUpload: this.form.fileUpload
25558             }));
25559             
25560             this.uploadProgress();
25561
25562         }else if (o.clientValidation !== false){ // client validation failed
25563             this.failureType = Roo.form.Action.CLIENT_INVALID;
25564             this.form.afterAction(this, false);
25565         }
25566     },
25567
25568     success : function(response)
25569     {
25570         this.uploadComplete= true;
25571         if (this.haveProgress) {
25572             Roo.MessageBox.hide();
25573         }
25574         
25575         
25576         var result = this.processResponse(response);
25577         if(result === true || result.success){
25578             this.form.afterAction(this, true);
25579             return;
25580         }
25581         if(result.errors){
25582             this.form.markInvalid(result.errors);
25583             this.failureType = Roo.form.Action.SERVER_INVALID;
25584         }
25585         this.form.afterAction(this, false);
25586     },
25587     failure : function(response)
25588     {
25589         this.uploadComplete= true;
25590         if (this.haveProgress) {
25591             Roo.MessageBox.hide();
25592         }
25593         
25594         this.response = response;
25595         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25596         this.form.afterAction(this, false);
25597     },
25598     
25599     handleResponse : function(response){
25600         if(this.form.errorReader){
25601             var rs = this.form.errorReader.read(response);
25602             var errors = [];
25603             if(rs.records){
25604                 for(var i = 0, len = rs.records.length; i < len; i++) {
25605                     var r = rs.records[i];
25606                     errors[i] = r.data;
25607                 }
25608             }
25609             if(errors.length < 1){
25610                 errors = null;
25611             }
25612             return {
25613                 success : rs.success,
25614                 errors : errors
25615             };
25616         }
25617         var ret = false;
25618         try {
25619             ret = Roo.decode(response.responseText);
25620         } catch (e) {
25621             ret = {
25622                 success: false,
25623                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25624                 errors : []
25625             };
25626         }
25627         return ret;
25628         
25629     }
25630 });
25631
25632
25633 Roo.form.Action.Load = function(form, options){
25634     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25635     this.reader = this.form.reader;
25636 };
25637
25638 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25639     type : 'load',
25640
25641     run : function(){
25642         
25643         Roo.Ajax.request(Roo.apply(
25644                 this.createCallback(), {
25645                     method:this.getMethod(),
25646                     url:this.getUrl(false),
25647                     params:this.getParams()
25648         }));
25649     },
25650
25651     success : function(response){
25652         
25653         var result = this.processResponse(response);
25654         if(result === true || !result.success || !result.data){
25655             this.failureType = Roo.form.Action.LOAD_FAILURE;
25656             this.form.afterAction(this, false);
25657             return;
25658         }
25659         this.form.clearInvalid();
25660         this.form.setValues(result.data);
25661         this.form.afterAction(this, true);
25662     },
25663
25664     handleResponse : function(response){
25665         if(this.form.reader){
25666             var rs = this.form.reader.read(response);
25667             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25668             return {
25669                 success : rs.success,
25670                 data : data
25671             };
25672         }
25673         return Roo.decode(response.responseText);
25674     }
25675 });
25676
25677 Roo.form.Action.ACTION_TYPES = {
25678     'load' : Roo.form.Action.Load,
25679     'submit' : Roo.form.Action.Submit
25680 };/*
25681  * Based on:
25682  * Ext JS Library 1.1.1
25683  * Copyright(c) 2006-2007, Ext JS, LLC.
25684  *
25685  * Originally Released Under LGPL - original licence link has changed is not relivant.
25686  *
25687  * Fork - LGPL
25688  * <script type="text/javascript">
25689  */
25690  
25691 /**
25692  * @class Roo.form.Layout
25693  * @extends Roo.Component
25694  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25695  * @constructor
25696  * @param {Object} config Configuration options
25697  */
25698 Roo.form.Layout = function(config){
25699     var xitems = [];
25700     if (config.items) {
25701         xitems = config.items;
25702         delete config.items;
25703     }
25704     Roo.form.Layout.superclass.constructor.call(this, config);
25705     this.stack = [];
25706     Roo.each(xitems, this.addxtype, this);
25707      
25708 };
25709
25710 Roo.extend(Roo.form.Layout, Roo.Component, {
25711     /**
25712      * @cfg {String/Object} autoCreate
25713      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25714      */
25715     /**
25716      * @cfg {String/Object/Function} style
25717      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25718      * a function which returns such a specification.
25719      */
25720     /**
25721      * @cfg {String} labelAlign
25722      * Valid values are "left," "top" and "right" (defaults to "left")
25723      */
25724     /**
25725      * @cfg {Number} labelWidth
25726      * Fixed width in pixels of all field labels (defaults to undefined)
25727      */
25728     /**
25729      * @cfg {Boolean} clear
25730      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25731      */
25732     clear : true,
25733     /**
25734      * @cfg {String} labelSeparator
25735      * The separator to use after field labels (defaults to ':')
25736      */
25737     labelSeparator : ':',
25738     /**
25739      * @cfg {Boolean} hideLabels
25740      * True to suppress the display of field labels in this layout (defaults to false)
25741      */
25742     hideLabels : false,
25743
25744     // private
25745     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25746     
25747     isLayout : true,
25748     
25749     // private
25750     onRender : function(ct, position){
25751         if(this.el){ // from markup
25752             this.el = Roo.get(this.el);
25753         }else {  // generate
25754             var cfg = this.getAutoCreate();
25755             this.el = ct.createChild(cfg, position);
25756         }
25757         if(this.style){
25758             this.el.applyStyles(this.style);
25759         }
25760         if(this.labelAlign){
25761             this.el.addClass('x-form-label-'+this.labelAlign);
25762         }
25763         if(this.hideLabels){
25764             this.labelStyle = "display:none";
25765             this.elementStyle = "padding-left:0;";
25766         }else{
25767             if(typeof this.labelWidth == 'number'){
25768                 this.labelStyle = "width:"+this.labelWidth+"px;";
25769                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25770             }
25771             if(this.labelAlign == 'top'){
25772                 this.labelStyle = "width:auto;";
25773                 this.elementStyle = "padding-left:0;";
25774             }
25775         }
25776         var stack = this.stack;
25777         var slen = stack.length;
25778         if(slen > 0){
25779             if(!this.fieldTpl){
25780                 var t = new Roo.Template(
25781                     '<div class="x-form-item {5}">',
25782                         '<label for="{0}" style="{2}">{1}{4}</label>',
25783                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25784                         '</div>',
25785                     '</div><div class="x-form-clear-left"></div>'
25786                 );
25787                 t.disableFormats = true;
25788                 t.compile();
25789                 Roo.form.Layout.prototype.fieldTpl = t;
25790             }
25791             for(var i = 0; i < slen; i++) {
25792                 if(stack[i].isFormField){
25793                     this.renderField(stack[i]);
25794                 }else{
25795                     this.renderComponent(stack[i]);
25796                 }
25797             }
25798         }
25799         if(this.clear){
25800             this.el.createChild({cls:'x-form-clear'});
25801         }
25802     },
25803
25804     // private
25805     renderField : function(f){
25806         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25807                f.id, //0
25808                f.fieldLabel, //1
25809                f.labelStyle||this.labelStyle||'', //2
25810                this.elementStyle||'', //3
25811                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25812                f.itemCls||this.itemCls||''  //5
25813        ], true).getPrevSibling());
25814     },
25815
25816     // private
25817     renderComponent : function(c){
25818         c.render(c.isLayout ? this.el : this.el.createChild());    
25819     },
25820     /**
25821      * Adds a object form elements (using the xtype property as the factory method.)
25822      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25823      * @param {Object} config 
25824      */
25825     addxtype : function(o)
25826     {
25827         // create the lement.
25828         o.form = this.form;
25829         var fe = Roo.factory(o, Roo.form);
25830         this.form.allItems.push(fe);
25831         this.stack.push(fe);
25832         
25833         if (fe.isFormField) {
25834             this.form.items.add(fe);
25835         }
25836          
25837         return fe;
25838     }
25839 });
25840
25841 /**
25842  * @class Roo.form.Column
25843  * @extends Roo.form.Layout
25844  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25845  * @constructor
25846  * @param {Object} config Configuration options
25847  */
25848 Roo.form.Column = function(config){
25849     Roo.form.Column.superclass.constructor.call(this, config);
25850 };
25851
25852 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25853     /**
25854      * @cfg {Number/String} width
25855      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25856      */
25857     /**
25858      * @cfg {String/Object} autoCreate
25859      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25860      */
25861
25862     // private
25863     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25864
25865     // private
25866     onRender : function(ct, position){
25867         Roo.form.Column.superclass.onRender.call(this, ct, position);
25868         if(this.width){
25869             this.el.setWidth(this.width);
25870         }
25871     }
25872 });
25873
25874
25875 /**
25876  * @class Roo.form.Row
25877  * @extends Roo.form.Layout
25878  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25879  * @constructor
25880  * @param {Object} config Configuration options
25881  */
25882
25883  
25884 Roo.form.Row = function(config){
25885     Roo.form.Row.superclass.constructor.call(this, config);
25886 };
25887  
25888 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25889       /**
25890      * @cfg {Number/String} width
25891      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25892      */
25893     /**
25894      * @cfg {Number/String} height
25895      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25896      */
25897     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25898     
25899     padWidth : 20,
25900     // private
25901     onRender : function(ct, position){
25902         //console.log('row render');
25903         if(!this.rowTpl){
25904             var t = new Roo.Template(
25905                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25906                     '<label for="{0}" style="{2}">{1}{4}</label>',
25907                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25908                     '</div>',
25909                 '</div>'
25910             );
25911             t.disableFormats = true;
25912             t.compile();
25913             Roo.form.Layout.prototype.rowTpl = t;
25914         }
25915         this.fieldTpl = this.rowTpl;
25916         
25917         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25918         var labelWidth = 100;
25919         
25920         if ((this.labelAlign != 'top')) {
25921             if (typeof this.labelWidth == 'number') {
25922                 labelWidth = this.labelWidth
25923             }
25924             this.padWidth =  20 + labelWidth;
25925             
25926         }
25927         
25928         Roo.form.Column.superclass.onRender.call(this, ct, position);
25929         if(this.width){
25930             this.el.setWidth(this.width);
25931         }
25932         if(this.height){
25933             this.el.setHeight(this.height);
25934         }
25935     },
25936     
25937     // private
25938     renderField : function(f){
25939         f.fieldEl = this.fieldTpl.append(this.el, [
25940                f.id, f.fieldLabel,
25941                f.labelStyle||this.labelStyle||'',
25942                this.elementStyle||'',
25943                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25944                f.itemCls||this.itemCls||'',
25945                f.width ? f.width + this.padWidth : 160 + this.padWidth
25946        ],true);
25947     }
25948 });
25949  
25950
25951 /**
25952  * @class Roo.form.FieldSet
25953  * @extends Roo.form.Layout
25954  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25955  * @constructor
25956  * @param {Object} config Configuration options
25957  */
25958 Roo.form.FieldSet = function(config){
25959     Roo.form.FieldSet.superclass.constructor.call(this, config);
25960 };
25961
25962 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25963     /**
25964      * @cfg {String} legend
25965      * The text to display as the legend for the FieldSet (defaults to '')
25966      */
25967     /**
25968      * @cfg {String/Object} autoCreate
25969      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25970      */
25971
25972     // private
25973     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25974
25975     // private
25976     onRender : function(ct, position){
25977         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25978         if(this.legend){
25979             this.setLegend(this.legend);
25980         }
25981     },
25982
25983     // private
25984     setLegend : function(text){
25985         if(this.rendered){
25986             this.el.child('legend').update(text);
25987         }
25988     }
25989 });/*
25990  * Based on:
25991  * Ext JS Library 1.1.1
25992  * Copyright(c) 2006-2007, Ext JS, LLC.
25993  *
25994  * Originally Released Under LGPL - original licence link has changed is not relivant.
25995  *
25996  * Fork - LGPL
25997  * <script type="text/javascript">
25998  */
25999 /**
26000  * @class Roo.form.VTypes
26001  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26002  * @singleton
26003  */
26004 Roo.form.VTypes = function(){
26005     // closure these in so they are only created once.
26006     var alpha = /^[a-zA-Z_]+$/;
26007     var alphanum = /^[a-zA-Z0-9_]+$/;
26008     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26009     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26010
26011     // All these messages and functions are configurable
26012     return {
26013         /**
26014          * The function used to validate email addresses
26015          * @param {String} value The email address
26016          */
26017         'email' : function(v){
26018             return email.test(v);
26019         },
26020         /**
26021          * The error text to display when the email validation function returns false
26022          * @type String
26023          */
26024         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26025         /**
26026          * The keystroke filter mask to be applied on email input
26027          * @type RegExp
26028          */
26029         'emailMask' : /[a-z0-9_\.\-@]/i,
26030
26031         /**
26032          * The function used to validate URLs
26033          * @param {String} value The URL
26034          */
26035         'url' : function(v){
26036             return url.test(v);
26037         },
26038         /**
26039          * The error text to display when the url validation function returns false
26040          * @type String
26041          */
26042         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26043         
26044         /**
26045          * The function used to validate alpha values
26046          * @param {String} value The value
26047          */
26048         'alpha' : function(v){
26049             return alpha.test(v);
26050         },
26051         /**
26052          * The error text to display when the alpha validation function returns false
26053          * @type String
26054          */
26055         'alphaText' : 'This field should only contain letters and _',
26056         /**
26057          * The keystroke filter mask to be applied on alpha input
26058          * @type RegExp
26059          */
26060         'alphaMask' : /[a-z_]/i,
26061
26062         /**
26063          * The function used to validate alphanumeric values
26064          * @param {String} value The value
26065          */
26066         'alphanum' : function(v){
26067             return alphanum.test(v);
26068         },
26069         /**
26070          * The error text to display when the alphanumeric validation function returns false
26071          * @type String
26072          */
26073         'alphanumText' : 'This field should only contain letters, numbers and _',
26074         /**
26075          * The keystroke filter mask to be applied on alphanumeric input
26076          * @type RegExp
26077          */
26078         'alphanumMask' : /[a-z0-9_]/i
26079     };
26080 }();//<script type="text/javascript">
26081
26082 /**
26083  * @class Roo.form.FCKeditor
26084  * @extends Roo.form.TextArea
26085  * Wrapper around the FCKEditor http://www.fckeditor.net
26086  * @constructor
26087  * Creates a new FCKeditor
26088  * @param {Object} config Configuration options
26089  */
26090 Roo.form.FCKeditor = function(config){
26091     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26092     this.addEvents({
26093          /**
26094          * @event editorinit
26095          * Fired when the editor is initialized - you can add extra handlers here..
26096          * @param {FCKeditor} this
26097          * @param {Object} the FCK object.
26098          */
26099         editorinit : true
26100     });
26101     
26102     
26103 };
26104 Roo.form.FCKeditor.editors = { };
26105 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26106 {
26107     //defaultAutoCreate : {
26108     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26109     //},
26110     // private
26111     /**
26112      * @cfg {Object} fck options - see fck manual for details.
26113      */
26114     fckconfig : false,
26115     
26116     /**
26117      * @cfg {Object} fck toolbar set (Basic or Default)
26118      */
26119     toolbarSet : 'Basic',
26120     /**
26121      * @cfg {Object} fck BasePath
26122      */ 
26123     basePath : '/fckeditor/',
26124     
26125     
26126     frame : false,
26127     
26128     value : '',
26129     
26130    
26131     onRender : function(ct, position)
26132     {
26133         if(!this.el){
26134             this.defaultAutoCreate = {
26135                 tag: "textarea",
26136                 style:"width:300px;height:60px;",
26137                 autocomplete: "new-password"
26138             };
26139         }
26140         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26141         /*
26142         if(this.grow){
26143             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26144             if(this.preventScrollbars){
26145                 this.el.setStyle("overflow", "hidden");
26146             }
26147             this.el.setHeight(this.growMin);
26148         }
26149         */
26150         //console.log('onrender' + this.getId() );
26151         Roo.form.FCKeditor.editors[this.getId()] = this;
26152          
26153
26154         this.replaceTextarea() ;
26155         
26156     },
26157     
26158     getEditor : function() {
26159         return this.fckEditor;
26160     },
26161     /**
26162      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26163      * @param {Mixed} value The value to set
26164      */
26165     
26166     
26167     setValue : function(value)
26168     {
26169         //console.log('setValue: ' + value);
26170         
26171         if(typeof(value) == 'undefined') { // not sure why this is happending...
26172             return;
26173         }
26174         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26175         
26176         //if(!this.el || !this.getEditor()) {
26177         //    this.value = value;
26178             //this.setValue.defer(100,this,[value]);    
26179         //    return;
26180         //} 
26181         
26182         if(!this.getEditor()) {
26183             return;
26184         }
26185         
26186         this.getEditor().SetData(value);
26187         
26188         //
26189
26190     },
26191
26192     /**
26193      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26194      * @return {Mixed} value The field value
26195      */
26196     getValue : function()
26197     {
26198         
26199         if (this.frame && this.frame.dom.style.display == 'none') {
26200             return Roo.form.FCKeditor.superclass.getValue.call(this);
26201         }
26202         
26203         if(!this.el || !this.getEditor()) {
26204            
26205            // this.getValue.defer(100,this); 
26206             return this.value;
26207         }
26208        
26209         
26210         var value=this.getEditor().GetData();
26211         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26212         return Roo.form.FCKeditor.superclass.getValue.call(this);
26213         
26214
26215     },
26216
26217     /**
26218      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26219      * @return {Mixed} value The field value
26220      */
26221     getRawValue : function()
26222     {
26223         if (this.frame && this.frame.dom.style.display == 'none') {
26224             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26225         }
26226         
26227         if(!this.el || !this.getEditor()) {
26228             //this.getRawValue.defer(100,this); 
26229             return this.value;
26230             return;
26231         }
26232         
26233         
26234         
26235         var value=this.getEditor().GetData();
26236         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26237         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26238          
26239     },
26240     
26241     setSize : function(w,h) {
26242         
26243         
26244         
26245         //if (this.frame && this.frame.dom.style.display == 'none') {
26246         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26247         //    return;
26248         //}
26249         //if(!this.el || !this.getEditor()) {
26250         //    this.setSize.defer(100,this, [w,h]); 
26251         //    return;
26252         //}
26253         
26254         
26255         
26256         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26257         
26258         this.frame.dom.setAttribute('width', w);
26259         this.frame.dom.setAttribute('height', h);
26260         this.frame.setSize(w,h);
26261         
26262     },
26263     
26264     toggleSourceEdit : function(value) {
26265         
26266       
26267          
26268         this.el.dom.style.display = value ? '' : 'none';
26269         this.frame.dom.style.display = value ?  'none' : '';
26270         
26271     },
26272     
26273     
26274     focus: function(tag)
26275     {
26276         if (this.frame.dom.style.display == 'none') {
26277             return Roo.form.FCKeditor.superclass.focus.call(this);
26278         }
26279         if(!this.el || !this.getEditor()) {
26280             this.focus.defer(100,this, [tag]); 
26281             return;
26282         }
26283         
26284         
26285         
26286         
26287         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26288         this.getEditor().Focus();
26289         if (tgs.length) {
26290             if (!this.getEditor().Selection.GetSelection()) {
26291                 this.focus.defer(100,this, [tag]); 
26292                 return;
26293             }
26294             
26295             
26296             var r = this.getEditor().EditorDocument.createRange();
26297             r.setStart(tgs[0],0);
26298             r.setEnd(tgs[0],0);
26299             this.getEditor().Selection.GetSelection().removeAllRanges();
26300             this.getEditor().Selection.GetSelection().addRange(r);
26301             this.getEditor().Focus();
26302         }
26303         
26304     },
26305     
26306     
26307     
26308     replaceTextarea : function()
26309     {
26310         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26311             return ;
26312         }
26313         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26314         //{
26315             // We must check the elements firstly using the Id and then the name.
26316         var oTextarea = document.getElementById( this.getId() );
26317         
26318         var colElementsByName = document.getElementsByName( this.getId() ) ;
26319          
26320         oTextarea.style.display = 'none' ;
26321
26322         if ( oTextarea.tabIndex ) {            
26323             this.TabIndex = oTextarea.tabIndex ;
26324         }
26325         
26326         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26327         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26328         this.frame = Roo.get(this.getId() + '___Frame')
26329     },
26330     
26331     _getConfigHtml : function()
26332     {
26333         var sConfig = '' ;
26334
26335         for ( var o in this.fckconfig ) {
26336             sConfig += sConfig.length > 0  ? '&amp;' : '';
26337             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26338         }
26339
26340         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26341     },
26342     
26343     
26344     _getIFrameHtml : function()
26345     {
26346         var sFile = 'fckeditor.html' ;
26347         /* no idea what this is about..
26348         try
26349         {
26350             if ( (/fcksource=true/i).test( window.top.location.search ) )
26351                 sFile = 'fckeditor.original.html' ;
26352         }
26353         catch (e) { 
26354         */
26355
26356         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26357         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26358         
26359         
26360         var html = '<iframe id="' + this.getId() +
26361             '___Frame" src="' + sLink +
26362             '" width="' + this.width +
26363             '" height="' + this.height + '"' +
26364             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26365             ' frameborder="0" scrolling="no"></iframe>' ;
26366
26367         return html ;
26368     },
26369     
26370     _insertHtmlBefore : function( html, element )
26371     {
26372         if ( element.insertAdjacentHTML )       {
26373             // IE
26374             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26375         } else { // Gecko
26376             var oRange = document.createRange() ;
26377             oRange.setStartBefore( element ) ;
26378             var oFragment = oRange.createContextualFragment( html );
26379             element.parentNode.insertBefore( oFragment, element ) ;
26380         }
26381     }
26382     
26383     
26384   
26385     
26386     
26387     
26388     
26389
26390 });
26391
26392 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26393
26394 function FCKeditor_OnComplete(editorInstance){
26395     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26396     f.fckEditor = editorInstance;
26397     //console.log("loaded");
26398     f.fireEvent('editorinit', f, editorInstance);
26399
26400   
26401
26402  
26403
26404
26405
26406
26407
26408
26409
26410
26411
26412
26413
26414
26415
26416
26417
26418 //<script type="text/javascript">
26419 /**
26420  * @class Roo.form.GridField
26421  * @extends Roo.form.Field
26422  * Embed a grid (or editable grid into a form)
26423  * STATUS ALPHA
26424  * 
26425  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26426  * it needs 
26427  * xgrid.store = Roo.data.Store
26428  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26429  * xgrid.store.reader = Roo.data.JsonReader 
26430  * 
26431  * 
26432  * @constructor
26433  * Creates a new GridField
26434  * @param {Object} config Configuration options
26435  */
26436 Roo.form.GridField = function(config){
26437     Roo.form.GridField.superclass.constructor.call(this, config);
26438      
26439 };
26440
26441 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26442     /**
26443      * @cfg {Number} width  - used to restrict width of grid..
26444      */
26445     width : 100,
26446     /**
26447      * @cfg {Number} height - used to restrict height of grid..
26448      */
26449     height : 50,
26450      /**
26451      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26452          * 
26453          *}
26454      */
26455     xgrid : false, 
26456     /**
26457      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26458      * {tag: "input", type: "checkbox", autocomplete: "off"})
26459      */
26460    // defaultAutoCreate : { tag: 'div' },
26461     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26462     /**
26463      * @cfg {String} addTitle Text to include for adding a title.
26464      */
26465     addTitle : false,
26466     //
26467     onResize : function(){
26468         Roo.form.Field.superclass.onResize.apply(this, arguments);
26469     },
26470
26471     initEvents : function(){
26472         // Roo.form.Checkbox.superclass.initEvents.call(this);
26473         // has no events...
26474        
26475     },
26476
26477
26478     getResizeEl : function(){
26479         return this.wrap;
26480     },
26481
26482     getPositionEl : function(){
26483         return this.wrap;
26484     },
26485
26486     // private
26487     onRender : function(ct, position){
26488         
26489         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26490         var style = this.style;
26491         delete this.style;
26492         
26493         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26494         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26495         this.viewEl = this.wrap.createChild({ tag: 'div' });
26496         if (style) {
26497             this.viewEl.applyStyles(style);
26498         }
26499         if (this.width) {
26500             this.viewEl.setWidth(this.width);
26501         }
26502         if (this.height) {
26503             this.viewEl.setHeight(this.height);
26504         }
26505         //if(this.inputValue !== undefined){
26506         //this.setValue(this.value);
26507         
26508         
26509         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26510         
26511         
26512         this.grid.render();
26513         this.grid.getDataSource().on('remove', this.refreshValue, this);
26514         this.grid.getDataSource().on('update', this.refreshValue, this);
26515         this.grid.on('afteredit', this.refreshValue, this);
26516  
26517     },
26518      
26519     
26520     /**
26521      * Sets the value of the item. 
26522      * @param {String} either an object  or a string..
26523      */
26524     setValue : function(v){
26525         //this.value = v;
26526         v = v || []; // empty set..
26527         // this does not seem smart - it really only affects memoryproxy grids..
26528         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26529             var ds = this.grid.getDataSource();
26530             // assumes a json reader..
26531             var data = {}
26532             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26533             ds.loadData( data);
26534         }
26535         // clear selection so it does not get stale.
26536         if (this.grid.sm) { 
26537             this.grid.sm.clearSelections();
26538         }
26539         
26540         Roo.form.GridField.superclass.setValue.call(this, v);
26541         this.refreshValue();
26542         // should load data in the grid really....
26543     },
26544     
26545     // private
26546     refreshValue: function() {
26547          var val = [];
26548         this.grid.getDataSource().each(function(r) {
26549             val.push(r.data);
26550         });
26551         this.el.dom.value = Roo.encode(val);
26552     }
26553     
26554      
26555     
26556     
26557 });/*
26558  * Based on:
26559  * Ext JS Library 1.1.1
26560  * Copyright(c) 2006-2007, Ext JS, LLC.
26561  *
26562  * Originally Released Under LGPL - original licence link has changed is not relivant.
26563  *
26564  * Fork - LGPL
26565  * <script type="text/javascript">
26566  */
26567 /**
26568  * @class Roo.form.DisplayField
26569  * @extends Roo.form.Field
26570  * A generic Field to display non-editable data.
26571  * @cfg {Boolean} closable (true|false) default false
26572  * @constructor
26573  * Creates a new Display Field item.
26574  * @param {Object} config Configuration options
26575  */
26576 Roo.form.DisplayField = function(config){
26577     Roo.form.DisplayField.superclass.constructor.call(this, config);
26578     
26579     this.addEvents({
26580         /**
26581          * @event close
26582          * Fires after the click the close btn
26583              * @param {Roo.form.DisplayField} this
26584              */
26585         close : true
26586     });
26587 };
26588
26589 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26590     inputType:      'hidden',
26591     allowBlank:     true,
26592     readOnly:         true,
26593     
26594  
26595     /**
26596      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26597      */
26598     focusClass : undefined,
26599     /**
26600      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26601      */
26602     fieldClass: 'x-form-field',
26603     
26604      /**
26605      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26606      */
26607     valueRenderer: undefined,
26608     
26609     width: 100,
26610     /**
26611      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26612      * {tag: "input", type: "checkbox", autocomplete: "off"})
26613      */
26614      
26615  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26616  
26617     closable : false,
26618     
26619     onResize : function(){
26620         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26621         
26622     },
26623
26624     initEvents : function(){
26625         // Roo.form.Checkbox.superclass.initEvents.call(this);
26626         // has no events...
26627         
26628         if(this.closable){
26629             this.closeEl.on('click', this.onClose, this);
26630         }
26631        
26632     },
26633
26634
26635     getResizeEl : function(){
26636         return this.wrap;
26637     },
26638
26639     getPositionEl : function(){
26640         return this.wrap;
26641     },
26642
26643     // private
26644     onRender : function(ct, position){
26645         
26646         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26647         //if(this.inputValue !== undefined){
26648         this.wrap = this.el.wrap();
26649         
26650         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26651         
26652         if(this.closable){
26653             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26654         }
26655         
26656         if (this.bodyStyle) {
26657             this.viewEl.applyStyles(this.bodyStyle);
26658         }
26659         //this.viewEl.setStyle('padding', '2px');
26660         
26661         this.setValue(this.value);
26662         
26663     },
26664 /*
26665     // private
26666     initValue : Roo.emptyFn,
26667
26668   */
26669
26670         // private
26671     onClick : function(){
26672         
26673     },
26674
26675     /**
26676      * Sets the checked state of the checkbox.
26677      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26678      */
26679     setValue : function(v){
26680         this.value = v;
26681         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26682         // this might be called before we have a dom element..
26683         if (!this.viewEl) {
26684             return;
26685         }
26686         this.viewEl.dom.innerHTML = html;
26687         Roo.form.DisplayField.superclass.setValue.call(this, v);
26688
26689     },
26690     
26691     onClose : function(e)
26692     {
26693         e.preventDefault();
26694         
26695         this.fireEvent('close', this);
26696     }
26697 });/*
26698  * 
26699  * Licence- LGPL
26700  * 
26701  */
26702
26703 /**
26704  * @class Roo.form.DayPicker
26705  * @extends Roo.form.Field
26706  * A Day picker show [M] [T] [W] ....
26707  * @constructor
26708  * Creates a new Day Picker
26709  * @param {Object} config Configuration options
26710  */
26711 Roo.form.DayPicker= function(config){
26712     Roo.form.DayPicker.superclass.constructor.call(this, config);
26713      
26714 };
26715
26716 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26717     /**
26718      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26719      */
26720     focusClass : undefined,
26721     /**
26722      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26723      */
26724     fieldClass: "x-form-field",
26725    
26726     /**
26727      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26728      * {tag: "input", type: "checkbox", autocomplete: "off"})
26729      */
26730     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26731     
26732    
26733     actionMode : 'viewEl', 
26734     //
26735     // private
26736  
26737     inputType : 'hidden',
26738     
26739      
26740     inputElement: false, // real input element?
26741     basedOn: false, // ????
26742     
26743     isFormField: true, // not sure where this is needed!!!!
26744
26745     onResize : function(){
26746         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26747         if(!this.boxLabel){
26748             this.el.alignTo(this.wrap, 'c-c');
26749         }
26750     },
26751
26752     initEvents : function(){
26753         Roo.form.Checkbox.superclass.initEvents.call(this);
26754         this.el.on("click", this.onClick,  this);
26755         this.el.on("change", this.onClick,  this);
26756     },
26757
26758
26759     getResizeEl : function(){
26760         return this.wrap;
26761     },
26762
26763     getPositionEl : function(){
26764         return this.wrap;
26765     },
26766
26767     
26768     // private
26769     onRender : function(ct, position){
26770         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26771        
26772         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26773         
26774         var r1 = '<table><tr>';
26775         var r2 = '<tr class="x-form-daypick-icons">';
26776         for (var i=0; i < 7; i++) {
26777             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26778             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26779         }
26780         
26781         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26782         viewEl.select('img').on('click', this.onClick, this);
26783         this.viewEl = viewEl;   
26784         
26785         
26786         // this will not work on Chrome!!!
26787         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26788         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26789         
26790         
26791           
26792
26793     },
26794
26795     // private
26796     initValue : Roo.emptyFn,
26797
26798     /**
26799      * Returns the checked state of the checkbox.
26800      * @return {Boolean} True if checked, else false
26801      */
26802     getValue : function(){
26803         return this.el.dom.value;
26804         
26805     },
26806
26807         // private
26808     onClick : function(e){ 
26809         //this.setChecked(!this.checked);
26810         Roo.get(e.target).toggleClass('x-menu-item-checked');
26811         this.refreshValue();
26812         //if(this.el.dom.checked != this.checked){
26813         //    this.setValue(this.el.dom.checked);
26814        // }
26815     },
26816     
26817     // private
26818     refreshValue : function()
26819     {
26820         var val = '';
26821         this.viewEl.select('img',true).each(function(e,i,n)  {
26822             val += e.is(".x-menu-item-checked") ? String(n) : '';
26823         });
26824         this.setValue(val, true);
26825     },
26826
26827     /**
26828      * Sets the checked state of the checkbox.
26829      * On is always based on a string comparison between inputValue and the param.
26830      * @param {Boolean/String} value - the value to set 
26831      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26832      */
26833     setValue : function(v,suppressEvent){
26834         if (!this.el.dom) {
26835             return;
26836         }
26837         var old = this.el.dom.value ;
26838         this.el.dom.value = v;
26839         if (suppressEvent) {
26840             return ;
26841         }
26842          
26843         // update display..
26844         this.viewEl.select('img',true).each(function(e,i,n)  {
26845             
26846             var on = e.is(".x-menu-item-checked");
26847             var newv = v.indexOf(String(n)) > -1;
26848             if (on != newv) {
26849                 e.toggleClass('x-menu-item-checked');
26850             }
26851             
26852         });
26853         
26854         
26855         this.fireEvent('change', this, v, old);
26856         
26857         
26858     },
26859    
26860     // handle setting of hidden value by some other method!!?!?
26861     setFromHidden: function()
26862     {
26863         if(!this.el){
26864             return;
26865         }
26866         //console.log("SET FROM HIDDEN");
26867         //alert('setFrom hidden');
26868         this.setValue(this.el.dom.value);
26869     },
26870     
26871     onDestroy : function()
26872     {
26873         if(this.viewEl){
26874             Roo.get(this.viewEl).remove();
26875         }
26876          
26877         Roo.form.DayPicker.superclass.onDestroy.call(this);
26878     }
26879
26880 });/*
26881  * RooJS Library 1.1.1
26882  * Copyright(c) 2008-2011  Alan Knowles
26883  *
26884  * License - LGPL
26885  */
26886  
26887
26888 /**
26889  * @class Roo.form.ComboCheck
26890  * @extends Roo.form.ComboBox
26891  * A combobox for multiple select items.
26892  *
26893  * FIXME - could do with a reset button..
26894  * 
26895  * @constructor
26896  * Create a new ComboCheck
26897  * @param {Object} config Configuration options
26898  */
26899 Roo.form.ComboCheck = function(config){
26900     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26901     // should verify some data...
26902     // like
26903     // hiddenName = required..
26904     // displayField = required
26905     // valudField == required
26906     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26907     var _t = this;
26908     Roo.each(req, function(e) {
26909         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26910             throw "Roo.form.ComboCheck : missing value for: " + e;
26911         }
26912     });
26913     
26914     
26915 };
26916
26917 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26918      
26919      
26920     editable : false,
26921      
26922     selectedClass: 'x-menu-item-checked', 
26923     
26924     // private
26925     onRender : function(ct, position){
26926         var _t = this;
26927         
26928         
26929         
26930         if(!this.tpl){
26931             var cls = 'x-combo-list';
26932
26933             
26934             this.tpl =  new Roo.Template({
26935                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26936                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26937                    '<span>{' + this.displayField + '}</span>' +
26938                     '</div>' 
26939                 
26940             });
26941         }
26942  
26943         
26944         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26945         this.view.singleSelect = false;
26946         this.view.multiSelect = true;
26947         this.view.toggleSelect = true;
26948         this.pageTb.add(new Roo.Toolbar.Fill(), {
26949             
26950             text: 'Done',
26951             handler: function()
26952             {
26953                 _t.collapse();
26954             }
26955         });
26956     },
26957     
26958     onViewOver : function(e, t){
26959         // do nothing...
26960         return;
26961         
26962     },
26963     
26964     onViewClick : function(doFocus,index){
26965         return;
26966         
26967     },
26968     select: function () {
26969         //Roo.log("SELECT CALLED");
26970     },
26971      
26972     selectByValue : function(xv, scrollIntoView){
26973         var ar = this.getValueArray();
26974         var sels = [];
26975         
26976         Roo.each(ar, function(v) {
26977             if(v === undefined || v === null){
26978                 return;
26979             }
26980             var r = this.findRecord(this.valueField, v);
26981             if(r){
26982                 sels.push(this.store.indexOf(r))
26983                 
26984             }
26985         },this);
26986         this.view.select(sels);
26987         return false;
26988     },
26989     
26990     
26991     
26992     onSelect : function(record, index){
26993        // Roo.log("onselect Called");
26994        // this is only called by the clear button now..
26995         this.view.clearSelections();
26996         this.setValue('[]');
26997         if (this.value != this.valueBefore) {
26998             this.fireEvent('change', this, this.value, this.valueBefore);
26999             this.valueBefore = this.value;
27000         }
27001     },
27002     getValueArray : function()
27003     {
27004         var ar = [] ;
27005         
27006         try {
27007             //Roo.log(this.value);
27008             if (typeof(this.value) == 'undefined') {
27009                 return [];
27010             }
27011             var ar = Roo.decode(this.value);
27012             return  ar instanceof Array ? ar : []; //?? valid?
27013             
27014         } catch(e) {
27015             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27016             return [];
27017         }
27018          
27019     },
27020     expand : function ()
27021     {
27022         
27023         Roo.form.ComboCheck.superclass.expand.call(this);
27024         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27025         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27026         
27027
27028     },
27029     
27030     collapse : function(){
27031         Roo.form.ComboCheck.superclass.collapse.call(this);
27032         var sl = this.view.getSelectedIndexes();
27033         var st = this.store;
27034         var nv = [];
27035         var tv = [];
27036         var r;
27037         Roo.each(sl, function(i) {
27038             r = st.getAt(i);
27039             nv.push(r.get(this.valueField));
27040         },this);
27041         this.setValue(Roo.encode(nv));
27042         if (this.value != this.valueBefore) {
27043
27044             this.fireEvent('change', this, this.value, this.valueBefore);
27045             this.valueBefore = this.value;
27046         }
27047         
27048     },
27049     
27050     setValue : function(v){
27051         // Roo.log(v);
27052         this.value = v;
27053         
27054         var vals = this.getValueArray();
27055         var tv = [];
27056         Roo.each(vals, function(k) {
27057             var r = this.findRecord(this.valueField, k);
27058             if(r){
27059                 tv.push(r.data[this.displayField]);
27060             }else if(this.valueNotFoundText !== undefined){
27061                 tv.push( this.valueNotFoundText );
27062             }
27063         },this);
27064        // Roo.log(tv);
27065         
27066         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27067         this.hiddenField.value = v;
27068         this.value = v;
27069     }
27070     
27071 });/*
27072  * Based on:
27073  * Ext JS Library 1.1.1
27074  * Copyright(c) 2006-2007, Ext JS, LLC.
27075  *
27076  * Originally Released Under LGPL - original licence link has changed is not relivant.
27077  *
27078  * Fork - LGPL
27079  * <script type="text/javascript">
27080  */
27081  
27082 /**
27083  * @class Roo.form.Signature
27084  * @extends Roo.form.Field
27085  * Signature field.  
27086  * @constructor
27087  * 
27088  * @param {Object} config Configuration options
27089  */
27090
27091 Roo.form.Signature = function(config){
27092     Roo.form.Signature.superclass.constructor.call(this, config);
27093     
27094     this.addEvents({// not in used??
27095          /**
27096          * @event confirm
27097          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27098              * @param {Roo.form.Signature} combo This combo box
27099              */
27100         'confirm' : true,
27101         /**
27102          * @event reset
27103          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27104              * @param {Roo.form.ComboBox} combo This combo box
27105              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27106              */
27107         'reset' : true
27108     });
27109 };
27110
27111 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27112     /**
27113      * @cfg {Object} labels Label to use when rendering a form.
27114      * defaults to 
27115      * labels : { 
27116      *      clear : "Clear",
27117      *      confirm : "Confirm"
27118      *  }
27119      */
27120     labels : { 
27121         clear : "Clear",
27122         confirm : "Confirm"
27123     },
27124     /**
27125      * @cfg {Number} width The signature panel width (defaults to 300)
27126      */
27127     width: 300,
27128     /**
27129      * @cfg {Number} height The signature panel height (defaults to 100)
27130      */
27131     height : 100,
27132     /**
27133      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27134      */
27135     allowBlank : false,
27136     
27137     //private
27138     // {Object} signPanel The signature SVG panel element (defaults to {})
27139     signPanel : {},
27140     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27141     isMouseDown : false,
27142     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27143     isConfirmed : false,
27144     // {String} signatureTmp SVG mapping string (defaults to empty string)
27145     signatureTmp : '',
27146     
27147     
27148     defaultAutoCreate : { // modified by initCompnoent..
27149         tag: "input",
27150         type:"hidden"
27151     },
27152
27153     // private
27154     onRender : function(ct, position){
27155         
27156         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27157         
27158         this.wrap = this.el.wrap({
27159             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27160         });
27161         
27162         this.createToolbar(this);
27163         this.signPanel = this.wrap.createChild({
27164                 tag: 'div',
27165                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27166             }, this.el
27167         );
27168             
27169         this.svgID = Roo.id();
27170         this.svgEl = this.signPanel.createChild({
27171               xmlns : 'http://www.w3.org/2000/svg',
27172               tag : 'svg',
27173               id : this.svgID + "-svg",
27174               width: this.width,
27175               height: this.height,
27176               viewBox: '0 0 '+this.width+' '+this.height,
27177               cn : [
27178                 {
27179                     tag: "rect",
27180                     id: this.svgID + "-svg-r",
27181                     width: this.width,
27182                     height: this.height,
27183                     fill: "#ffa"
27184                 },
27185                 {
27186                     tag: "line",
27187                     id: this.svgID + "-svg-l",
27188                     x1: "0", // start
27189                     y1: (this.height*0.8), // start set the line in 80% of height
27190                     x2: this.width, // end
27191                     y2: (this.height*0.8), // end set the line in 80% of height
27192                     'stroke': "#666",
27193                     'stroke-width': "1",
27194                     'stroke-dasharray': "3",
27195                     'shape-rendering': "crispEdges",
27196                     'pointer-events': "none"
27197                 },
27198                 {
27199                     tag: "path",
27200                     id: this.svgID + "-svg-p",
27201                     'stroke': "navy",
27202                     'stroke-width': "3",
27203                     'fill': "none",
27204                     'pointer-events': 'none'
27205                 }
27206               ]
27207         });
27208         this.createSVG();
27209         this.svgBox = this.svgEl.dom.getScreenCTM();
27210     },
27211     createSVG : function(){ 
27212         var svg = this.signPanel;
27213         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27214         var t = this;
27215
27216         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27217         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27218         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27219         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27220         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27221         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27222         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27223         
27224     },
27225     isTouchEvent : function(e){
27226         return e.type.match(/^touch/);
27227     },
27228     getCoords : function (e) {
27229         var pt    = this.svgEl.dom.createSVGPoint();
27230         pt.x = e.clientX; 
27231         pt.y = e.clientY;
27232         if (this.isTouchEvent(e)) {
27233             pt.x =  e.targetTouches[0].clientX;
27234             pt.y = e.targetTouches[0].clientY;
27235         }
27236         var a = this.svgEl.dom.getScreenCTM();
27237         var b = a.inverse();
27238         var mx = pt.matrixTransform(b);
27239         return mx.x + ',' + mx.y;
27240     },
27241     //mouse event headler 
27242     down : function (e) {
27243         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27244         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27245         
27246         this.isMouseDown = true;
27247         
27248         e.preventDefault();
27249     },
27250     move : function (e) {
27251         if (this.isMouseDown) {
27252             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27253             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27254         }
27255         
27256         e.preventDefault();
27257     },
27258     up : function (e) {
27259         this.isMouseDown = false;
27260         var sp = this.signatureTmp.split(' ');
27261         
27262         if(sp.length > 1){
27263             if(!sp[sp.length-2].match(/^L/)){
27264                 sp.pop();
27265                 sp.pop();
27266                 sp.push("");
27267                 this.signatureTmp = sp.join(" ");
27268             }
27269         }
27270         if(this.getValue() != this.signatureTmp){
27271             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27272             this.isConfirmed = false;
27273         }
27274         e.preventDefault();
27275     },
27276     
27277     /**
27278      * Protected method that will not generally be called directly. It
27279      * is called when the editor creates its toolbar. Override this method if you need to
27280      * add custom toolbar buttons.
27281      * @param {HtmlEditor} editor
27282      */
27283     createToolbar : function(editor){
27284          function btn(id, toggle, handler){
27285             var xid = fid + '-'+ id ;
27286             return {
27287                 id : xid,
27288                 cmd : id,
27289                 cls : 'x-btn-icon x-edit-'+id,
27290                 enableToggle:toggle !== false,
27291                 scope: editor, // was editor...
27292                 handler:handler||editor.relayBtnCmd,
27293                 clickEvent:'mousedown',
27294                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27295                 tabIndex:-1
27296             };
27297         }
27298         
27299         
27300         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27301         this.tb = tb;
27302         this.tb.add(
27303            {
27304                 cls : ' x-signature-btn x-signature-'+id,
27305                 scope: editor, // was editor...
27306                 handler: this.reset,
27307                 clickEvent:'mousedown',
27308                 text: this.labels.clear
27309             },
27310             {
27311                  xtype : 'Fill',
27312                  xns: Roo.Toolbar
27313             }, 
27314             {
27315                 cls : '  x-signature-btn x-signature-'+id,
27316                 scope: editor, // was editor...
27317                 handler: this.confirmHandler,
27318                 clickEvent:'mousedown',
27319                 text: this.labels.confirm
27320             }
27321         );
27322     
27323     },
27324     //public
27325     /**
27326      * when user is clicked confirm then show this image.....
27327      * 
27328      * @return {String} Image Data URI
27329      */
27330     getImageDataURI : function(){
27331         var svg = this.svgEl.dom.parentNode.innerHTML;
27332         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27333         return src; 
27334     },
27335     /**
27336      * 
27337      * @return {Boolean} this.isConfirmed
27338      */
27339     getConfirmed : function(){
27340         return this.isConfirmed;
27341     },
27342     /**
27343      * 
27344      * @return {Number} this.width
27345      */
27346     getWidth : function(){
27347         return this.width;
27348     },
27349     /**
27350      * 
27351      * @return {Number} this.height
27352      */
27353     getHeight : function(){
27354         return this.height;
27355     },
27356     // private
27357     getSignature : function(){
27358         return this.signatureTmp;
27359     },
27360     // private
27361     reset : function(){
27362         this.signatureTmp = '';
27363         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27364         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27365         this.isConfirmed = false;
27366         Roo.form.Signature.superclass.reset.call(this);
27367     },
27368     setSignature : function(s){
27369         this.signatureTmp = s;
27370         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27371         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27372         this.setValue(s);
27373         this.isConfirmed = false;
27374         Roo.form.Signature.superclass.reset.call(this);
27375     }, 
27376     test : function(){
27377 //        Roo.log(this.signPanel.dom.contentWindow.up())
27378     },
27379     //private
27380     setConfirmed : function(){
27381         
27382         
27383         
27384 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27385     },
27386     // private
27387     confirmHandler : function(){
27388         if(!this.getSignature()){
27389             return;
27390         }
27391         
27392         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27393         this.setValue(this.getSignature());
27394         this.isConfirmed = true;
27395         
27396         this.fireEvent('confirm', this);
27397     },
27398     // private
27399     // Subclasses should provide the validation implementation by overriding this
27400     validateValue : function(value){
27401         if(this.allowBlank){
27402             return true;
27403         }
27404         
27405         if(this.isConfirmed){
27406             return true;
27407         }
27408         return false;
27409     }
27410 });/*
27411  * Based on:
27412  * Ext JS Library 1.1.1
27413  * Copyright(c) 2006-2007, Ext JS, LLC.
27414  *
27415  * Originally Released Under LGPL - original licence link has changed is not relivant.
27416  *
27417  * Fork - LGPL
27418  * <script type="text/javascript">
27419  */
27420  
27421
27422 /**
27423  * @class Roo.form.ComboBox
27424  * @extends Roo.form.TriggerField
27425  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27426  * @constructor
27427  * Create a new ComboBox.
27428  * @param {Object} config Configuration options
27429  */
27430 Roo.form.Select = function(config){
27431     Roo.form.Select.superclass.constructor.call(this, config);
27432      
27433 };
27434
27435 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27436     /**
27437      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27438      */
27439     /**
27440      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27441      * rendering into an Roo.Editor, defaults to false)
27442      */
27443     /**
27444      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27445      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27446      */
27447     /**
27448      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27449      */
27450     /**
27451      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27452      * the dropdown list (defaults to undefined, with no header element)
27453      */
27454
27455      /**
27456      * @cfg {String/Roo.Template} tpl The template to use to render the output
27457      */
27458      
27459     // private
27460     defaultAutoCreate : {tag: "select"  },
27461     /**
27462      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27463      */
27464     listWidth: undefined,
27465     /**
27466      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27467      * mode = 'remote' or 'text' if mode = 'local')
27468      */
27469     displayField: undefined,
27470     /**
27471      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27472      * mode = 'remote' or 'value' if mode = 'local'). 
27473      * Note: use of a valueField requires the user make a selection
27474      * in order for a value to be mapped.
27475      */
27476     valueField: undefined,
27477     
27478     
27479     /**
27480      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27481      * field's data value (defaults to the underlying DOM element's name)
27482      */
27483     hiddenName: undefined,
27484     /**
27485      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27486      */
27487     listClass: '',
27488     /**
27489      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27490      */
27491     selectedClass: 'x-combo-selected',
27492     /**
27493      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27494      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27495      * which displays a downward arrow icon).
27496      */
27497     triggerClass : 'x-form-arrow-trigger',
27498     /**
27499      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27500      */
27501     shadow:'sides',
27502     /**
27503      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27504      * anchor positions (defaults to 'tl-bl')
27505      */
27506     listAlign: 'tl-bl?',
27507     /**
27508      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27509      */
27510     maxHeight: 300,
27511     /**
27512      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27513      * query specified by the allQuery config option (defaults to 'query')
27514      */
27515     triggerAction: 'query',
27516     /**
27517      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27518      * (defaults to 4, does not apply if editable = false)
27519      */
27520     minChars : 4,
27521     /**
27522      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27523      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27524      */
27525     typeAhead: false,
27526     /**
27527      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27528      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27529      */
27530     queryDelay: 500,
27531     /**
27532      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27533      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27534      */
27535     pageSize: 0,
27536     /**
27537      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27538      * when editable = true (defaults to false)
27539      */
27540     selectOnFocus:false,
27541     /**
27542      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27543      */
27544     queryParam: 'query',
27545     /**
27546      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27547      * when mode = 'remote' (defaults to 'Loading...')
27548      */
27549     loadingText: 'Loading...',
27550     /**
27551      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27552      */
27553     resizable: false,
27554     /**
27555      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27556      */
27557     handleHeight : 8,
27558     /**
27559      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27560      * traditional select (defaults to true)
27561      */
27562     editable: true,
27563     /**
27564      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27565      */
27566     allQuery: '',
27567     /**
27568      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27569      */
27570     mode: 'remote',
27571     /**
27572      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27573      * listWidth has a higher value)
27574      */
27575     minListWidth : 70,
27576     /**
27577      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27578      * allow the user to set arbitrary text into the field (defaults to false)
27579      */
27580     forceSelection:false,
27581     /**
27582      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27583      * if typeAhead = true (defaults to 250)
27584      */
27585     typeAheadDelay : 250,
27586     /**
27587      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27588      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27589      */
27590     valueNotFoundText : undefined,
27591     
27592     /**
27593      * @cfg {String} defaultValue The value displayed after loading the store.
27594      */
27595     defaultValue: '',
27596     
27597     /**
27598      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27599      */
27600     blockFocus : false,
27601     
27602     /**
27603      * @cfg {Boolean} disableClear Disable showing of clear button.
27604      */
27605     disableClear : false,
27606     /**
27607      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27608      */
27609     alwaysQuery : false,
27610     
27611     //private
27612     addicon : false,
27613     editicon: false,
27614     
27615     // element that contains real text value.. (when hidden is used..)
27616      
27617     // private
27618     onRender : function(ct, position){
27619         Roo.form.Field.prototype.onRender.call(this, ct, position);
27620         
27621         if(this.store){
27622             this.store.on('beforeload', this.onBeforeLoad, this);
27623             this.store.on('load', this.onLoad, this);
27624             this.store.on('loadexception', this.onLoadException, this);
27625             this.store.load({});
27626         }
27627         
27628         
27629         
27630     },
27631
27632     // private
27633     initEvents : function(){
27634         //Roo.form.ComboBox.superclass.initEvents.call(this);
27635  
27636     },
27637
27638     onDestroy : function(){
27639        
27640         if(this.store){
27641             this.store.un('beforeload', this.onBeforeLoad, this);
27642             this.store.un('load', this.onLoad, this);
27643             this.store.un('loadexception', this.onLoadException, this);
27644         }
27645         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27646     },
27647
27648     // private
27649     fireKey : function(e){
27650         if(e.isNavKeyPress() && !this.list.isVisible()){
27651             this.fireEvent("specialkey", this, e);
27652         }
27653     },
27654
27655     // private
27656     onResize: function(w, h){
27657         
27658         return; 
27659     
27660         
27661     },
27662
27663     /**
27664      * Allow or prevent the user from directly editing the field text.  If false is passed,
27665      * the user will only be able to select from the items defined in the dropdown list.  This method
27666      * is the runtime equivalent of setting the 'editable' config option at config time.
27667      * @param {Boolean} value True to allow the user to directly edit the field text
27668      */
27669     setEditable : function(value){
27670          
27671     },
27672
27673     // private
27674     onBeforeLoad : function(){
27675         
27676         Roo.log("Select before load");
27677         return;
27678     
27679         this.innerList.update(this.loadingText ?
27680                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27681         //this.restrictHeight();
27682         this.selectedIndex = -1;
27683     },
27684
27685     // private
27686     onLoad : function(){
27687
27688     
27689         var dom = this.el.dom;
27690         dom.innerHTML = '';
27691          var od = dom.ownerDocument;
27692          
27693         if (this.emptyText) {
27694             var op = od.createElement('option');
27695             op.setAttribute('value', '');
27696             op.innerHTML = String.format('{0}', this.emptyText);
27697             dom.appendChild(op);
27698         }
27699         if(this.store.getCount() > 0){
27700            
27701             var vf = this.valueField;
27702             var df = this.displayField;
27703             this.store.data.each(function(r) {
27704                 // which colmsn to use... testing - cdoe / title..
27705                 var op = od.createElement('option');
27706                 op.setAttribute('value', r.data[vf]);
27707                 op.innerHTML = String.format('{0}', r.data[df]);
27708                 dom.appendChild(op);
27709             });
27710             if (typeof(this.defaultValue != 'undefined')) {
27711                 this.setValue(this.defaultValue);
27712             }
27713             
27714              
27715         }else{
27716             //this.onEmptyResults();
27717         }
27718         //this.el.focus();
27719     },
27720     // private
27721     onLoadException : function()
27722     {
27723         dom.innerHTML = '';
27724             
27725         Roo.log("Select on load exception");
27726         return;
27727     
27728         this.collapse();
27729         Roo.log(this.store.reader.jsonData);
27730         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27731             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27732         }
27733         
27734         
27735     },
27736     // private
27737     onTypeAhead : function(){
27738          
27739     },
27740
27741     // private
27742     onSelect : function(record, index){
27743         Roo.log('on select?');
27744         return;
27745         if(this.fireEvent('beforeselect', this, record, index) !== false){
27746             this.setFromData(index > -1 ? record.data : false);
27747             this.collapse();
27748             this.fireEvent('select', this, record, index);
27749         }
27750     },
27751
27752     /**
27753      * Returns the currently selected field value or empty string if no value is set.
27754      * @return {String} value The selected value
27755      */
27756     getValue : function(){
27757         var dom = this.el.dom;
27758         this.value = dom.options[dom.selectedIndex].value;
27759         return this.value;
27760         
27761     },
27762
27763     /**
27764      * Clears any text/value currently set in the field
27765      */
27766     clearValue : function(){
27767         this.value = '';
27768         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27769         
27770     },
27771
27772     /**
27773      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27774      * will be displayed in the field.  If the value does not match the data value of an existing item,
27775      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27776      * Otherwise the field will be blank (although the value will still be set).
27777      * @param {String} value The value to match
27778      */
27779     setValue : function(v){
27780         var d = this.el.dom;
27781         for (var i =0; i < d.options.length;i++) {
27782             if (v == d.options[i].value) {
27783                 d.selectedIndex = i;
27784                 this.value = v;
27785                 return;
27786             }
27787         }
27788         this.clearValue();
27789     },
27790     /**
27791      * @property {Object} the last set data for the element
27792      */
27793     
27794     lastData : false,
27795     /**
27796      * Sets the value of the field based on a object which is related to the record format for the store.
27797      * @param {Object} value the value to set as. or false on reset?
27798      */
27799     setFromData : function(o){
27800         Roo.log('setfrom data?');
27801          
27802         
27803         
27804     },
27805     // private
27806     reset : function(){
27807         this.clearValue();
27808     },
27809     // private
27810     findRecord : function(prop, value){
27811         
27812         return false;
27813     
27814         var record;
27815         if(this.store.getCount() > 0){
27816             this.store.each(function(r){
27817                 if(r.data[prop] == value){
27818                     record = r;
27819                     return false;
27820                 }
27821                 return true;
27822             });
27823         }
27824         return record;
27825     },
27826     
27827     getName: function()
27828     {
27829         // returns hidden if it's set..
27830         if (!this.rendered) {return ''};
27831         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27832         
27833     },
27834      
27835
27836     
27837
27838     // private
27839     onEmptyResults : function(){
27840         Roo.log('empty results');
27841         //this.collapse();
27842     },
27843
27844     /**
27845      * Returns true if the dropdown list is expanded, else false.
27846      */
27847     isExpanded : function(){
27848         return false;
27849     },
27850
27851     /**
27852      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27853      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27854      * @param {String} value The data value of the item to select
27855      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27856      * selected item if it is not currently in view (defaults to true)
27857      * @return {Boolean} True if the value matched an item in the list, else false
27858      */
27859     selectByValue : function(v, scrollIntoView){
27860         Roo.log('select By Value');
27861         return false;
27862     
27863         if(v !== undefined && v !== null){
27864             var r = this.findRecord(this.valueField || this.displayField, v);
27865             if(r){
27866                 this.select(this.store.indexOf(r), scrollIntoView);
27867                 return true;
27868             }
27869         }
27870         return false;
27871     },
27872
27873     /**
27874      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27875      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27876      * @param {Number} index The zero-based index of the list item to select
27877      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27878      * selected item if it is not currently in view (defaults to true)
27879      */
27880     select : function(index, scrollIntoView){
27881         Roo.log('select ');
27882         return  ;
27883         
27884         this.selectedIndex = index;
27885         this.view.select(index);
27886         if(scrollIntoView !== false){
27887             var el = this.view.getNode(index);
27888             if(el){
27889                 this.innerList.scrollChildIntoView(el, false);
27890             }
27891         }
27892     },
27893
27894       
27895
27896     // private
27897     validateBlur : function(){
27898         
27899         return;
27900         
27901     },
27902
27903     // private
27904     initQuery : function(){
27905         this.doQuery(this.getRawValue());
27906     },
27907
27908     // private
27909     doForce : function(){
27910         if(this.el.dom.value.length > 0){
27911             this.el.dom.value =
27912                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27913              
27914         }
27915     },
27916
27917     /**
27918      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27919      * query allowing the query action to be canceled if needed.
27920      * @param {String} query The SQL query to execute
27921      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27922      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27923      * saved in the current store (defaults to false)
27924      */
27925     doQuery : function(q, forceAll){
27926         
27927         Roo.log('doQuery?');
27928         if(q === undefined || q === null){
27929             q = '';
27930         }
27931         var qe = {
27932             query: q,
27933             forceAll: forceAll,
27934             combo: this,
27935             cancel:false
27936         };
27937         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27938             return false;
27939         }
27940         q = qe.query;
27941         forceAll = qe.forceAll;
27942         if(forceAll === true || (q.length >= this.minChars)){
27943             if(this.lastQuery != q || this.alwaysQuery){
27944                 this.lastQuery = q;
27945                 if(this.mode == 'local'){
27946                     this.selectedIndex = -1;
27947                     if(forceAll){
27948                         this.store.clearFilter();
27949                     }else{
27950                         this.store.filter(this.displayField, q);
27951                     }
27952                     this.onLoad();
27953                 }else{
27954                     this.store.baseParams[this.queryParam] = q;
27955                     this.store.load({
27956                         params: this.getParams(q)
27957                     });
27958                     this.expand();
27959                 }
27960             }else{
27961                 this.selectedIndex = -1;
27962                 this.onLoad();   
27963             }
27964         }
27965     },
27966
27967     // private
27968     getParams : function(q){
27969         var p = {};
27970         //p[this.queryParam] = q;
27971         if(this.pageSize){
27972             p.start = 0;
27973             p.limit = this.pageSize;
27974         }
27975         return p;
27976     },
27977
27978     /**
27979      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27980      */
27981     collapse : function(){
27982         
27983     },
27984
27985     // private
27986     collapseIf : function(e){
27987         
27988     },
27989
27990     /**
27991      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27992      */
27993     expand : function(){
27994         
27995     } ,
27996
27997     // private
27998      
27999
28000     /** 
28001     * @cfg {Boolean} grow 
28002     * @hide 
28003     */
28004     /** 
28005     * @cfg {Number} growMin 
28006     * @hide 
28007     */
28008     /** 
28009     * @cfg {Number} growMax 
28010     * @hide 
28011     */
28012     /**
28013      * @hide
28014      * @method autoSize
28015      */
28016     
28017     setWidth : function()
28018     {
28019         
28020     },
28021     getResizeEl : function(){
28022         return this.el;
28023     }
28024 });//<script type="text/javasscript">
28025  
28026
28027 /**
28028  * @class Roo.DDView
28029  * A DnD enabled version of Roo.View.
28030  * @param {Element/String} container The Element in which to create the View.
28031  * @param {String} tpl The template string used to create the markup for each element of the View
28032  * @param {Object} config The configuration properties. These include all the config options of
28033  * {@link Roo.View} plus some specific to this class.<br>
28034  * <p>
28035  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28036  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28037  * <p>
28038  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28039 .x-view-drag-insert-above {
28040         border-top:1px dotted #3366cc;
28041 }
28042 .x-view-drag-insert-below {
28043         border-bottom:1px dotted #3366cc;
28044 }
28045 </code></pre>
28046  * 
28047  */
28048  
28049 Roo.DDView = function(container, tpl, config) {
28050     Roo.DDView.superclass.constructor.apply(this, arguments);
28051     this.getEl().setStyle("outline", "0px none");
28052     this.getEl().unselectable();
28053     if (this.dragGroup) {
28054                 this.setDraggable(this.dragGroup.split(","));
28055     }
28056     if (this.dropGroup) {
28057                 this.setDroppable(this.dropGroup.split(","));
28058     }
28059     if (this.deletable) {
28060         this.setDeletable();
28061     }
28062     this.isDirtyFlag = false;
28063         this.addEvents({
28064                 "drop" : true
28065         });
28066 };
28067
28068 Roo.extend(Roo.DDView, Roo.View, {
28069 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28070 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28071 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28072 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28073
28074         isFormField: true,
28075
28076         reset: Roo.emptyFn,
28077         
28078         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28079
28080         validate: function() {
28081                 return true;
28082         },
28083         
28084         destroy: function() {
28085                 this.purgeListeners();
28086                 this.getEl.removeAllListeners();
28087                 this.getEl().remove();
28088                 if (this.dragZone) {
28089                         if (this.dragZone.destroy) {
28090                                 this.dragZone.destroy();
28091                         }
28092                 }
28093                 if (this.dropZone) {
28094                         if (this.dropZone.destroy) {
28095                                 this.dropZone.destroy();
28096                         }
28097                 }
28098         },
28099
28100 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28101         getName: function() {
28102                 return this.name;
28103         },
28104
28105 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28106         setValue: function(v) {
28107                 if (!this.store) {
28108                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28109                 }
28110                 var data = {};
28111                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28112                 this.store.proxy = new Roo.data.MemoryProxy(data);
28113                 this.store.load();
28114         },
28115
28116 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28117         getValue: function() {
28118                 var result = '(';
28119                 this.store.each(function(rec) {
28120                         result += rec.id + ',';
28121                 });
28122                 return result.substr(0, result.length - 1) + ')';
28123         },
28124         
28125         getIds: function() {
28126                 var i = 0, result = new Array(this.store.getCount());
28127                 this.store.each(function(rec) {
28128                         result[i++] = rec.id;
28129                 });
28130                 return result;
28131         },
28132         
28133         isDirty: function() {
28134                 return this.isDirtyFlag;
28135         },
28136
28137 /**
28138  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28139  *      whole Element becomes the target, and this causes the drop gesture to append.
28140  */
28141     getTargetFromEvent : function(e) {
28142                 var target = e.getTarget();
28143                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28144                 target = target.parentNode;
28145                 }
28146                 if (!target) {
28147                         target = this.el.dom.lastChild || this.el.dom;
28148                 }
28149                 return target;
28150     },
28151
28152 /**
28153  *      Create the drag data which consists of an object which has the property "ddel" as
28154  *      the drag proxy element. 
28155  */
28156     getDragData : function(e) {
28157         var target = this.findItemFromChild(e.getTarget());
28158                 if(target) {
28159                         this.handleSelection(e);
28160                         var selNodes = this.getSelectedNodes();
28161             var dragData = {
28162                 source: this,
28163                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28164                 nodes: selNodes,
28165                 records: []
28166                         };
28167                         var selectedIndices = this.getSelectedIndexes();
28168                         for (var i = 0; i < selectedIndices.length; i++) {
28169                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28170                         }
28171                         if (selNodes.length == 1) {
28172                                 dragData.ddel = target.cloneNode(true); // the div element
28173                         } else {
28174                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28175                                 div.className = 'multi-proxy';
28176                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28177                                         div.appendChild(selNodes[i].cloneNode(true));
28178                                 }
28179                                 dragData.ddel = div;
28180                         }
28181             //console.log(dragData)
28182             //console.log(dragData.ddel.innerHTML)
28183                         return dragData;
28184                 }
28185         //console.log('nodragData')
28186                 return false;
28187     },
28188     
28189 /**     Specify to which ddGroup items in this DDView may be dragged. */
28190     setDraggable: function(ddGroup) {
28191         if (ddGroup instanceof Array) {
28192                 Roo.each(ddGroup, this.setDraggable, this);
28193                 return;
28194         }
28195         if (this.dragZone) {
28196                 this.dragZone.addToGroup(ddGroup);
28197         } else {
28198                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28199                                 containerScroll: true,
28200                                 ddGroup: ddGroup 
28201
28202                         });
28203 //                      Draggability implies selection. DragZone's mousedown selects the element.
28204                         if (!this.multiSelect) { this.singleSelect = true; }
28205
28206 //                      Wire the DragZone's handlers up to methods in *this*
28207                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28208                 }
28209     },
28210
28211 /**     Specify from which ddGroup this DDView accepts drops. */
28212     setDroppable: function(ddGroup) {
28213         if (ddGroup instanceof Array) {
28214                 Roo.each(ddGroup, this.setDroppable, this);
28215                 return;
28216         }
28217         if (this.dropZone) {
28218                 this.dropZone.addToGroup(ddGroup);
28219         } else {
28220                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28221                                 containerScroll: true,
28222                                 ddGroup: ddGroup
28223                         });
28224
28225 //                      Wire the DropZone's handlers up to methods in *this*
28226                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28227                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28228                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28229                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28230                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28231                 }
28232     },
28233
28234 /**     Decide whether to drop above or below a View node. */
28235     getDropPoint : function(e, n, dd){
28236         if (n == this.el.dom) { return "above"; }
28237                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28238                 var c = t + (b - t) / 2;
28239                 var y = Roo.lib.Event.getPageY(e);
28240                 if(y <= c) {
28241                         return "above";
28242                 }else{
28243                         return "below";
28244                 }
28245     },
28246
28247     onNodeEnter : function(n, dd, e, data){
28248                 return false;
28249     },
28250     
28251     onNodeOver : function(n, dd, e, data){
28252                 var pt = this.getDropPoint(e, n, dd);
28253                 // set the insert point style on the target node
28254                 var dragElClass = this.dropNotAllowed;
28255                 if (pt) {
28256                         var targetElClass;
28257                         if (pt == "above"){
28258                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28259                                 targetElClass = "x-view-drag-insert-above";
28260                         } else {
28261                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28262                                 targetElClass = "x-view-drag-insert-below";
28263                         }
28264                         if (this.lastInsertClass != targetElClass){
28265                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28266                                 this.lastInsertClass = targetElClass;
28267                         }
28268                 }
28269                 return dragElClass;
28270         },
28271
28272     onNodeOut : function(n, dd, e, data){
28273                 this.removeDropIndicators(n);
28274     },
28275
28276     onNodeDrop : function(n, dd, e, data){
28277         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28278                 return false;
28279         }
28280         var pt = this.getDropPoint(e, n, dd);
28281                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28282                 if (pt == "below") { insertAt++; }
28283                 for (var i = 0; i < data.records.length; i++) {
28284                         var r = data.records[i];
28285                         var dup = this.store.getById(r.id);
28286                         if (dup && (dd != this.dragZone)) {
28287                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28288                         } else {
28289                                 if (data.copy) {
28290                                         this.store.insert(insertAt++, r.copy());
28291                                 } else {
28292                                         data.source.isDirtyFlag = true;
28293                                         r.store.remove(r);
28294                                         this.store.insert(insertAt++, r);
28295                                 }
28296                                 this.isDirtyFlag = true;
28297                         }
28298                 }
28299                 this.dragZone.cachedTarget = null;
28300                 return true;
28301     },
28302
28303     removeDropIndicators : function(n){
28304                 if(n){
28305                         Roo.fly(n).removeClass([
28306                                 "x-view-drag-insert-above",
28307                                 "x-view-drag-insert-below"]);
28308                         this.lastInsertClass = "_noclass";
28309                 }
28310     },
28311
28312 /**
28313  *      Utility method. Add a delete option to the DDView's context menu.
28314  *      @param {String} imageUrl The URL of the "delete" icon image.
28315  */
28316         setDeletable: function(imageUrl) {
28317                 if (!this.singleSelect && !this.multiSelect) {
28318                         this.singleSelect = true;
28319                 }
28320                 var c = this.getContextMenu();
28321                 this.contextMenu.on("itemclick", function(item) {
28322                         switch (item.id) {
28323                                 case "delete":
28324                                         this.remove(this.getSelectedIndexes());
28325                                         break;
28326                         }
28327                 }, this);
28328                 this.contextMenu.add({
28329                         icon: imageUrl,
28330                         id: "delete",
28331                         text: 'Delete'
28332                 });
28333         },
28334         
28335 /**     Return the context menu for this DDView. */
28336         getContextMenu: function() {
28337                 if (!this.contextMenu) {
28338 //                      Create the View's context menu
28339                         this.contextMenu = new Roo.menu.Menu({
28340                                 id: this.id + "-contextmenu"
28341                         });
28342                         this.el.on("contextmenu", this.showContextMenu, this);
28343                 }
28344                 return this.contextMenu;
28345         },
28346         
28347         disableContextMenu: function() {
28348                 if (this.contextMenu) {
28349                         this.el.un("contextmenu", this.showContextMenu, this);
28350                 }
28351         },
28352
28353         showContextMenu: function(e, item) {
28354         item = this.findItemFromChild(e.getTarget());
28355                 if (item) {
28356                         e.stopEvent();
28357                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28358                         this.contextMenu.showAt(e.getXY());
28359             }
28360     },
28361
28362 /**
28363  *      Remove {@link Roo.data.Record}s at the specified indices.
28364  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28365  */
28366     remove: function(selectedIndices) {
28367                 selectedIndices = [].concat(selectedIndices);
28368                 for (var i = 0; i < selectedIndices.length; i++) {
28369                         var rec = this.store.getAt(selectedIndices[i]);
28370                         this.store.remove(rec);
28371                 }
28372     },
28373
28374 /**
28375  *      Double click fires the event, but also, if this is draggable, and there is only one other
28376  *      related DropZone, it transfers the selected node.
28377  */
28378     onDblClick : function(e){
28379         var item = this.findItemFromChild(e.getTarget());
28380         if(item){
28381             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28382                 return false;
28383             }
28384             if (this.dragGroup) {
28385                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28386                     while (targets.indexOf(this.dropZone) > -1) {
28387                             targets.remove(this.dropZone);
28388                                 }
28389                     if (targets.length == 1) {
28390                                         this.dragZone.cachedTarget = null;
28391                         var el = Roo.get(targets[0].getEl());
28392                         var box = el.getBox(true);
28393                         targets[0].onNodeDrop(el.dom, {
28394                                 target: el.dom,
28395                                 xy: [box.x, box.y + box.height - 1]
28396                         }, null, this.getDragData(e));
28397                     }
28398                 }
28399         }
28400     },
28401     
28402     handleSelection: function(e) {
28403                 this.dragZone.cachedTarget = null;
28404         var item = this.findItemFromChild(e.getTarget());
28405         if (!item) {
28406                 this.clearSelections(true);
28407                 return;
28408         }
28409                 if (item && (this.multiSelect || this.singleSelect)){
28410                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28411                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28412                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28413                                 this.unselect(item);
28414                         } else {
28415                                 this.select(item, this.multiSelect && e.ctrlKey);
28416                                 this.lastSelection = item;
28417                         }
28418                 }
28419     },
28420
28421     onItemClick : function(item, index, e){
28422                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28423                         return false;
28424                 }
28425                 return true;
28426     },
28427
28428     unselect : function(nodeInfo, suppressEvent){
28429                 var node = this.getNode(nodeInfo);
28430                 if(node && this.isSelected(node)){
28431                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28432                                 Roo.fly(node).removeClass(this.selectedClass);
28433                                 this.selections.remove(node);
28434                                 if(!suppressEvent){
28435                                         this.fireEvent("selectionchange", this, this.selections);
28436                                 }
28437                         }
28438                 }
28439     }
28440 });
28441 /*
28442  * Based on:
28443  * Ext JS Library 1.1.1
28444  * Copyright(c) 2006-2007, Ext JS, LLC.
28445  *
28446  * Originally Released Under LGPL - original licence link has changed is not relivant.
28447  *
28448  * Fork - LGPL
28449  * <script type="text/javascript">
28450  */
28451  
28452 /**
28453  * @class Roo.LayoutManager
28454  * @extends Roo.util.Observable
28455  * Base class for layout managers.
28456  */
28457 Roo.LayoutManager = function(container, config){
28458     Roo.LayoutManager.superclass.constructor.call(this);
28459     this.el = Roo.get(container);
28460     // ie scrollbar fix
28461     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28462         document.body.scroll = "no";
28463     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28464         this.el.position('relative');
28465     }
28466     this.id = this.el.id;
28467     this.el.addClass("x-layout-container");
28468     /** false to disable window resize monitoring @type Boolean */
28469     this.monitorWindowResize = true;
28470     this.regions = {};
28471     this.addEvents({
28472         /**
28473          * @event layout
28474          * Fires when a layout is performed. 
28475          * @param {Roo.LayoutManager} this
28476          */
28477         "layout" : true,
28478         /**
28479          * @event regionresized
28480          * Fires when the user resizes a region. 
28481          * @param {Roo.LayoutRegion} region The resized region
28482          * @param {Number} newSize The new size (width for east/west, height for north/south)
28483          */
28484         "regionresized" : true,
28485         /**
28486          * @event regioncollapsed
28487          * Fires when a region is collapsed. 
28488          * @param {Roo.LayoutRegion} region The collapsed region
28489          */
28490         "regioncollapsed" : true,
28491         /**
28492          * @event regionexpanded
28493          * Fires when a region is expanded.  
28494          * @param {Roo.LayoutRegion} region The expanded region
28495          */
28496         "regionexpanded" : true
28497     });
28498     this.updating = false;
28499     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28500 };
28501
28502 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28503     /**
28504      * Returns true if this layout is currently being updated
28505      * @return {Boolean}
28506      */
28507     isUpdating : function(){
28508         return this.updating; 
28509     },
28510     
28511     /**
28512      * Suspend the LayoutManager from doing auto-layouts while
28513      * making multiple add or remove calls
28514      */
28515     beginUpdate : function(){
28516         this.updating = true;    
28517     },
28518     
28519     /**
28520      * Restore auto-layouts and optionally disable the manager from performing a layout
28521      * @param {Boolean} noLayout true to disable a layout update 
28522      */
28523     endUpdate : function(noLayout){
28524         this.updating = false;
28525         if(!noLayout){
28526             this.layout();
28527         }    
28528     },
28529     
28530     layout: function(){
28531         
28532     },
28533     
28534     onRegionResized : function(region, newSize){
28535         this.fireEvent("regionresized", region, newSize);
28536         this.layout();
28537     },
28538     
28539     onRegionCollapsed : function(region){
28540         this.fireEvent("regioncollapsed", region);
28541     },
28542     
28543     onRegionExpanded : function(region){
28544         this.fireEvent("regionexpanded", region);
28545     },
28546         
28547     /**
28548      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28549      * performs box-model adjustments.
28550      * @return {Object} The size as an object {width: (the width), height: (the height)}
28551      */
28552     getViewSize : function(){
28553         var size;
28554         if(this.el.dom != document.body){
28555             size = this.el.getSize();
28556         }else{
28557             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28558         }
28559         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28560         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28561         return size;
28562     },
28563     
28564     /**
28565      * Returns the Element this layout is bound to.
28566      * @return {Roo.Element}
28567      */
28568     getEl : function(){
28569         return this.el;
28570     },
28571     
28572     /**
28573      * Returns the specified region.
28574      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28575      * @return {Roo.LayoutRegion}
28576      */
28577     getRegion : function(target){
28578         return this.regions[target.toLowerCase()];
28579     },
28580     
28581     onWindowResize : function(){
28582         if(this.monitorWindowResize){
28583             this.layout();
28584         }
28585     }
28586 });/*
28587  * Based on:
28588  * Ext JS Library 1.1.1
28589  * Copyright(c) 2006-2007, Ext JS, LLC.
28590  *
28591  * Originally Released Under LGPL - original licence link has changed is not relivant.
28592  *
28593  * Fork - LGPL
28594  * <script type="text/javascript">
28595  */
28596 /**
28597  * @class Roo.BorderLayout
28598  * @extends Roo.LayoutManager
28599  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28600  * please see: <br><br>
28601  * <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>
28602  * <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>
28603  * Example:
28604  <pre><code>
28605  var layout = new Roo.BorderLayout(document.body, {
28606     north: {
28607         initialSize: 25,
28608         titlebar: false
28609     },
28610     west: {
28611         split:true,
28612         initialSize: 200,
28613         minSize: 175,
28614         maxSize: 400,
28615         titlebar: true,
28616         collapsible: true
28617     },
28618     east: {
28619         split:true,
28620         initialSize: 202,
28621         minSize: 175,
28622         maxSize: 400,
28623         titlebar: true,
28624         collapsible: true
28625     },
28626     south: {
28627         split:true,
28628         initialSize: 100,
28629         minSize: 100,
28630         maxSize: 200,
28631         titlebar: true,
28632         collapsible: true
28633     },
28634     center: {
28635         titlebar: true,
28636         autoScroll:true,
28637         resizeTabs: true,
28638         minTabWidth: 50,
28639         preferredTabWidth: 150
28640     }
28641 });
28642
28643 // shorthand
28644 var CP = Roo.ContentPanel;
28645
28646 layout.beginUpdate();
28647 layout.add("north", new CP("north", "North"));
28648 layout.add("south", new CP("south", {title: "South", closable: true}));
28649 layout.add("west", new CP("west", {title: "West"}));
28650 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28651 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28652 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28653 layout.getRegion("center").showPanel("center1");
28654 layout.endUpdate();
28655 </code></pre>
28656
28657 <b>The container the layout is rendered into can be either the body element or any other element.
28658 If it is not the body element, the container needs to either be an absolute positioned element,
28659 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28660 the container size if it is not the body element.</b>
28661
28662 * @constructor
28663 * Create a new BorderLayout
28664 * @param {String/HTMLElement/Element} container The container this layout is bound to
28665 * @param {Object} config Configuration options
28666  */
28667 Roo.BorderLayout = function(container, config){
28668     config = config || {};
28669     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28670     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28671     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28672         var target = this.factory.validRegions[i];
28673         if(config[target]){
28674             this.addRegion(target, config[target]);
28675         }
28676     }
28677 };
28678
28679 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28680     /**
28681      * Creates and adds a new region if it doesn't already exist.
28682      * @param {String} target The target region key (north, south, east, west or center).
28683      * @param {Object} config The regions config object
28684      * @return {BorderLayoutRegion} The new region
28685      */
28686     addRegion : function(target, config){
28687         if(!this.regions[target]){
28688             var r = this.factory.create(target, this, config);
28689             this.bindRegion(target, r);
28690         }
28691         return this.regions[target];
28692     },
28693
28694     // private (kinda)
28695     bindRegion : function(name, r){
28696         this.regions[name] = r;
28697         r.on("visibilitychange", this.layout, this);
28698         r.on("paneladded", this.layout, this);
28699         r.on("panelremoved", this.layout, this);
28700         r.on("invalidated", this.layout, this);
28701         r.on("resized", this.onRegionResized, this);
28702         r.on("collapsed", this.onRegionCollapsed, this);
28703         r.on("expanded", this.onRegionExpanded, this);
28704     },
28705
28706     /**
28707      * Performs a layout update.
28708      */
28709     layout : function(){
28710         if(this.updating) {
28711             return;
28712         }
28713         var size = this.getViewSize();
28714         var w = size.width;
28715         var h = size.height;
28716         var centerW = w;
28717         var centerH = h;
28718         var centerY = 0;
28719         var centerX = 0;
28720         //var x = 0, y = 0;
28721
28722         var rs = this.regions;
28723         var north = rs["north"];
28724         var south = rs["south"]; 
28725         var west = rs["west"];
28726         var east = rs["east"];
28727         var center = rs["center"];
28728         //if(this.hideOnLayout){ // not supported anymore
28729             //c.el.setStyle("display", "none");
28730         //}
28731         if(north && north.isVisible()){
28732             var b = north.getBox();
28733             var m = north.getMargins();
28734             b.width = w - (m.left+m.right);
28735             b.x = m.left;
28736             b.y = m.top;
28737             centerY = b.height + b.y + m.bottom;
28738             centerH -= centerY;
28739             north.updateBox(this.safeBox(b));
28740         }
28741         if(south && south.isVisible()){
28742             var b = south.getBox();
28743             var m = south.getMargins();
28744             b.width = w - (m.left+m.right);
28745             b.x = m.left;
28746             var totalHeight = (b.height + m.top + m.bottom);
28747             b.y = h - totalHeight + m.top;
28748             centerH -= totalHeight;
28749             south.updateBox(this.safeBox(b));
28750         }
28751         if(west && west.isVisible()){
28752             var b = west.getBox();
28753             var m = west.getMargins();
28754             b.height = centerH - (m.top+m.bottom);
28755             b.x = m.left;
28756             b.y = centerY + m.top;
28757             var totalWidth = (b.width + m.left + m.right);
28758             centerX += totalWidth;
28759             centerW -= totalWidth;
28760             west.updateBox(this.safeBox(b));
28761         }
28762         if(east && east.isVisible()){
28763             var b = east.getBox();
28764             var m = east.getMargins();
28765             b.height = centerH - (m.top+m.bottom);
28766             var totalWidth = (b.width + m.left + m.right);
28767             b.x = w - totalWidth + m.left;
28768             b.y = centerY + m.top;
28769             centerW -= totalWidth;
28770             east.updateBox(this.safeBox(b));
28771         }
28772         if(center){
28773             var m = center.getMargins();
28774             var centerBox = {
28775                 x: centerX + m.left,
28776                 y: centerY + m.top,
28777                 width: centerW - (m.left+m.right),
28778                 height: centerH - (m.top+m.bottom)
28779             };
28780             //if(this.hideOnLayout){
28781                 //center.el.setStyle("display", "block");
28782             //}
28783             center.updateBox(this.safeBox(centerBox));
28784         }
28785         this.el.repaint();
28786         this.fireEvent("layout", this);
28787     },
28788
28789     // private
28790     safeBox : function(box){
28791         box.width = Math.max(0, box.width);
28792         box.height = Math.max(0, box.height);
28793         return box;
28794     },
28795
28796     /**
28797      * Adds a ContentPanel (or subclass) to this layout.
28798      * @param {String} target The target region key (north, south, east, west or center).
28799      * @param {Roo.ContentPanel} panel The panel to add
28800      * @return {Roo.ContentPanel} The added panel
28801      */
28802     add : function(target, panel){
28803          
28804         target = target.toLowerCase();
28805         return this.regions[target].add(panel);
28806     },
28807
28808     /**
28809      * Remove a ContentPanel (or subclass) to this layout.
28810      * @param {String} target The target region key (north, south, east, west or center).
28811      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28812      * @return {Roo.ContentPanel} The removed panel
28813      */
28814     remove : function(target, panel){
28815         target = target.toLowerCase();
28816         return this.regions[target].remove(panel);
28817     },
28818
28819     /**
28820      * Searches all regions for a panel with the specified id
28821      * @param {String} panelId
28822      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28823      */
28824     findPanel : function(panelId){
28825         var rs = this.regions;
28826         for(var target in rs){
28827             if(typeof rs[target] != "function"){
28828                 var p = rs[target].getPanel(panelId);
28829                 if(p){
28830                     return p;
28831                 }
28832             }
28833         }
28834         return null;
28835     },
28836
28837     /**
28838      * Searches all regions for a panel with the specified id and activates (shows) it.
28839      * @param {String/ContentPanel} panelId The panels id or the panel itself
28840      * @return {Roo.ContentPanel} The shown panel or null
28841      */
28842     showPanel : function(panelId) {
28843       var rs = this.regions;
28844       for(var target in rs){
28845          var r = rs[target];
28846          if(typeof r != "function"){
28847             if(r.hasPanel(panelId)){
28848                return r.showPanel(panelId);
28849             }
28850          }
28851       }
28852       return null;
28853    },
28854
28855    /**
28856      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28857      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28858      */
28859     restoreState : function(provider){
28860         if(!provider){
28861             provider = Roo.state.Manager;
28862         }
28863         var sm = new Roo.LayoutStateManager();
28864         sm.init(this, provider);
28865     },
28866
28867     /**
28868      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28869      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28870      * a valid ContentPanel config object.  Example:
28871      * <pre><code>
28872 // Create the main layout
28873 var layout = new Roo.BorderLayout('main-ct', {
28874     west: {
28875         split:true,
28876         minSize: 175,
28877         titlebar: true
28878     },
28879     center: {
28880         title:'Components'
28881     }
28882 }, 'main-ct');
28883
28884 // Create and add multiple ContentPanels at once via configs
28885 layout.batchAdd({
28886    west: {
28887        id: 'source-files',
28888        autoCreate:true,
28889        title:'Ext Source Files',
28890        autoScroll:true,
28891        fitToFrame:true
28892    },
28893    center : {
28894        el: cview,
28895        autoScroll:true,
28896        fitToFrame:true,
28897        toolbar: tb,
28898        resizeEl:'cbody'
28899    }
28900 });
28901 </code></pre>
28902      * @param {Object} regions An object containing ContentPanel configs by region name
28903      */
28904     batchAdd : function(regions){
28905         this.beginUpdate();
28906         for(var rname in regions){
28907             var lr = this.regions[rname];
28908             if(lr){
28909                 this.addTypedPanels(lr, regions[rname]);
28910             }
28911         }
28912         this.endUpdate();
28913     },
28914
28915     // private
28916     addTypedPanels : function(lr, ps){
28917         if(typeof ps == 'string'){
28918             lr.add(new Roo.ContentPanel(ps));
28919         }
28920         else if(ps instanceof Array){
28921             for(var i =0, len = ps.length; i < len; i++){
28922                 this.addTypedPanels(lr, ps[i]);
28923             }
28924         }
28925         else if(!ps.events){ // raw config?
28926             var el = ps.el;
28927             delete ps.el; // prevent conflict
28928             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28929         }
28930         else {  // panel object assumed!
28931             lr.add(ps);
28932         }
28933     },
28934     /**
28935      * Adds a xtype elements to the layout.
28936      * <pre><code>
28937
28938 layout.addxtype({
28939        xtype : 'ContentPanel',
28940        region: 'west',
28941        items: [ .... ]
28942    }
28943 );
28944
28945 layout.addxtype({
28946         xtype : 'NestedLayoutPanel',
28947         region: 'west',
28948         layout: {
28949            center: { },
28950            west: { }   
28951         },
28952         items : [ ... list of content panels or nested layout panels.. ]
28953    }
28954 );
28955 </code></pre>
28956      * @param {Object} cfg Xtype definition of item to add.
28957      */
28958     addxtype : function(cfg)
28959     {
28960         // basically accepts a pannel...
28961         // can accept a layout region..!?!?
28962         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28963         
28964         if (!cfg.xtype.match(/Panel$/)) {
28965             return false;
28966         }
28967         var ret = false;
28968         
28969         if (typeof(cfg.region) == 'undefined') {
28970             Roo.log("Failed to add Panel, region was not set");
28971             Roo.log(cfg);
28972             return false;
28973         }
28974         var region = cfg.region;
28975         delete cfg.region;
28976         
28977           
28978         var xitems = [];
28979         if (cfg.items) {
28980             xitems = cfg.items;
28981             delete cfg.items;
28982         }
28983         var nb = false;
28984         
28985         switch(cfg.xtype) 
28986         {
28987             case 'ContentPanel':  // ContentPanel (el, cfg)
28988             case 'ScrollPanel':  // ContentPanel (el, cfg)
28989             case 'ViewPanel': 
28990                 if(cfg.autoCreate) {
28991                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28992                 } else {
28993                     var el = this.el.createChild();
28994                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28995                 }
28996                 
28997                 this.add(region, ret);
28998                 break;
28999             
29000             
29001             case 'TreePanel': // our new panel!
29002                 cfg.el = this.el.createChild();
29003                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29004                 this.add(region, ret);
29005                 break;
29006             
29007             case 'NestedLayoutPanel': 
29008                 // create a new Layout (which is  a Border Layout...
29009                 var el = this.el.createChild();
29010                 var clayout = cfg.layout;
29011                 delete cfg.layout;
29012                 clayout.items   = clayout.items  || [];
29013                 // replace this exitems with the clayout ones..
29014                 xitems = clayout.items;
29015                  
29016                 
29017                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29018                     cfg.background = false;
29019                 }
29020                 var layout = new Roo.BorderLayout(el, clayout);
29021                 
29022                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29023                 //console.log('adding nested layout panel '  + cfg.toSource());
29024                 this.add(region, ret);
29025                 nb = {}; /// find first...
29026                 break;
29027                 
29028             case 'GridPanel': 
29029             
29030                 // needs grid and region
29031                 
29032                 //var el = this.getRegion(region).el.createChild();
29033                 var el = this.el.createChild();
29034                 // create the grid first...
29035                 
29036                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29037                 delete cfg.grid;
29038                 if (region == 'center' && this.active ) {
29039                     cfg.background = false;
29040                 }
29041                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29042                 
29043                 this.add(region, ret);
29044                 if (cfg.background) {
29045                     ret.on('activate', function(gp) {
29046                         if (!gp.grid.rendered) {
29047                             gp.grid.render();
29048                         }
29049                     });
29050                 } else {
29051                     grid.render();
29052                 }
29053                 break;
29054            
29055            
29056            
29057                 
29058                 
29059                 
29060             default:
29061                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29062                     
29063                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29064                     this.add(region, ret);
29065                 } else {
29066                 
29067                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29068                     return null;
29069                 }
29070                 
29071              // GridPanel (grid, cfg)
29072             
29073         }
29074         this.beginUpdate();
29075         // add children..
29076         var region = '';
29077         var abn = {};
29078         Roo.each(xitems, function(i)  {
29079             region = nb && i.region ? i.region : false;
29080             
29081             var add = ret.addxtype(i);
29082            
29083             if (region) {
29084                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29085                 if (!i.background) {
29086                     abn[region] = nb[region] ;
29087                 }
29088             }
29089             
29090         });
29091         this.endUpdate();
29092
29093         // make the last non-background panel active..
29094         //if (nb) { Roo.log(abn); }
29095         if (nb) {
29096             
29097             for(var r in abn) {
29098                 region = this.getRegion(r);
29099                 if (region) {
29100                     // tried using nb[r], but it does not work..
29101                      
29102                     region.showPanel(abn[r]);
29103                    
29104                 }
29105             }
29106         }
29107         return ret;
29108         
29109     }
29110 });
29111
29112 /**
29113  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29114  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29115  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29116  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29117  * <pre><code>
29118 // shorthand
29119 var CP = Roo.ContentPanel;
29120
29121 var layout = Roo.BorderLayout.create({
29122     north: {
29123         initialSize: 25,
29124         titlebar: false,
29125         panels: [new CP("north", "North")]
29126     },
29127     west: {
29128         split:true,
29129         initialSize: 200,
29130         minSize: 175,
29131         maxSize: 400,
29132         titlebar: true,
29133         collapsible: true,
29134         panels: [new CP("west", {title: "West"})]
29135     },
29136     east: {
29137         split:true,
29138         initialSize: 202,
29139         minSize: 175,
29140         maxSize: 400,
29141         titlebar: true,
29142         collapsible: true,
29143         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29144     },
29145     south: {
29146         split:true,
29147         initialSize: 100,
29148         minSize: 100,
29149         maxSize: 200,
29150         titlebar: true,
29151         collapsible: true,
29152         panels: [new CP("south", {title: "South", closable: true})]
29153     },
29154     center: {
29155         titlebar: true,
29156         autoScroll:true,
29157         resizeTabs: true,
29158         minTabWidth: 50,
29159         preferredTabWidth: 150,
29160         panels: [
29161             new CP("center1", {title: "Close Me", closable: true}),
29162             new CP("center2", {title: "Center Panel", closable: false})
29163         ]
29164     }
29165 }, document.body);
29166
29167 layout.getRegion("center").showPanel("center1");
29168 </code></pre>
29169  * @param config
29170  * @param targetEl
29171  */
29172 Roo.BorderLayout.create = function(config, targetEl){
29173     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29174     layout.beginUpdate();
29175     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29176     for(var j = 0, jlen = regions.length; j < jlen; j++){
29177         var lr = regions[j];
29178         if(layout.regions[lr] && config[lr].panels){
29179             var r = layout.regions[lr];
29180             var ps = config[lr].panels;
29181             layout.addTypedPanels(r, ps);
29182         }
29183     }
29184     layout.endUpdate();
29185     return layout;
29186 };
29187
29188 // private
29189 Roo.BorderLayout.RegionFactory = {
29190     // private
29191     validRegions : ["north","south","east","west","center"],
29192
29193     // private
29194     create : function(target, mgr, config){
29195         target = target.toLowerCase();
29196         if(config.lightweight || config.basic){
29197             return new Roo.BasicLayoutRegion(mgr, config, target);
29198         }
29199         switch(target){
29200             case "north":
29201                 return new Roo.NorthLayoutRegion(mgr, config);
29202             case "south":
29203                 return new Roo.SouthLayoutRegion(mgr, config);
29204             case "east":
29205                 return new Roo.EastLayoutRegion(mgr, config);
29206             case "west":
29207                 return new Roo.WestLayoutRegion(mgr, config);
29208             case "center":
29209                 return new Roo.CenterLayoutRegion(mgr, config);
29210         }
29211         throw 'Layout region "'+target+'" not supported.';
29212     }
29213 };/*
29214  * Based on:
29215  * Ext JS Library 1.1.1
29216  * Copyright(c) 2006-2007, Ext JS, LLC.
29217  *
29218  * Originally Released Under LGPL - original licence link has changed is not relivant.
29219  *
29220  * Fork - LGPL
29221  * <script type="text/javascript">
29222  */
29223  
29224 /**
29225  * @class Roo.BasicLayoutRegion
29226  * @extends Roo.util.Observable
29227  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29228  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29229  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29230  */
29231 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29232     this.mgr = mgr;
29233     this.position  = pos;
29234     this.events = {
29235         /**
29236          * @scope Roo.BasicLayoutRegion
29237          */
29238         
29239         /**
29240          * @event beforeremove
29241          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29242          * @param {Roo.LayoutRegion} this
29243          * @param {Roo.ContentPanel} panel The panel
29244          * @param {Object} e The cancel event object
29245          */
29246         "beforeremove" : true,
29247         /**
29248          * @event invalidated
29249          * Fires when the layout for this region is changed.
29250          * @param {Roo.LayoutRegion} this
29251          */
29252         "invalidated" : true,
29253         /**
29254          * @event visibilitychange
29255          * Fires when this region is shown or hidden 
29256          * @param {Roo.LayoutRegion} this
29257          * @param {Boolean} visibility true or false
29258          */
29259         "visibilitychange" : true,
29260         /**
29261          * @event paneladded
29262          * Fires when a panel is added. 
29263          * @param {Roo.LayoutRegion} this
29264          * @param {Roo.ContentPanel} panel The panel
29265          */
29266         "paneladded" : true,
29267         /**
29268          * @event panelremoved
29269          * Fires when a panel is removed. 
29270          * @param {Roo.LayoutRegion} this
29271          * @param {Roo.ContentPanel} panel The panel
29272          */
29273         "panelremoved" : true,
29274         /**
29275          * @event beforecollapse
29276          * Fires when this region before collapse.
29277          * @param {Roo.LayoutRegion} this
29278          */
29279         "beforecollapse" : true,
29280         /**
29281          * @event collapsed
29282          * Fires when this region is collapsed.
29283          * @param {Roo.LayoutRegion} this
29284          */
29285         "collapsed" : true,
29286         /**
29287          * @event expanded
29288          * Fires when this region is expanded.
29289          * @param {Roo.LayoutRegion} this
29290          */
29291         "expanded" : true,
29292         /**
29293          * @event slideshow
29294          * Fires when this region is slid into view.
29295          * @param {Roo.LayoutRegion} this
29296          */
29297         "slideshow" : true,
29298         /**
29299          * @event slidehide
29300          * Fires when this region slides out of view. 
29301          * @param {Roo.LayoutRegion} this
29302          */
29303         "slidehide" : true,
29304         /**
29305          * @event panelactivated
29306          * Fires when a panel is activated. 
29307          * @param {Roo.LayoutRegion} this
29308          * @param {Roo.ContentPanel} panel The activated panel
29309          */
29310         "panelactivated" : true,
29311         /**
29312          * @event resized
29313          * Fires when the user resizes this region. 
29314          * @param {Roo.LayoutRegion} this
29315          * @param {Number} newSize The new size (width for east/west, height for north/south)
29316          */
29317         "resized" : true
29318     };
29319     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29320     this.panels = new Roo.util.MixedCollection();
29321     this.panels.getKey = this.getPanelId.createDelegate(this);
29322     this.box = null;
29323     this.activePanel = null;
29324     // ensure listeners are added...
29325     
29326     if (config.listeners || config.events) {
29327         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29328             listeners : config.listeners || {},
29329             events : config.events || {}
29330         });
29331     }
29332     
29333     if(skipConfig !== true){
29334         this.applyConfig(config);
29335     }
29336 };
29337
29338 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29339     getPanelId : function(p){
29340         return p.getId();
29341     },
29342     
29343     applyConfig : function(config){
29344         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29345         this.config = config;
29346         
29347     },
29348     
29349     /**
29350      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29351      * the width, for horizontal (north, south) the height.
29352      * @param {Number} newSize The new width or height
29353      */
29354     resizeTo : function(newSize){
29355         var el = this.el ? this.el :
29356                  (this.activePanel ? this.activePanel.getEl() : null);
29357         if(el){
29358             switch(this.position){
29359                 case "east":
29360                 case "west":
29361                     el.setWidth(newSize);
29362                     this.fireEvent("resized", this, newSize);
29363                 break;
29364                 case "north":
29365                 case "south":
29366                     el.setHeight(newSize);
29367                     this.fireEvent("resized", this, newSize);
29368                 break;                
29369             }
29370         }
29371     },
29372     
29373     getBox : function(){
29374         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29375     },
29376     
29377     getMargins : function(){
29378         return this.margins;
29379     },
29380     
29381     updateBox : function(box){
29382         this.box = box;
29383         var el = this.activePanel.getEl();
29384         el.dom.style.left = box.x + "px";
29385         el.dom.style.top = box.y + "px";
29386         this.activePanel.setSize(box.width, box.height);
29387     },
29388     
29389     /**
29390      * Returns the container element for this region.
29391      * @return {Roo.Element}
29392      */
29393     getEl : function(){
29394         return this.activePanel;
29395     },
29396     
29397     /**
29398      * Returns true if this region is currently visible.
29399      * @return {Boolean}
29400      */
29401     isVisible : function(){
29402         return this.activePanel ? true : false;
29403     },
29404     
29405     setActivePanel : function(panel){
29406         panel = this.getPanel(panel);
29407         if(this.activePanel && this.activePanel != panel){
29408             this.activePanel.setActiveState(false);
29409             this.activePanel.getEl().setLeftTop(-10000,-10000);
29410         }
29411         this.activePanel = panel;
29412         panel.setActiveState(true);
29413         if(this.box){
29414             panel.setSize(this.box.width, this.box.height);
29415         }
29416         this.fireEvent("panelactivated", this, panel);
29417         this.fireEvent("invalidated");
29418     },
29419     
29420     /**
29421      * Show the specified panel.
29422      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29423      * @return {Roo.ContentPanel} The shown panel or null
29424      */
29425     showPanel : function(panel){
29426         if(panel = this.getPanel(panel)){
29427             this.setActivePanel(panel);
29428         }
29429         return panel;
29430     },
29431     
29432     /**
29433      * Get the active panel for this region.
29434      * @return {Roo.ContentPanel} The active panel or null
29435      */
29436     getActivePanel : function(){
29437         return this.activePanel;
29438     },
29439     
29440     /**
29441      * Add the passed ContentPanel(s)
29442      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29443      * @return {Roo.ContentPanel} The panel added (if only one was added)
29444      */
29445     add : function(panel){
29446         if(arguments.length > 1){
29447             for(var i = 0, len = arguments.length; i < len; i++) {
29448                 this.add(arguments[i]);
29449             }
29450             return null;
29451         }
29452         if(this.hasPanel(panel)){
29453             this.showPanel(panel);
29454             return panel;
29455         }
29456         var el = panel.getEl();
29457         if(el.dom.parentNode != this.mgr.el.dom){
29458             this.mgr.el.dom.appendChild(el.dom);
29459         }
29460         if(panel.setRegion){
29461             panel.setRegion(this);
29462         }
29463         this.panels.add(panel);
29464         el.setStyle("position", "absolute");
29465         if(!panel.background){
29466             this.setActivePanel(panel);
29467             if(this.config.initialSize && this.panels.getCount()==1){
29468                 this.resizeTo(this.config.initialSize);
29469             }
29470         }
29471         this.fireEvent("paneladded", this, panel);
29472         return panel;
29473     },
29474     
29475     /**
29476      * Returns true if the panel is in this region.
29477      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29478      * @return {Boolean}
29479      */
29480     hasPanel : function(panel){
29481         if(typeof panel == "object"){ // must be panel obj
29482             panel = panel.getId();
29483         }
29484         return this.getPanel(panel) ? true : false;
29485     },
29486     
29487     /**
29488      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29489      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29490      * @param {Boolean} preservePanel Overrides the config preservePanel option
29491      * @return {Roo.ContentPanel} The panel that was removed
29492      */
29493     remove : function(panel, preservePanel){
29494         panel = this.getPanel(panel);
29495         if(!panel){
29496             return null;
29497         }
29498         var e = {};
29499         this.fireEvent("beforeremove", this, panel, e);
29500         if(e.cancel === true){
29501             return null;
29502         }
29503         var panelId = panel.getId();
29504         this.panels.removeKey(panelId);
29505         return panel;
29506     },
29507     
29508     /**
29509      * Returns the panel specified or null if it's not in this region.
29510      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29511      * @return {Roo.ContentPanel}
29512      */
29513     getPanel : function(id){
29514         if(typeof id == "object"){ // must be panel obj
29515             return id;
29516         }
29517         return this.panels.get(id);
29518     },
29519     
29520     /**
29521      * Returns this regions position (north/south/east/west/center).
29522      * @return {String} 
29523      */
29524     getPosition: function(){
29525         return this.position;    
29526     }
29527 });/*
29528  * Based on:
29529  * Ext JS Library 1.1.1
29530  * Copyright(c) 2006-2007, Ext JS, LLC.
29531  *
29532  * Originally Released Under LGPL - original licence link has changed is not relivant.
29533  *
29534  * Fork - LGPL
29535  * <script type="text/javascript">
29536  */
29537  
29538 /**
29539  * @class Roo.LayoutRegion
29540  * @extends Roo.BasicLayoutRegion
29541  * This class represents a region in a layout manager.
29542  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29543  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29544  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29545  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29546  * @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})
29547  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29548  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29549  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29550  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29551  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29552  * @cfg {String}    title           The title for the region (overrides panel titles)
29553  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29554  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29555  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29556  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29557  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29558  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29559  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29560  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29561  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29562  * @cfg {Boolean}   showPin         True to show a pin button
29563  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29564  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29565  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29566  * @cfg {Number}    width           For East/West panels
29567  * @cfg {Number}    height          For North/South panels
29568  * @cfg {Boolean}   split           To show the splitter
29569  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29570  */
29571 Roo.LayoutRegion = function(mgr, config, pos){
29572     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29573     var dh = Roo.DomHelper;
29574     /** This region's container element 
29575     * @type Roo.Element */
29576     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29577     /** This region's title element 
29578     * @type Roo.Element */
29579
29580     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29581         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29582         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29583     ]}, true);
29584     this.titleEl.enableDisplayMode();
29585     /** This region's title text element 
29586     * @type HTMLElement */
29587     this.titleTextEl = this.titleEl.dom.firstChild;
29588     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29589     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29590     this.closeBtn.enableDisplayMode();
29591     this.closeBtn.on("click", this.closeClicked, this);
29592     this.closeBtn.hide();
29593
29594     this.createBody(config);
29595     this.visible = true;
29596     this.collapsed = false;
29597
29598     if(config.hideWhenEmpty){
29599         this.hide();
29600         this.on("paneladded", this.validateVisibility, this);
29601         this.on("panelremoved", this.validateVisibility, this);
29602     }
29603     this.applyConfig(config);
29604 };
29605
29606 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29607
29608     createBody : function(){
29609         /** This region's body element 
29610         * @type Roo.Element */
29611         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29612     },
29613
29614     applyConfig : function(c){
29615         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29616             var dh = Roo.DomHelper;
29617             if(c.titlebar !== false){
29618                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29619                 this.collapseBtn.on("click", this.collapse, this);
29620                 this.collapseBtn.enableDisplayMode();
29621
29622                 if(c.showPin === true || this.showPin){
29623                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29624                     this.stickBtn.enableDisplayMode();
29625                     this.stickBtn.on("click", this.expand, this);
29626                     this.stickBtn.hide();
29627                 }
29628             }
29629             /** This region's collapsed element
29630             * @type Roo.Element */
29631             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29632                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29633             ]}, true);
29634             if(c.floatable !== false){
29635                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29636                this.collapsedEl.on("click", this.collapseClick, this);
29637             }
29638
29639             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29640                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29641                    id: "message", unselectable: "on", style:{"float":"left"}});
29642                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29643              }
29644             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29645             this.expandBtn.on("click", this.expand, this);
29646         }
29647         if(this.collapseBtn){
29648             this.collapseBtn.setVisible(c.collapsible == true);
29649         }
29650         this.cmargins = c.cmargins || this.cmargins ||
29651                          (this.position == "west" || this.position == "east" ?
29652                              {top: 0, left: 2, right:2, bottom: 0} :
29653                              {top: 2, left: 0, right:0, bottom: 2});
29654         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29655         this.bottomTabs = c.tabPosition != "top";
29656         this.autoScroll = c.autoScroll || false;
29657         if(this.autoScroll){
29658             this.bodyEl.setStyle("overflow", "auto");
29659         }else{
29660             this.bodyEl.setStyle("overflow", "hidden");
29661         }
29662         //if(c.titlebar !== false){
29663             if((!c.titlebar && !c.title) || c.titlebar === false){
29664                 this.titleEl.hide();
29665             }else{
29666                 this.titleEl.show();
29667                 if(c.title){
29668                     this.titleTextEl.innerHTML = c.title;
29669                 }
29670             }
29671         //}
29672         this.duration = c.duration || .30;
29673         this.slideDuration = c.slideDuration || .45;
29674         this.config = c;
29675         if(c.collapsed){
29676             this.collapse(true);
29677         }
29678         if(c.hidden){
29679             this.hide();
29680         }
29681     },
29682     /**
29683      * Returns true if this region is currently visible.
29684      * @return {Boolean}
29685      */
29686     isVisible : function(){
29687         return this.visible;
29688     },
29689
29690     /**
29691      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29692      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29693      */
29694     setCollapsedTitle : function(title){
29695         title = title || "&#160;";
29696         if(this.collapsedTitleTextEl){
29697             this.collapsedTitleTextEl.innerHTML = title;
29698         }
29699     },
29700
29701     getBox : function(){
29702         var b;
29703         if(!this.collapsed){
29704             b = this.el.getBox(false, true);
29705         }else{
29706             b = this.collapsedEl.getBox(false, true);
29707         }
29708         return b;
29709     },
29710
29711     getMargins : function(){
29712         return this.collapsed ? this.cmargins : this.margins;
29713     },
29714
29715     highlight : function(){
29716         this.el.addClass("x-layout-panel-dragover");
29717     },
29718
29719     unhighlight : function(){
29720         this.el.removeClass("x-layout-panel-dragover");
29721     },
29722
29723     updateBox : function(box){
29724         this.box = box;
29725         if(!this.collapsed){
29726             this.el.dom.style.left = box.x + "px";
29727             this.el.dom.style.top = box.y + "px";
29728             this.updateBody(box.width, box.height);
29729         }else{
29730             this.collapsedEl.dom.style.left = box.x + "px";
29731             this.collapsedEl.dom.style.top = box.y + "px";
29732             this.collapsedEl.setSize(box.width, box.height);
29733         }
29734         if(this.tabs){
29735             this.tabs.autoSizeTabs();
29736         }
29737     },
29738
29739     updateBody : function(w, h){
29740         if(w !== null){
29741             this.el.setWidth(w);
29742             w -= this.el.getBorderWidth("rl");
29743             if(this.config.adjustments){
29744                 w += this.config.adjustments[0];
29745             }
29746         }
29747         if(h !== null){
29748             this.el.setHeight(h);
29749             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29750             h -= this.el.getBorderWidth("tb");
29751             if(this.config.adjustments){
29752                 h += this.config.adjustments[1];
29753             }
29754             this.bodyEl.setHeight(h);
29755             if(this.tabs){
29756                 h = this.tabs.syncHeight(h);
29757             }
29758         }
29759         if(this.panelSize){
29760             w = w !== null ? w : this.panelSize.width;
29761             h = h !== null ? h : this.panelSize.height;
29762         }
29763         if(this.activePanel){
29764             var el = this.activePanel.getEl();
29765             w = w !== null ? w : el.getWidth();
29766             h = h !== null ? h : el.getHeight();
29767             this.panelSize = {width: w, height: h};
29768             this.activePanel.setSize(w, h);
29769         }
29770         if(Roo.isIE && this.tabs){
29771             this.tabs.el.repaint();
29772         }
29773     },
29774
29775     /**
29776      * Returns the container element for this region.
29777      * @return {Roo.Element}
29778      */
29779     getEl : function(){
29780         return this.el;
29781     },
29782
29783     /**
29784      * Hides this region.
29785      */
29786     hide : function(){
29787         if(!this.collapsed){
29788             this.el.dom.style.left = "-2000px";
29789             this.el.hide();
29790         }else{
29791             this.collapsedEl.dom.style.left = "-2000px";
29792             this.collapsedEl.hide();
29793         }
29794         this.visible = false;
29795         this.fireEvent("visibilitychange", this, false);
29796     },
29797
29798     /**
29799      * Shows this region if it was previously hidden.
29800      */
29801     show : function(){
29802         if(!this.collapsed){
29803             this.el.show();
29804         }else{
29805             this.collapsedEl.show();
29806         }
29807         this.visible = true;
29808         this.fireEvent("visibilitychange", this, true);
29809     },
29810
29811     closeClicked : function(){
29812         if(this.activePanel){
29813             this.remove(this.activePanel);
29814         }
29815     },
29816
29817     collapseClick : function(e){
29818         if(this.isSlid){
29819            e.stopPropagation();
29820            this.slideIn();
29821         }else{
29822            e.stopPropagation();
29823            this.slideOut();
29824         }
29825     },
29826
29827     /**
29828      * Collapses this region.
29829      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29830      */
29831     collapse : function(skipAnim, skipCheck = false){
29832         if(this.collapsed) {
29833             return;
29834         }
29835         
29836         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29837             
29838             this.collapsed = true;
29839             if(this.split){
29840                 this.split.el.hide();
29841             }
29842             if(this.config.animate && skipAnim !== true){
29843                 this.fireEvent("invalidated", this);
29844                 this.animateCollapse();
29845             }else{
29846                 this.el.setLocation(-20000,-20000);
29847                 this.el.hide();
29848                 this.collapsedEl.show();
29849                 this.fireEvent("collapsed", this);
29850                 this.fireEvent("invalidated", this);
29851             }
29852         }
29853         
29854     },
29855
29856     animateCollapse : function(){
29857         // overridden
29858     },
29859
29860     /**
29861      * Expands this region if it was previously collapsed.
29862      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29863      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29864      */
29865     expand : function(e, skipAnim){
29866         if(e) {
29867             e.stopPropagation();
29868         }
29869         if(!this.collapsed || this.el.hasActiveFx()) {
29870             return;
29871         }
29872         if(this.isSlid){
29873             this.afterSlideIn();
29874             skipAnim = true;
29875         }
29876         this.collapsed = false;
29877         if(this.config.animate && skipAnim !== true){
29878             this.animateExpand();
29879         }else{
29880             this.el.show();
29881             if(this.split){
29882                 this.split.el.show();
29883             }
29884             this.collapsedEl.setLocation(-2000,-2000);
29885             this.collapsedEl.hide();
29886             this.fireEvent("invalidated", this);
29887             this.fireEvent("expanded", this);
29888         }
29889     },
29890
29891     animateExpand : function(){
29892         // overridden
29893     },
29894
29895     initTabs : function()
29896     {
29897         this.bodyEl.setStyle("overflow", "hidden");
29898         var ts = new Roo.TabPanel(
29899                 this.bodyEl.dom,
29900                 {
29901                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29902                     disableTooltips: this.config.disableTabTips,
29903                     toolbar : this.config.toolbar
29904                 }
29905         );
29906         if(this.config.hideTabs){
29907             ts.stripWrap.setDisplayed(false);
29908         }
29909         this.tabs = ts;
29910         ts.resizeTabs = this.config.resizeTabs === true;
29911         ts.minTabWidth = this.config.minTabWidth || 40;
29912         ts.maxTabWidth = this.config.maxTabWidth || 250;
29913         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29914         ts.monitorResize = false;
29915         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29916         ts.bodyEl.addClass('x-layout-tabs-body');
29917         this.panels.each(this.initPanelAsTab, this);
29918     },
29919
29920     initPanelAsTab : function(panel){
29921         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29922                     this.config.closeOnTab && panel.isClosable());
29923         if(panel.tabTip !== undefined){
29924             ti.setTooltip(panel.tabTip);
29925         }
29926         ti.on("activate", function(){
29927               this.setActivePanel(panel);
29928         }, this);
29929         if(this.config.closeOnTab){
29930             ti.on("beforeclose", function(t, e){
29931                 e.cancel = true;
29932                 this.remove(panel);
29933             }, this);
29934         }
29935         return ti;
29936     },
29937
29938     updatePanelTitle : function(panel, title){
29939         if(this.activePanel == panel){
29940             this.updateTitle(title);
29941         }
29942         if(this.tabs){
29943             var ti = this.tabs.getTab(panel.getEl().id);
29944             ti.setText(title);
29945             if(panel.tabTip !== undefined){
29946                 ti.setTooltip(panel.tabTip);
29947             }
29948         }
29949     },
29950
29951     updateTitle : function(title){
29952         if(this.titleTextEl && !this.config.title){
29953             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29954         }
29955     },
29956
29957     setActivePanel : function(panel){
29958         panel = this.getPanel(panel);
29959         if(this.activePanel && this.activePanel != panel){
29960             this.activePanel.setActiveState(false);
29961         }
29962         this.activePanel = panel;
29963         panel.setActiveState(true);
29964         if(this.panelSize){
29965             panel.setSize(this.panelSize.width, this.panelSize.height);
29966         }
29967         if(this.closeBtn){
29968             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29969         }
29970         this.updateTitle(panel.getTitle());
29971         if(this.tabs){
29972             this.fireEvent("invalidated", this);
29973         }
29974         this.fireEvent("panelactivated", this, panel);
29975     },
29976
29977     /**
29978      * Shows the specified panel.
29979      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29980      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29981      */
29982     showPanel : function(panel)
29983     {
29984         panel = this.getPanel(panel);
29985         if(panel){
29986             if(this.tabs){
29987                 var tab = this.tabs.getTab(panel.getEl().id);
29988                 if(tab.isHidden()){
29989                     this.tabs.unhideTab(tab.id);
29990                 }
29991                 tab.activate();
29992             }else{
29993                 this.setActivePanel(panel);
29994             }
29995         }
29996         return panel;
29997     },
29998
29999     /**
30000      * Get the active panel for this region.
30001      * @return {Roo.ContentPanel} The active panel or null
30002      */
30003     getActivePanel : function(){
30004         return this.activePanel;
30005     },
30006
30007     validateVisibility : function(){
30008         if(this.panels.getCount() < 1){
30009             this.updateTitle("&#160;");
30010             this.closeBtn.hide();
30011             this.hide();
30012         }else{
30013             if(!this.isVisible()){
30014                 this.show();
30015             }
30016         }
30017     },
30018
30019     /**
30020      * Adds the passed ContentPanel(s) to this region.
30021      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30022      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30023      */
30024     add : function(panel){
30025         if(arguments.length > 1){
30026             for(var i = 0, len = arguments.length; i < len; i++) {
30027                 this.add(arguments[i]);
30028             }
30029             return null;
30030         }
30031         if(this.hasPanel(panel)){
30032             this.showPanel(panel);
30033             return panel;
30034         }
30035         panel.setRegion(this);
30036         this.panels.add(panel);
30037         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30038             this.bodyEl.dom.appendChild(panel.getEl().dom);
30039             if(panel.background !== true){
30040                 this.setActivePanel(panel);
30041             }
30042             this.fireEvent("paneladded", this, panel);
30043             return panel;
30044         }
30045         if(!this.tabs){
30046             this.initTabs();
30047         }else{
30048             this.initPanelAsTab(panel);
30049         }
30050         if(panel.background !== true){
30051             this.tabs.activate(panel.getEl().id);
30052         }
30053         this.fireEvent("paneladded", this, panel);
30054         return panel;
30055     },
30056
30057     /**
30058      * Hides the tab for the specified panel.
30059      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30060      */
30061     hidePanel : function(panel){
30062         if(this.tabs && (panel = this.getPanel(panel))){
30063             this.tabs.hideTab(panel.getEl().id);
30064         }
30065     },
30066
30067     /**
30068      * Unhides the tab for a previously hidden panel.
30069      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30070      */
30071     unhidePanel : function(panel){
30072         if(this.tabs && (panel = this.getPanel(panel))){
30073             this.tabs.unhideTab(panel.getEl().id);
30074         }
30075     },
30076
30077     clearPanels : function(){
30078         while(this.panels.getCount() > 0){
30079              this.remove(this.panels.first());
30080         }
30081     },
30082
30083     /**
30084      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30085      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30086      * @param {Boolean} preservePanel Overrides the config preservePanel option
30087      * @return {Roo.ContentPanel} The panel that was removed
30088      */
30089     remove : function(panel, preservePanel){
30090         panel = this.getPanel(panel);
30091         if(!panel){
30092             return null;
30093         }
30094         var e = {};
30095         this.fireEvent("beforeremove", this, panel, e);
30096         if(e.cancel === true){
30097             return null;
30098         }
30099         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30100         var panelId = panel.getId();
30101         this.panels.removeKey(panelId);
30102         if(preservePanel){
30103             document.body.appendChild(panel.getEl().dom);
30104         }
30105         if(this.tabs){
30106             this.tabs.removeTab(panel.getEl().id);
30107         }else if (!preservePanel){
30108             this.bodyEl.dom.removeChild(panel.getEl().dom);
30109         }
30110         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30111             var p = this.panels.first();
30112             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30113             tempEl.appendChild(p.getEl().dom);
30114             this.bodyEl.update("");
30115             this.bodyEl.dom.appendChild(p.getEl().dom);
30116             tempEl = null;
30117             this.updateTitle(p.getTitle());
30118             this.tabs = null;
30119             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30120             this.setActivePanel(p);
30121         }
30122         panel.setRegion(null);
30123         if(this.activePanel == panel){
30124             this.activePanel = null;
30125         }
30126         if(this.config.autoDestroy !== false && preservePanel !== true){
30127             try{panel.destroy();}catch(e){}
30128         }
30129         this.fireEvent("panelremoved", this, panel);
30130         return panel;
30131     },
30132
30133     /**
30134      * Returns the TabPanel component used by this region
30135      * @return {Roo.TabPanel}
30136      */
30137     getTabs : function(){
30138         return this.tabs;
30139     },
30140
30141     createTool : function(parentEl, className){
30142         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30143             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30144         btn.addClassOnOver("x-layout-tools-button-over");
30145         return btn;
30146     }
30147 });/*
30148  * Based on:
30149  * Ext JS Library 1.1.1
30150  * Copyright(c) 2006-2007, Ext JS, LLC.
30151  *
30152  * Originally Released Under LGPL - original licence link has changed is not relivant.
30153  *
30154  * Fork - LGPL
30155  * <script type="text/javascript">
30156  */
30157  
30158
30159
30160 /**
30161  * @class Roo.SplitLayoutRegion
30162  * @extends Roo.LayoutRegion
30163  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30164  */
30165 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30166     this.cursor = cursor;
30167     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30168 };
30169
30170 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30171     splitTip : "Drag to resize.",
30172     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30173     useSplitTips : false,
30174
30175     applyConfig : function(config){
30176         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30177         if(config.split){
30178             if(!this.split){
30179                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30180                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30181                 /** The SplitBar for this region 
30182                 * @type Roo.SplitBar */
30183                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30184                 this.split.on("moved", this.onSplitMove, this);
30185                 this.split.useShim = config.useShim === true;
30186                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30187                 if(this.useSplitTips){
30188                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30189                 }
30190                 if(config.collapsible){
30191                     this.split.el.on("dblclick", this.collapse,  this);
30192                 }
30193             }
30194             if(typeof config.minSize != "undefined"){
30195                 this.split.minSize = config.minSize;
30196             }
30197             if(typeof config.maxSize != "undefined"){
30198                 this.split.maxSize = config.maxSize;
30199             }
30200             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30201                 this.hideSplitter();
30202             }
30203         }
30204     },
30205
30206     getHMaxSize : function(){
30207          var cmax = this.config.maxSize || 10000;
30208          var center = this.mgr.getRegion("center");
30209          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30210     },
30211
30212     getVMaxSize : function(){
30213          var cmax = this.config.maxSize || 10000;
30214          var center = this.mgr.getRegion("center");
30215          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30216     },
30217
30218     onSplitMove : function(split, newSize){
30219         this.fireEvent("resized", this, newSize);
30220     },
30221     
30222     /** 
30223      * Returns the {@link Roo.SplitBar} for this region.
30224      * @return {Roo.SplitBar}
30225      */
30226     getSplitBar : function(){
30227         return this.split;
30228     },
30229     
30230     hide : function(){
30231         this.hideSplitter();
30232         Roo.SplitLayoutRegion.superclass.hide.call(this);
30233     },
30234
30235     hideSplitter : function(){
30236         if(this.split){
30237             this.split.el.setLocation(-2000,-2000);
30238             this.split.el.hide();
30239         }
30240     },
30241
30242     show : function(){
30243         if(this.split){
30244             this.split.el.show();
30245         }
30246         Roo.SplitLayoutRegion.superclass.show.call(this);
30247     },
30248     
30249     beforeSlide: function(){
30250         if(Roo.isGecko){// firefox overflow auto bug workaround
30251             this.bodyEl.clip();
30252             if(this.tabs) {
30253                 this.tabs.bodyEl.clip();
30254             }
30255             if(this.activePanel){
30256                 this.activePanel.getEl().clip();
30257                 
30258                 if(this.activePanel.beforeSlide){
30259                     this.activePanel.beforeSlide();
30260                 }
30261             }
30262         }
30263     },
30264     
30265     afterSlide : function(){
30266         if(Roo.isGecko){// firefox overflow auto bug workaround
30267             this.bodyEl.unclip();
30268             if(this.tabs) {
30269                 this.tabs.bodyEl.unclip();
30270             }
30271             if(this.activePanel){
30272                 this.activePanel.getEl().unclip();
30273                 if(this.activePanel.afterSlide){
30274                     this.activePanel.afterSlide();
30275                 }
30276             }
30277         }
30278     },
30279
30280     initAutoHide : function(){
30281         if(this.autoHide !== false){
30282             if(!this.autoHideHd){
30283                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30284                 this.autoHideHd = {
30285                     "mouseout": function(e){
30286                         if(!e.within(this.el, true)){
30287                             st.delay(500);
30288                         }
30289                     },
30290                     "mouseover" : function(e){
30291                         st.cancel();
30292                     },
30293                     scope : this
30294                 };
30295             }
30296             this.el.on(this.autoHideHd);
30297         }
30298     },
30299
30300     clearAutoHide : function(){
30301         if(this.autoHide !== false){
30302             this.el.un("mouseout", this.autoHideHd.mouseout);
30303             this.el.un("mouseover", this.autoHideHd.mouseover);
30304         }
30305     },
30306
30307     clearMonitor : function(){
30308         Roo.get(document).un("click", this.slideInIf, this);
30309     },
30310
30311     // these names are backwards but not changed for compat
30312     slideOut : function(){
30313         if(this.isSlid || this.el.hasActiveFx()){
30314             return;
30315         }
30316         this.isSlid = true;
30317         if(this.collapseBtn){
30318             this.collapseBtn.hide();
30319         }
30320         this.closeBtnState = this.closeBtn.getStyle('display');
30321         this.closeBtn.hide();
30322         if(this.stickBtn){
30323             this.stickBtn.show();
30324         }
30325         this.el.show();
30326         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30327         this.beforeSlide();
30328         this.el.setStyle("z-index", 10001);
30329         this.el.slideIn(this.getSlideAnchor(), {
30330             callback: function(){
30331                 this.afterSlide();
30332                 this.initAutoHide();
30333                 Roo.get(document).on("click", this.slideInIf, this);
30334                 this.fireEvent("slideshow", this);
30335             },
30336             scope: this,
30337             block: true
30338         });
30339     },
30340
30341     afterSlideIn : function(){
30342         this.clearAutoHide();
30343         this.isSlid = false;
30344         this.clearMonitor();
30345         this.el.setStyle("z-index", "");
30346         if(this.collapseBtn){
30347             this.collapseBtn.show();
30348         }
30349         this.closeBtn.setStyle('display', this.closeBtnState);
30350         if(this.stickBtn){
30351             this.stickBtn.hide();
30352         }
30353         this.fireEvent("slidehide", this);
30354     },
30355
30356     slideIn : function(cb){
30357         if(!this.isSlid || this.el.hasActiveFx()){
30358             Roo.callback(cb);
30359             return;
30360         }
30361         this.isSlid = false;
30362         this.beforeSlide();
30363         this.el.slideOut(this.getSlideAnchor(), {
30364             callback: function(){
30365                 this.el.setLeftTop(-10000, -10000);
30366                 this.afterSlide();
30367                 this.afterSlideIn();
30368                 Roo.callback(cb);
30369             },
30370             scope: this,
30371             block: true
30372         });
30373     },
30374     
30375     slideInIf : function(e){
30376         if(!e.within(this.el)){
30377             this.slideIn();
30378         }
30379     },
30380
30381     animateCollapse : function(){
30382         this.beforeSlide();
30383         this.el.setStyle("z-index", 20000);
30384         var anchor = this.getSlideAnchor();
30385         this.el.slideOut(anchor, {
30386             callback : function(){
30387                 this.el.setStyle("z-index", "");
30388                 this.collapsedEl.slideIn(anchor, {duration:.3});
30389                 this.afterSlide();
30390                 this.el.setLocation(-10000,-10000);
30391                 this.el.hide();
30392                 this.fireEvent("collapsed", this);
30393             },
30394             scope: this,
30395             block: true
30396         });
30397     },
30398
30399     animateExpand : function(){
30400         this.beforeSlide();
30401         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30402         this.el.setStyle("z-index", 20000);
30403         this.collapsedEl.hide({
30404             duration:.1
30405         });
30406         this.el.slideIn(this.getSlideAnchor(), {
30407             callback : function(){
30408                 this.el.setStyle("z-index", "");
30409                 this.afterSlide();
30410                 if(this.split){
30411                     this.split.el.show();
30412                 }
30413                 this.fireEvent("invalidated", this);
30414                 this.fireEvent("expanded", this);
30415             },
30416             scope: this,
30417             block: true
30418         });
30419     },
30420
30421     anchors : {
30422         "west" : "left",
30423         "east" : "right",
30424         "north" : "top",
30425         "south" : "bottom"
30426     },
30427
30428     sanchors : {
30429         "west" : "l",
30430         "east" : "r",
30431         "north" : "t",
30432         "south" : "b"
30433     },
30434
30435     canchors : {
30436         "west" : "tl-tr",
30437         "east" : "tr-tl",
30438         "north" : "tl-bl",
30439         "south" : "bl-tl"
30440     },
30441
30442     getAnchor : function(){
30443         return this.anchors[this.position];
30444     },
30445
30446     getCollapseAnchor : function(){
30447         return this.canchors[this.position];
30448     },
30449
30450     getSlideAnchor : function(){
30451         return this.sanchors[this.position];
30452     },
30453
30454     getAlignAdj : function(){
30455         var cm = this.cmargins;
30456         switch(this.position){
30457             case "west":
30458                 return [0, 0];
30459             break;
30460             case "east":
30461                 return [0, 0];
30462             break;
30463             case "north":
30464                 return [0, 0];
30465             break;
30466             case "south":
30467                 return [0, 0];
30468             break;
30469         }
30470     },
30471
30472     getExpandAdj : function(){
30473         var c = this.collapsedEl, cm = this.cmargins;
30474         switch(this.position){
30475             case "west":
30476                 return [-(cm.right+c.getWidth()+cm.left), 0];
30477             break;
30478             case "east":
30479                 return [cm.right+c.getWidth()+cm.left, 0];
30480             break;
30481             case "north":
30482                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30483             break;
30484             case "south":
30485                 return [0, cm.top+cm.bottom+c.getHeight()];
30486             break;
30487         }
30488     }
30489 });/*
30490  * Based on:
30491  * Ext JS Library 1.1.1
30492  * Copyright(c) 2006-2007, Ext JS, LLC.
30493  *
30494  * Originally Released Under LGPL - original licence link has changed is not relivant.
30495  *
30496  * Fork - LGPL
30497  * <script type="text/javascript">
30498  */
30499 /*
30500  * These classes are private internal classes
30501  */
30502 Roo.CenterLayoutRegion = function(mgr, config){
30503     Roo.LayoutRegion.call(this, mgr, config, "center");
30504     this.visible = true;
30505     this.minWidth = config.minWidth || 20;
30506     this.minHeight = config.minHeight || 20;
30507 };
30508
30509 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30510     hide : function(){
30511         // center panel can't be hidden
30512     },
30513     
30514     show : function(){
30515         // center panel can't be hidden
30516     },
30517     
30518     getMinWidth: function(){
30519         return this.minWidth;
30520     },
30521     
30522     getMinHeight: function(){
30523         return this.minHeight;
30524     }
30525 });
30526
30527
30528 Roo.NorthLayoutRegion = function(mgr, config){
30529     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30530     if(this.split){
30531         this.split.placement = Roo.SplitBar.TOP;
30532         this.split.orientation = Roo.SplitBar.VERTICAL;
30533         this.split.el.addClass("x-layout-split-v");
30534     }
30535     var size = config.initialSize || config.height;
30536     if(typeof size != "undefined"){
30537         this.el.setHeight(size);
30538     }
30539 };
30540 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30541     orientation: Roo.SplitBar.VERTICAL,
30542     getBox : function(){
30543         if(this.collapsed){
30544             return this.collapsedEl.getBox();
30545         }
30546         var box = this.el.getBox();
30547         if(this.split){
30548             box.height += this.split.el.getHeight();
30549         }
30550         return box;
30551     },
30552     
30553     updateBox : function(box){
30554         if(this.split && !this.collapsed){
30555             box.height -= this.split.el.getHeight();
30556             this.split.el.setLeft(box.x);
30557             this.split.el.setTop(box.y+box.height);
30558             this.split.el.setWidth(box.width);
30559         }
30560         if(this.collapsed){
30561             this.updateBody(box.width, null);
30562         }
30563         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30564     }
30565 });
30566
30567 Roo.SouthLayoutRegion = function(mgr, config){
30568     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30569     if(this.split){
30570         this.split.placement = Roo.SplitBar.BOTTOM;
30571         this.split.orientation = Roo.SplitBar.VERTICAL;
30572         this.split.el.addClass("x-layout-split-v");
30573     }
30574     var size = config.initialSize || config.height;
30575     if(typeof size != "undefined"){
30576         this.el.setHeight(size);
30577     }
30578 };
30579 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30580     orientation: Roo.SplitBar.VERTICAL,
30581     getBox : function(){
30582         if(this.collapsed){
30583             return this.collapsedEl.getBox();
30584         }
30585         var box = this.el.getBox();
30586         if(this.split){
30587             var sh = this.split.el.getHeight();
30588             box.height += sh;
30589             box.y -= sh;
30590         }
30591         return box;
30592     },
30593     
30594     updateBox : function(box){
30595         if(this.split && !this.collapsed){
30596             var sh = this.split.el.getHeight();
30597             box.height -= sh;
30598             box.y += sh;
30599             this.split.el.setLeft(box.x);
30600             this.split.el.setTop(box.y-sh);
30601             this.split.el.setWidth(box.width);
30602         }
30603         if(this.collapsed){
30604             this.updateBody(box.width, null);
30605         }
30606         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30607     }
30608 });
30609
30610 Roo.EastLayoutRegion = function(mgr, config){
30611     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30612     if(this.split){
30613         this.split.placement = Roo.SplitBar.RIGHT;
30614         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30615         this.split.el.addClass("x-layout-split-h");
30616     }
30617     var size = config.initialSize || config.width;
30618     if(typeof size != "undefined"){
30619         this.el.setWidth(size);
30620     }
30621 };
30622 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30623     orientation: Roo.SplitBar.HORIZONTAL,
30624     getBox : function(){
30625         if(this.collapsed){
30626             return this.collapsedEl.getBox();
30627         }
30628         var box = this.el.getBox();
30629         if(this.split){
30630             var sw = this.split.el.getWidth();
30631             box.width += sw;
30632             box.x -= sw;
30633         }
30634         return box;
30635     },
30636
30637     updateBox : function(box){
30638         if(this.split && !this.collapsed){
30639             var sw = this.split.el.getWidth();
30640             box.width -= sw;
30641             this.split.el.setLeft(box.x);
30642             this.split.el.setTop(box.y);
30643             this.split.el.setHeight(box.height);
30644             box.x += sw;
30645         }
30646         if(this.collapsed){
30647             this.updateBody(null, box.height);
30648         }
30649         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30650     }
30651 });
30652
30653 Roo.WestLayoutRegion = function(mgr, config){
30654     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30655     if(this.split){
30656         this.split.placement = Roo.SplitBar.LEFT;
30657         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30658         this.split.el.addClass("x-layout-split-h");
30659     }
30660     var size = config.initialSize || config.width;
30661     if(typeof size != "undefined"){
30662         this.el.setWidth(size);
30663     }
30664 };
30665 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30666     orientation: Roo.SplitBar.HORIZONTAL,
30667     getBox : function(){
30668         if(this.collapsed){
30669             return this.collapsedEl.getBox();
30670         }
30671         var box = this.el.getBox();
30672         if(this.split){
30673             box.width += this.split.el.getWidth();
30674         }
30675         return box;
30676     },
30677     
30678     updateBox : function(box){
30679         if(this.split && !this.collapsed){
30680             var sw = this.split.el.getWidth();
30681             box.width -= sw;
30682             this.split.el.setLeft(box.x+box.width);
30683             this.split.el.setTop(box.y);
30684             this.split.el.setHeight(box.height);
30685         }
30686         if(this.collapsed){
30687             this.updateBody(null, box.height);
30688         }
30689         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30690     }
30691 });
30692 /*
30693  * Based on:
30694  * Ext JS Library 1.1.1
30695  * Copyright(c) 2006-2007, Ext JS, LLC.
30696  *
30697  * Originally Released Under LGPL - original licence link has changed is not relivant.
30698  *
30699  * Fork - LGPL
30700  * <script type="text/javascript">
30701  */
30702  
30703  
30704 /*
30705  * Private internal class for reading and applying state
30706  */
30707 Roo.LayoutStateManager = function(layout){
30708      // default empty state
30709      this.state = {
30710         north: {},
30711         south: {},
30712         east: {},
30713         west: {}       
30714     };
30715 };
30716
30717 Roo.LayoutStateManager.prototype = {
30718     init : function(layout, provider){
30719         this.provider = provider;
30720         var state = provider.get(layout.id+"-layout-state");
30721         if(state){
30722             var wasUpdating = layout.isUpdating();
30723             if(!wasUpdating){
30724                 layout.beginUpdate();
30725             }
30726             for(var key in state){
30727                 if(typeof state[key] != "function"){
30728                     var rstate = state[key];
30729                     var r = layout.getRegion(key);
30730                     if(r && rstate){
30731                         if(rstate.size){
30732                             r.resizeTo(rstate.size);
30733                         }
30734                         if(rstate.collapsed == true){
30735                             r.collapse(true);
30736                         }else{
30737                             r.expand(null, true);
30738                         }
30739                     }
30740                 }
30741             }
30742             if(!wasUpdating){
30743                 layout.endUpdate();
30744             }
30745             this.state = state; 
30746         }
30747         this.layout = layout;
30748         layout.on("regionresized", this.onRegionResized, this);
30749         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30750         layout.on("regionexpanded", this.onRegionExpanded, this);
30751     },
30752     
30753     storeState : function(){
30754         this.provider.set(this.layout.id+"-layout-state", this.state);
30755     },
30756     
30757     onRegionResized : function(region, newSize){
30758         this.state[region.getPosition()].size = newSize;
30759         this.storeState();
30760     },
30761     
30762     onRegionCollapsed : function(region){
30763         this.state[region.getPosition()].collapsed = true;
30764         this.storeState();
30765     },
30766     
30767     onRegionExpanded : function(region){
30768         this.state[region.getPosition()].collapsed = false;
30769         this.storeState();
30770     }
30771 };/*
30772  * Based on:
30773  * Ext JS Library 1.1.1
30774  * Copyright(c) 2006-2007, Ext JS, LLC.
30775  *
30776  * Originally Released Under LGPL - original licence link has changed is not relivant.
30777  *
30778  * Fork - LGPL
30779  * <script type="text/javascript">
30780  */
30781 /**
30782  * @class Roo.ContentPanel
30783  * @extends Roo.util.Observable
30784  * A basic ContentPanel element.
30785  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30786  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30787  * @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
30788  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30789  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30790  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30791  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30792  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30793  * @cfg {String} title          The title for this panel
30794  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30795  * @cfg {String} url            Calls {@link #setUrl} with this value
30796  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30797  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30798  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30799  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30800
30801  * @constructor
30802  * Create a new ContentPanel.
30803  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30804  * @param {String/Object} config A string to set only the title or a config object
30805  * @param {String} content (optional) Set the HTML content for this panel
30806  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30807  */
30808 Roo.ContentPanel = function(el, config, content){
30809     
30810      
30811     /*
30812     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30813         config = el;
30814         el = Roo.id();
30815     }
30816     if (config && config.parentLayout) { 
30817         el = config.parentLayout.el.createChild(); 
30818     }
30819     */
30820     if(el.autoCreate){ // xtype is available if this is called from factory
30821         config = el;
30822         el = Roo.id();
30823     }
30824     this.el = Roo.get(el);
30825     if(!this.el && config && config.autoCreate){
30826         if(typeof config.autoCreate == "object"){
30827             if(!config.autoCreate.id){
30828                 config.autoCreate.id = config.id||el;
30829             }
30830             this.el = Roo.DomHelper.append(document.body,
30831                         config.autoCreate, true);
30832         }else{
30833             this.el = Roo.DomHelper.append(document.body,
30834                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30835         }
30836     }
30837     this.closable = false;
30838     this.loaded = false;
30839     this.active = false;
30840     if(typeof config == "string"){
30841         this.title = config;
30842     }else{
30843         Roo.apply(this, config);
30844     }
30845     
30846     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30847         this.wrapEl = this.el.wrap();
30848         this.toolbar.container = this.el.insertSibling(false, 'before');
30849         this.toolbar = new Roo.Toolbar(this.toolbar);
30850     }
30851     
30852     // xtype created footer. - not sure if will work as we normally have to render first..
30853     if (this.footer && !this.footer.el && this.footer.xtype) {
30854         if (!this.wrapEl) {
30855             this.wrapEl = this.el.wrap();
30856         }
30857     
30858         this.footer.container = this.wrapEl.createChild();
30859          
30860         this.footer = Roo.factory(this.footer, Roo);
30861         
30862     }
30863     
30864     if(this.resizeEl){
30865         this.resizeEl = Roo.get(this.resizeEl, true);
30866     }else{
30867         this.resizeEl = this.el;
30868     }
30869     // handle view.xtype
30870     
30871  
30872     
30873     
30874     this.addEvents({
30875         /**
30876          * @event activate
30877          * Fires when this panel is activated. 
30878          * @param {Roo.ContentPanel} this
30879          */
30880         "activate" : true,
30881         /**
30882          * @event deactivate
30883          * Fires when this panel is activated. 
30884          * @param {Roo.ContentPanel} this
30885          */
30886         "deactivate" : true,
30887
30888         /**
30889          * @event resize
30890          * Fires when this panel is resized if fitToFrame is true.
30891          * @param {Roo.ContentPanel} this
30892          * @param {Number} width The width after any component adjustments
30893          * @param {Number} height The height after any component adjustments
30894          */
30895         "resize" : true,
30896         
30897          /**
30898          * @event render
30899          * Fires when this tab is created
30900          * @param {Roo.ContentPanel} this
30901          */
30902         "render" : true
30903         
30904         
30905         
30906     });
30907     
30908
30909     
30910     
30911     if(this.autoScroll){
30912         this.resizeEl.setStyle("overflow", "auto");
30913     } else {
30914         // fix randome scrolling
30915         this.el.on('scroll', function() {
30916             Roo.log('fix random scolling');
30917             this.scrollTo('top',0); 
30918         });
30919     }
30920     content = content || this.content;
30921     if(content){
30922         this.setContent(content);
30923     }
30924     if(config && config.url){
30925         this.setUrl(this.url, this.params, this.loadOnce);
30926     }
30927     
30928     
30929     
30930     Roo.ContentPanel.superclass.constructor.call(this);
30931     
30932     if (this.view && typeof(this.view.xtype) != 'undefined') {
30933         this.view.el = this.el.appendChild(document.createElement("div"));
30934         this.view = Roo.factory(this.view); 
30935         this.view.render  &&  this.view.render(false, '');  
30936     }
30937     
30938     
30939     this.fireEvent('render', this);
30940 };
30941
30942 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30943     tabTip:'',
30944     setRegion : function(region){
30945         this.region = region;
30946         if(region){
30947            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30948         }else{
30949            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30950         } 
30951     },
30952     
30953     /**
30954      * Returns the toolbar for this Panel if one was configured. 
30955      * @return {Roo.Toolbar} 
30956      */
30957     getToolbar : function(){
30958         return this.toolbar;
30959     },
30960     
30961     setActiveState : function(active){
30962         this.active = active;
30963         if(!active){
30964             this.fireEvent("deactivate", this);
30965         }else{
30966             this.fireEvent("activate", this);
30967         }
30968     },
30969     /**
30970      * Updates this panel's element
30971      * @param {String} content The new content
30972      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30973     */
30974     setContent : function(content, loadScripts){
30975         this.el.update(content, loadScripts);
30976     },
30977
30978     ignoreResize : function(w, h){
30979         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30980             return true;
30981         }else{
30982             this.lastSize = {width: w, height: h};
30983             return false;
30984         }
30985     },
30986     /**
30987      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30988      * @return {Roo.UpdateManager} The UpdateManager
30989      */
30990     getUpdateManager : function(){
30991         return this.el.getUpdateManager();
30992     },
30993      /**
30994      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30995      * @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:
30996 <pre><code>
30997 panel.load({
30998     url: "your-url.php",
30999     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31000     callback: yourFunction,
31001     scope: yourObject, //(optional scope)
31002     discardUrl: false,
31003     nocache: false,
31004     text: "Loading...",
31005     timeout: 30,
31006     scripts: false
31007 });
31008 </code></pre>
31009      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31010      * 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.
31011      * @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}
31012      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31013      * @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.
31014      * @return {Roo.ContentPanel} this
31015      */
31016     load : function(){
31017         var um = this.el.getUpdateManager();
31018         um.update.apply(um, arguments);
31019         return this;
31020     },
31021
31022
31023     /**
31024      * 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.
31025      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31026      * @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)
31027      * @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)
31028      * @return {Roo.UpdateManager} The UpdateManager
31029      */
31030     setUrl : function(url, params, loadOnce){
31031         if(this.refreshDelegate){
31032             this.removeListener("activate", this.refreshDelegate);
31033         }
31034         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31035         this.on("activate", this.refreshDelegate);
31036         return this.el.getUpdateManager();
31037     },
31038     
31039     _handleRefresh : function(url, params, loadOnce){
31040         if(!loadOnce || !this.loaded){
31041             var updater = this.el.getUpdateManager();
31042             updater.update(url, params, this._setLoaded.createDelegate(this));
31043         }
31044     },
31045     
31046     _setLoaded : function(){
31047         this.loaded = true;
31048     }, 
31049     
31050     /**
31051      * Returns this panel's id
31052      * @return {String} 
31053      */
31054     getId : function(){
31055         return this.el.id;
31056     },
31057     
31058     /** 
31059      * Returns this panel's element - used by regiosn to add.
31060      * @return {Roo.Element} 
31061      */
31062     getEl : function(){
31063         return this.wrapEl || this.el;
31064     },
31065     
31066     adjustForComponents : function(width, height)
31067     {
31068         //Roo.log('adjustForComponents ');
31069         if(this.resizeEl != this.el){
31070             width -= this.el.getFrameWidth('lr');
31071             height -= this.el.getFrameWidth('tb');
31072         }
31073         if(this.toolbar){
31074             var te = this.toolbar.getEl();
31075             height -= te.getHeight();
31076             te.setWidth(width);
31077         }
31078         if(this.footer){
31079             var te = this.footer.getEl();
31080             Roo.log("footer:" + te.getHeight());
31081             
31082             height -= te.getHeight();
31083             te.setWidth(width);
31084         }
31085         
31086         
31087         if(this.adjustments){
31088             width += this.adjustments[0];
31089             height += this.adjustments[1];
31090         }
31091         return {"width": width, "height": height};
31092     },
31093     
31094     setSize : function(width, height){
31095         if(this.fitToFrame && !this.ignoreResize(width, height)){
31096             if(this.fitContainer && this.resizeEl != this.el){
31097                 this.el.setSize(width, height);
31098             }
31099             var size = this.adjustForComponents(width, height);
31100             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31101             this.fireEvent('resize', this, size.width, size.height);
31102         }
31103     },
31104     
31105     /**
31106      * Returns this panel's title
31107      * @return {String} 
31108      */
31109     getTitle : function(){
31110         return this.title;
31111     },
31112     
31113     /**
31114      * Set this panel's title
31115      * @param {String} title
31116      */
31117     setTitle : function(title){
31118         this.title = title;
31119         if(this.region){
31120             this.region.updatePanelTitle(this, title);
31121         }
31122     },
31123     
31124     /**
31125      * Returns true is this panel was configured to be closable
31126      * @return {Boolean} 
31127      */
31128     isClosable : function(){
31129         return this.closable;
31130     },
31131     
31132     beforeSlide : function(){
31133         this.el.clip();
31134         this.resizeEl.clip();
31135     },
31136     
31137     afterSlide : function(){
31138         this.el.unclip();
31139         this.resizeEl.unclip();
31140     },
31141     
31142     /**
31143      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31144      *   Will fail silently if the {@link #setUrl} method has not been called.
31145      *   This does not activate the panel, just updates its content.
31146      */
31147     refresh : function(){
31148         if(this.refreshDelegate){
31149            this.loaded = false;
31150            this.refreshDelegate();
31151         }
31152     },
31153     
31154     /**
31155      * Destroys this panel
31156      */
31157     destroy : function(){
31158         this.el.removeAllListeners();
31159         var tempEl = document.createElement("span");
31160         tempEl.appendChild(this.el.dom);
31161         tempEl.innerHTML = "";
31162         this.el.remove();
31163         this.el = null;
31164     },
31165     
31166     /**
31167      * form - if the content panel contains a form - this is a reference to it.
31168      * @type {Roo.form.Form}
31169      */
31170     form : false,
31171     /**
31172      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31173      *    This contains a reference to it.
31174      * @type {Roo.View}
31175      */
31176     view : false,
31177     
31178       /**
31179      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31180      * <pre><code>
31181
31182 layout.addxtype({
31183        xtype : 'Form',
31184        items: [ .... ]
31185    }
31186 );
31187
31188 </code></pre>
31189      * @param {Object} cfg Xtype definition of item to add.
31190      */
31191     
31192     addxtype : function(cfg) {
31193         // add form..
31194         if (cfg.xtype.match(/^Form$/)) {
31195             
31196             var el;
31197             //if (this.footer) {
31198             //    el = this.footer.container.insertSibling(false, 'before');
31199             //} else {
31200                 el = this.el.createChild();
31201             //}
31202
31203             this.form = new  Roo.form.Form(cfg);
31204             
31205             
31206             if ( this.form.allItems.length) {
31207                 this.form.render(el.dom);
31208             }
31209             return this.form;
31210         }
31211         // should only have one of theses..
31212         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31213             // views.. should not be just added - used named prop 'view''
31214             
31215             cfg.el = this.el.appendChild(document.createElement("div"));
31216             // factory?
31217             
31218             var ret = new Roo.factory(cfg);
31219              
31220              ret.render && ret.render(false, ''); // render blank..
31221             this.view = ret;
31222             return ret;
31223         }
31224         return false;
31225     }
31226 });
31227
31228 /**
31229  * @class Roo.GridPanel
31230  * @extends Roo.ContentPanel
31231  * @constructor
31232  * Create a new GridPanel.
31233  * @param {Roo.grid.Grid} grid The grid for this panel
31234  * @param {String/Object} config A string to set only the panel's title, or a config object
31235  */
31236 Roo.GridPanel = function(grid, config){
31237     
31238   
31239     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31240         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31241         
31242     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31243     
31244     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31245     
31246     if(this.toolbar){
31247         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31248     }
31249     // xtype created footer. - not sure if will work as we normally have to render first..
31250     if (this.footer && !this.footer.el && this.footer.xtype) {
31251         
31252         this.footer.container = this.grid.getView().getFooterPanel(true);
31253         this.footer.dataSource = this.grid.dataSource;
31254         this.footer = Roo.factory(this.footer, Roo);
31255         
31256     }
31257     
31258     grid.monitorWindowResize = false; // turn off autosizing
31259     grid.autoHeight = false;
31260     grid.autoWidth = false;
31261     this.grid = grid;
31262     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31263 };
31264
31265 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31266     getId : function(){
31267         return this.grid.id;
31268     },
31269     
31270     /**
31271      * Returns the grid for this panel
31272      * @return {Roo.grid.Grid} 
31273      */
31274     getGrid : function(){
31275         return this.grid;    
31276     },
31277     
31278     setSize : function(width, height){
31279         if(!this.ignoreResize(width, height)){
31280             var grid = this.grid;
31281             var size = this.adjustForComponents(width, height);
31282             grid.getGridEl().setSize(size.width, size.height);
31283             grid.autoSize();
31284         }
31285     },
31286     
31287     beforeSlide : function(){
31288         this.grid.getView().scroller.clip();
31289     },
31290     
31291     afterSlide : function(){
31292         this.grid.getView().scroller.unclip();
31293     },
31294     
31295     destroy : function(){
31296         this.grid.destroy();
31297         delete this.grid;
31298         Roo.GridPanel.superclass.destroy.call(this); 
31299     }
31300 });
31301
31302
31303 /**
31304  * @class Roo.NestedLayoutPanel
31305  * @extends Roo.ContentPanel
31306  * @constructor
31307  * Create a new NestedLayoutPanel.
31308  * 
31309  * 
31310  * @param {Roo.BorderLayout} layout The layout for this panel
31311  * @param {String/Object} config A string to set only the title or a config object
31312  */
31313 Roo.NestedLayoutPanel = function(layout, config)
31314 {
31315     // construct with only one argument..
31316     /* FIXME - implement nicer consturctors
31317     if (layout.layout) {
31318         config = layout;
31319         layout = config.layout;
31320         delete config.layout;
31321     }
31322     if (layout.xtype && !layout.getEl) {
31323         // then layout needs constructing..
31324         layout = Roo.factory(layout, Roo);
31325     }
31326     */
31327     
31328     
31329     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31330     
31331     layout.monitorWindowResize = false; // turn off autosizing
31332     this.layout = layout;
31333     this.layout.getEl().addClass("x-layout-nested-layout");
31334     
31335     
31336     
31337     
31338 };
31339
31340 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31341
31342     setSize : function(width, height){
31343         if(!this.ignoreResize(width, height)){
31344             var size = this.adjustForComponents(width, height);
31345             var el = this.layout.getEl();
31346             el.setSize(size.width, size.height);
31347             var touch = el.dom.offsetWidth;
31348             this.layout.layout();
31349             // ie requires a double layout on the first pass
31350             if(Roo.isIE && !this.initialized){
31351                 this.initialized = true;
31352                 this.layout.layout();
31353             }
31354         }
31355     },
31356     
31357     // activate all subpanels if not currently active..
31358     
31359     setActiveState : function(active){
31360         this.active = active;
31361         if(!active){
31362             this.fireEvent("deactivate", this);
31363             return;
31364         }
31365         
31366         this.fireEvent("activate", this);
31367         // not sure if this should happen before or after..
31368         if (!this.layout) {
31369             return; // should not happen..
31370         }
31371         var reg = false;
31372         for (var r in this.layout.regions) {
31373             reg = this.layout.getRegion(r);
31374             if (reg.getActivePanel()) {
31375                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31376                 reg.setActivePanel(reg.getActivePanel());
31377                 continue;
31378             }
31379             if (!reg.panels.length) {
31380                 continue;
31381             }
31382             reg.showPanel(reg.getPanel(0));
31383         }
31384         
31385         
31386         
31387         
31388     },
31389     
31390     /**
31391      * Returns the nested BorderLayout for this panel
31392      * @return {Roo.BorderLayout} 
31393      */
31394     getLayout : function(){
31395         return this.layout;
31396     },
31397     
31398      /**
31399      * Adds a xtype elements to the layout of the nested panel
31400      * <pre><code>
31401
31402 panel.addxtype({
31403        xtype : 'ContentPanel',
31404        region: 'west',
31405        items: [ .... ]
31406    }
31407 );
31408
31409 panel.addxtype({
31410         xtype : 'NestedLayoutPanel',
31411         region: 'west',
31412         layout: {
31413            center: { },
31414            west: { }   
31415         },
31416         items : [ ... list of content panels or nested layout panels.. ]
31417    }
31418 );
31419 </code></pre>
31420      * @param {Object} cfg Xtype definition of item to add.
31421      */
31422     addxtype : function(cfg) {
31423         return this.layout.addxtype(cfg);
31424     
31425     }
31426 });
31427
31428 Roo.ScrollPanel = function(el, config, content){
31429     config = config || {};
31430     config.fitToFrame = true;
31431     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31432     
31433     this.el.dom.style.overflow = "hidden";
31434     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31435     this.el.removeClass("x-layout-inactive-content");
31436     this.el.on("mousewheel", this.onWheel, this);
31437
31438     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31439     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31440     up.unselectable(); down.unselectable();
31441     up.on("click", this.scrollUp, this);
31442     down.on("click", this.scrollDown, this);
31443     up.addClassOnOver("x-scroller-btn-over");
31444     down.addClassOnOver("x-scroller-btn-over");
31445     up.addClassOnClick("x-scroller-btn-click");
31446     down.addClassOnClick("x-scroller-btn-click");
31447     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31448
31449     this.resizeEl = this.el;
31450     this.el = wrap; this.up = up; this.down = down;
31451 };
31452
31453 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31454     increment : 100,
31455     wheelIncrement : 5,
31456     scrollUp : function(){
31457         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31458     },
31459
31460     scrollDown : function(){
31461         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31462     },
31463
31464     afterScroll : function(){
31465         var el = this.resizeEl;
31466         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31467         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31468         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31469     },
31470
31471     setSize : function(){
31472         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31473         this.afterScroll();
31474     },
31475
31476     onWheel : function(e){
31477         var d = e.getWheelDelta();
31478         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31479         this.afterScroll();
31480         e.stopEvent();
31481     },
31482
31483     setContent : function(content, loadScripts){
31484         this.resizeEl.update(content, loadScripts);
31485     }
31486
31487 });
31488
31489
31490
31491
31492
31493
31494
31495
31496
31497 /**
31498  * @class Roo.TreePanel
31499  * @extends Roo.ContentPanel
31500  * @constructor
31501  * Create a new TreePanel. - defaults to fit/scoll contents.
31502  * @param {String/Object} config A string to set only the panel's title, or a config object
31503  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31504  */
31505 Roo.TreePanel = function(config){
31506     var el = config.el;
31507     var tree = config.tree;
31508     delete config.tree; 
31509     delete config.el; // hopefull!
31510     
31511     // wrapper for IE7 strict & safari scroll issue
31512     
31513     var treeEl = el.createChild();
31514     config.resizeEl = treeEl;
31515     
31516     
31517     
31518     Roo.TreePanel.superclass.constructor.call(this, el, config);
31519  
31520  
31521     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31522     //console.log(tree);
31523     this.on('activate', function()
31524     {
31525         if (this.tree.rendered) {
31526             return;
31527         }
31528         //console.log('render tree');
31529         this.tree.render();
31530     });
31531     // this should not be needed.. - it's actually the 'el' that resizes?
31532     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31533     
31534     //this.on('resize',  function (cp, w, h) {
31535     //        this.tree.innerCt.setWidth(w);
31536     //        this.tree.innerCt.setHeight(h);
31537     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31538     //});
31539
31540         
31541     
31542 };
31543
31544 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31545     fitToFrame : true,
31546     autoScroll : true
31547 });
31548
31549
31550
31551
31552
31553
31554
31555
31556
31557
31558
31559 /*
31560  * Based on:
31561  * Ext JS Library 1.1.1
31562  * Copyright(c) 2006-2007, Ext JS, LLC.
31563  *
31564  * Originally Released Under LGPL - original licence link has changed is not relivant.
31565  *
31566  * Fork - LGPL
31567  * <script type="text/javascript">
31568  */
31569  
31570
31571 /**
31572  * @class Roo.ReaderLayout
31573  * @extends Roo.BorderLayout
31574  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31575  * center region containing two nested regions (a top one for a list view and one for item preview below),
31576  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31577  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31578  * expedites the setup of the overall layout and regions for this common application style.
31579  * Example:
31580  <pre><code>
31581 var reader = new Roo.ReaderLayout();
31582 var CP = Roo.ContentPanel;  // shortcut for adding
31583
31584 reader.beginUpdate();
31585 reader.add("north", new CP("north", "North"));
31586 reader.add("west", new CP("west", {title: "West"}));
31587 reader.add("east", new CP("east", {title: "East"}));
31588
31589 reader.regions.listView.add(new CP("listView", "List"));
31590 reader.regions.preview.add(new CP("preview", "Preview"));
31591 reader.endUpdate();
31592 </code></pre>
31593 * @constructor
31594 * Create a new ReaderLayout
31595 * @param {Object} config Configuration options
31596 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31597 * document.body if omitted)
31598 */
31599 Roo.ReaderLayout = function(config, renderTo){
31600     var c = config || {size:{}};
31601     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31602         north: c.north !== false ? Roo.apply({
31603             split:false,
31604             initialSize: 32,
31605             titlebar: false
31606         }, c.north) : false,
31607         west: c.west !== false ? Roo.apply({
31608             split:true,
31609             initialSize: 200,
31610             minSize: 175,
31611             maxSize: 400,
31612             titlebar: true,
31613             collapsible: true,
31614             animate: true,
31615             margins:{left:5,right:0,bottom:5,top:5},
31616             cmargins:{left:5,right:5,bottom:5,top:5}
31617         }, c.west) : false,
31618         east: c.east !== false ? Roo.apply({
31619             split:true,
31620             initialSize: 200,
31621             minSize: 175,
31622             maxSize: 400,
31623             titlebar: true,
31624             collapsible: true,
31625             animate: true,
31626             margins:{left:0,right:5,bottom:5,top:5},
31627             cmargins:{left:5,right:5,bottom:5,top:5}
31628         }, c.east) : false,
31629         center: Roo.apply({
31630             tabPosition: 'top',
31631             autoScroll:false,
31632             closeOnTab: true,
31633             titlebar:false,
31634             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31635         }, c.center)
31636     });
31637
31638     this.el.addClass('x-reader');
31639
31640     this.beginUpdate();
31641
31642     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31643         south: c.preview !== false ? Roo.apply({
31644             split:true,
31645             initialSize: 200,
31646             minSize: 100,
31647             autoScroll:true,
31648             collapsible:true,
31649             titlebar: true,
31650             cmargins:{top:5,left:0, right:0, bottom:0}
31651         }, c.preview) : false,
31652         center: Roo.apply({
31653             autoScroll:false,
31654             titlebar:false,
31655             minHeight:200
31656         }, c.listView)
31657     });
31658     this.add('center', new Roo.NestedLayoutPanel(inner,
31659             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31660
31661     this.endUpdate();
31662
31663     this.regions.preview = inner.getRegion('south');
31664     this.regions.listView = inner.getRegion('center');
31665 };
31666
31667 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31668  * Based on:
31669  * Ext JS Library 1.1.1
31670  * Copyright(c) 2006-2007, Ext JS, LLC.
31671  *
31672  * Originally Released Under LGPL - original licence link has changed is not relivant.
31673  *
31674  * Fork - LGPL
31675  * <script type="text/javascript">
31676  */
31677  
31678 /**
31679  * @class Roo.grid.Grid
31680  * @extends Roo.util.Observable
31681  * This class represents the primary interface of a component based grid control.
31682  * <br><br>Usage:<pre><code>
31683  var grid = new Roo.grid.Grid("my-container-id", {
31684      ds: myDataStore,
31685      cm: myColModel,
31686      selModel: mySelectionModel,
31687      autoSizeColumns: true,
31688      monitorWindowResize: false,
31689      trackMouseOver: true
31690  });
31691  // set any options
31692  grid.render();
31693  * </code></pre>
31694  * <b>Common Problems:</b><br/>
31695  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31696  * element will correct this<br/>
31697  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31698  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31699  * are unpredictable.<br/>
31700  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31701  * grid to calculate dimensions/offsets.<br/>
31702   * @constructor
31703  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31704  * The container MUST have some type of size defined for the grid to fill. The container will be
31705  * automatically set to position relative if it isn't already.
31706  * @param {Object} config A config object that sets properties on this grid.
31707  */
31708 Roo.grid.Grid = function(container, config){
31709         // initialize the container
31710         this.container = Roo.get(container);
31711         this.container.update("");
31712         this.container.setStyle("overflow", "hidden");
31713     this.container.addClass('x-grid-container');
31714
31715     this.id = this.container.id;
31716
31717     Roo.apply(this, config);
31718     // check and correct shorthanded configs
31719     if(this.ds){
31720         this.dataSource = this.ds;
31721         delete this.ds;
31722     }
31723     if(this.cm){
31724         this.colModel = this.cm;
31725         delete this.cm;
31726     }
31727     if(this.sm){
31728         this.selModel = this.sm;
31729         delete this.sm;
31730     }
31731
31732     if (this.selModel) {
31733         this.selModel = Roo.factory(this.selModel, Roo.grid);
31734         this.sm = this.selModel;
31735         this.sm.xmodule = this.xmodule || false;
31736     }
31737     if (typeof(this.colModel.config) == 'undefined') {
31738         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31739         this.cm = this.colModel;
31740         this.cm.xmodule = this.xmodule || false;
31741     }
31742     if (this.dataSource) {
31743         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31744         this.ds = this.dataSource;
31745         this.ds.xmodule = this.xmodule || false;
31746          
31747     }
31748     
31749     
31750     
31751     if(this.width){
31752         this.container.setWidth(this.width);
31753     }
31754
31755     if(this.height){
31756         this.container.setHeight(this.height);
31757     }
31758     /** @private */
31759         this.addEvents({
31760         // raw events
31761         /**
31762          * @event click
31763          * The raw click event for the entire grid.
31764          * @param {Roo.EventObject} e
31765          */
31766         "click" : true,
31767         /**
31768          * @event dblclick
31769          * The raw dblclick event for the entire grid.
31770          * @param {Roo.EventObject} e
31771          */
31772         "dblclick" : true,
31773         /**
31774          * @event contextmenu
31775          * The raw contextmenu event for the entire grid.
31776          * @param {Roo.EventObject} e
31777          */
31778         "contextmenu" : true,
31779         /**
31780          * @event mousedown
31781          * The raw mousedown event for the entire grid.
31782          * @param {Roo.EventObject} e
31783          */
31784         "mousedown" : true,
31785         /**
31786          * @event mouseup
31787          * The raw mouseup event for the entire grid.
31788          * @param {Roo.EventObject} e
31789          */
31790         "mouseup" : true,
31791         /**
31792          * @event mouseover
31793          * The raw mouseover event for the entire grid.
31794          * @param {Roo.EventObject} e
31795          */
31796         "mouseover" : true,
31797         /**
31798          * @event mouseout
31799          * The raw mouseout event for the entire grid.
31800          * @param {Roo.EventObject} e
31801          */
31802         "mouseout" : true,
31803         /**
31804          * @event keypress
31805          * The raw keypress event for the entire grid.
31806          * @param {Roo.EventObject} e
31807          */
31808         "keypress" : true,
31809         /**
31810          * @event keydown
31811          * The raw keydown event for the entire grid.
31812          * @param {Roo.EventObject} e
31813          */
31814         "keydown" : true,
31815
31816         // custom events
31817
31818         /**
31819          * @event cellclick
31820          * Fires when a cell is clicked
31821          * @param {Grid} this
31822          * @param {Number} rowIndex
31823          * @param {Number} columnIndex
31824          * @param {Roo.EventObject} e
31825          */
31826         "cellclick" : true,
31827         /**
31828          * @event celldblclick
31829          * Fires when a cell is double clicked
31830          * @param {Grid} this
31831          * @param {Number} rowIndex
31832          * @param {Number} columnIndex
31833          * @param {Roo.EventObject} e
31834          */
31835         "celldblclick" : true,
31836         /**
31837          * @event rowclick
31838          * Fires when a row is clicked
31839          * @param {Grid} this
31840          * @param {Number} rowIndex
31841          * @param {Roo.EventObject} e
31842          */
31843         "rowclick" : true,
31844         /**
31845          * @event rowdblclick
31846          * Fires when a row is double clicked
31847          * @param {Grid} this
31848          * @param {Number} rowIndex
31849          * @param {Roo.EventObject} e
31850          */
31851         "rowdblclick" : true,
31852         /**
31853          * @event headerclick
31854          * Fires when a header is clicked
31855          * @param {Grid} this
31856          * @param {Number} columnIndex
31857          * @param {Roo.EventObject} e
31858          */
31859         "headerclick" : true,
31860         /**
31861          * @event headerdblclick
31862          * Fires when a header cell is double clicked
31863          * @param {Grid} this
31864          * @param {Number} columnIndex
31865          * @param {Roo.EventObject} e
31866          */
31867         "headerdblclick" : true,
31868         /**
31869          * @event rowcontextmenu
31870          * Fires when a row is right clicked
31871          * @param {Grid} this
31872          * @param {Number} rowIndex
31873          * @param {Roo.EventObject} e
31874          */
31875         "rowcontextmenu" : true,
31876         /**
31877          * @event cellcontextmenu
31878          * Fires when a cell is right clicked
31879          * @param {Grid} this
31880          * @param {Number} rowIndex
31881          * @param {Number} cellIndex
31882          * @param {Roo.EventObject} e
31883          */
31884          "cellcontextmenu" : true,
31885         /**
31886          * @event headercontextmenu
31887          * Fires when a header is right clicked
31888          * @param {Grid} this
31889          * @param {Number} columnIndex
31890          * @param {Roo.EventObject} e
31891          */
31892         "headercontextmenu" : true,
31893         /**
31894          * @event bodyscroll
31895          * Fires when the body element is scrolled
31896          * @param {Number} scrollLeft
31897          * @param {Number} scrollTop
31898          */
31899         "bodyscroll" : true,
31900         /**
31901          * @event columnresize
31902          * Fires when the user resizes a column
31903          * @param {Number} columnIndex
31904          * @param {Number} newSize
31905          */
31906         "columnresize" : true,
31907         /**
31908          * @event columnmove
31909          * Fires when the user moves a column
31910          * @param {Number} oldIndex
31911          * @param {Number} newIndex
31912          */
31913         "columnmove" : true,
31914         /**
31915          * @event startdrag
31916          * Fires when row(s) start being dragged
31917          * @param {Grid} this
31918          * @param {Roo.GridDD} dd The drag drop object
31919          * @param {event} e The raw browser event
31920          */
31921         "startdrag" : true,
31922         /**
31923          * @event enddrag
31924          * Fires when a drag operation is complete
31925          * @param {Grid} this
31926          * @param {Roo.GridDD} dd The drag drop object
31927          * @param {event} e The raw browser event
31928          */
31929         "enddrag" : true,
31930         /**
31931          * @event dragdrop
31932          * Fires when dragged row(s) are dropped on a valid DD target
31933          * @param {Grid} this
31934          * @param {Roo.GridDD} dd The drag drop object
31935          * @param {String} targetId The target drag drop object
31936          * @param {event} e The raw browser event
31937          */
31938         "dragdrop" : true,
31939         /**
31940          * @event dragover
31941          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31942          * @param {Grid} this
31943          * @param {Roo.GridDD} dd The drag drop object
31944          * @param {String} targetId The target drag drop object
31945          * @param {event} e The raw browser event
31946          */
31947         "dragover" : true,
31948         /**
31949          * @event dragenter
31950          *  Fires when the dragged row(s) first cross another DD target while being dragged
31951          * @param {Grid} this
31952          * @param {Roo.GridDD} dd The drag drop object
31953          * @param {String} targetId The target drag drop object
31954          * @param {event} e The raw browser event
31955          */
31956         "dragenter" : true,
31957         /**
31958          * @event dragout
31959          * Fires when the dragged row(s) leave another DD target while being dragged
31960          * @param {Grid} this
31961          * @param {Roo.GridDD} dd The drag drop object
31962          * @param {String} targetId The target drag drop object
31963          * @param {event} e The raw browser event
31964          */
31965         "dragout" : true,
31966         /**
31967          * @event rowclass
31968          * Fires when a row is rendered, so you can change add a style to it.
31969          * @param {GridView} gridview   The grid view
31970          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31971          */
31972         'rowclass' : true,
31973
31974         /**
31975          * @event render
31976          * Fires when the grid is rendered
31977          * @param {Grid} grid
31978          */
31979         'render' : true
31980     });
31981
31982     Roo.grid.Grid.superclass.constructor.call(this);
31983 };
31984 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31985     
31986     /**
31987      * @cfg {String} ddGroup - drag drop group.
31988      */
31989
31990     /**
31991      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31992      */
31993     minColumnWidth : 25,
31994
31995     /**
31996      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31997      * <b>on initial render.</b> It is more efficient to explicitly size the columns
31998      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31999      */
32000     autoSizeColumns : false,
32001
32002     /**
32003      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32004      */
32005     autoSizeHeaders : true,
32006
32007     /**
32008      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32009      */
32010     monitorWindowResize : true,
32011
32012     /**
32013      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32014      * rows measured to get a columns size. Default is 0 (all rows).
32015      */
32016     maxRowsToMeasure : 0,
32017
32018     /**
32019      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32020      */
32021     trackMouseOver : true,
32022
32023     /**
32024     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32025     */
32026     
32027     /**
32028     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32029     */
32030     enableDragDrop : false,
32031     
32032     /**
32033     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32034     */
32035     enableColumnMove : true,
32036     
32037     /**
32038     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32039     */
32040     enableColumnHide : true,
32041     
32042     /**
32043     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32044     */
32045     enableRowHeightSync : false,
32046     
32047     /**
32048     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32049     */
32050     stripeRows : true,
32051     
32052     /**
32053     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32054     */
32055     autoHeight : false,
32056
32057     /**
32058      * @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.
32059      */
32060     autoExpandColumn : false,
32061
32062     /**
32063     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32064     * Default is 50.
32065     */
32066     autoExpandMin : 50,
32067
32068     /**
32069     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32070     */
32071     autoExpandMax : 1000,
32072
32073     /**
32074     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32075     */
32076     view : null,
32077
32078     /**
32079     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32080     */
32081     loadMask : false,
32082     /**
32083     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32084     */
32085     dropTarget: false,
32086     
32087    
32088     
32089     // private
32090     rendered : false,
32091
32092     /**
32093     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32094     * of a fixed width. Default is false.
32095     */
32096     /**
32097     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32098     */
32099     /**
32100      * Called once after all setup has been completed and the grid is ready to be rendered.
32101      * @return {Roo.grid.Grid} this
32102      */
32103     render : function()
32104     {
32105         var c = this.container;
32106         // try to detect autoHeight/width mode
32107         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32108             this.autoHeight = true;
32109         }
32110         var view = this.getView();
32111         view.init(this);
32112
32113         c.on("click", this.onClick, this);
32114         c.on("dblclick", this.onDblClick, this);
32115         c.on("contextmenu", this.onContextMenu, this);
32116         c.on("keydown", this.onKeyDown, this);
32117         if (Roo.isTouch) {
32118             c.on("touchstart", this.onTouchStart, this);
32119         }
32120
32121         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32122
32123         this.getSelectionModel().init(this);
32124
32125         view.render();
32126
32127         if(this.loadMask){
32128             this.loadMask = new Roo.LoadMask(this.container,
32129                     Roo.apply({store:this.dataSource}, this.loadMask));
32130         }
32131         
32132         
32133         if (this.toolbar && this.toolbar.xtype) {
32134             this.toolbar.container = this.getView().getHeaderPanel(true);
32135             this.toolbar = new Roo.Toolbar(this.toolbar);
32136         }
32137         if (this.footer && this.footer.xtype) {
32138             this.footer.dataSource = this.getDataSource();
32139             this.footer.container = this.getView().getFooterPanel(true);
32140             this.footer = Roo.factory(this.footer, Roo);
32141         }
32142         if (this.dropTarget && this.dropTarget.xtype) {
32143             delete this.dropTarget.xtype;
32144             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32145         }
32146         
32147         
32148         this.rendered = true;
32149         this.fireEvent('render', this);
32150         return this;
32151     },
32152
32153         /**
32154          * Reconfigures the grid to use a different Store and Column Model.
32155          * The View will be bound to the new objects and refreshed.
32156          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32157          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32158          */
32159     reconfigure : function(dataSource, colModel){
32160         if(this.loadMask){
32161             this.loadMask.destroy();
32162             this.loadMask = new Roo.LoadMask(this.container,
32163                     Roo.apply({store:dataSource}, this.loadMask));
32164         }
32165         this.view.bind(dataSource, colModel);
32166         this.dataSource = dataSource;
32167         this.colModel = colModel;
32168         this.view.refresh(true);
32169     },
32170
32171     // private
32172     onKeyDown : function(e){
32173         this.fireEvent("keydown", e);
32174     },
32175
32176     /**
32177      * Destroy this grid.
32178      * @param {Boolean} removeEl True to remove the element
32179      */
32180     destroy : function(removeEl, keepListeners){
32181         if(this.loadMask){
32182             this.loadMask.destroy();
32183         }
32184         var c = this.container;
32185         c.removeAllListeners();
32186         this.view.destroy();
32187         this.colModel.purgeListeners();
32188         if(!keepListeners){
32189             this.purgeListeners();
32190         }
32191         c.update("");
32192         if(removeEl === true){
32193             c.remove();
32194         }
32195     },
32196
32197     // private
32198     processEvent : function(name, e){
32199         // does this fire select???
32200         //Roo.log('grid:processEvent '  + name);
32201         
32202         if (name != 'touchstart' ) {
32203             this.fireEvent(name, e);    
32204         }
32205         
32206         var t = e.getTarget();
32207         var v = this.view;
32208         var header = v.findHeaderIndex(t);
32209         if(header !== false){
32210             var ename = name == 'touchstart' ? 'click' : name;
32211              
32212             this.fireEvent("header" + ename, this, header, e);
32213         }else{
32214             var row = v.findRowIndex(t);
32215             var cell = v.findCellIndex(t);
32216             if (name == 'touchstart') {
32217                 // first touch is always a click.
32218                 // hopefull this happens after selection is updated.?
32219                 name = false;
32220                 
32221                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32222                     var cs = this.selModel.getSelectedCell();
32223                     if (row == cs[0] && cell == cs[1]){
32224                         name = 'dblclick';
32225                     }
32226                 }
32227                 if (typeof(this.selModel.getSelections) != 'undefined') {
32228                     var cs = this.selModel.getSelections();
32229                     var ds = this.dataSource;
32230                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32231                         name = 'dblclick';
32232                     }
32233                 }
32234                 if (!name) {
32235                     return;
32236                 }
32237             }
32238             
32239             
32240             if(row !== false){
32241                 this.fireEvent("row" + name, this, row, e);
32242                 if(cell !== false){
32243                     this.fireEvent("cell" + name, this, row, cell, e);
32244                 }
32245             }
32246         }
32247     },
32248
32249     // private
32250     onClick : function(e){
32251         this.processEvent("click", e);
32252     },
32253    // private
32254     onTouchStart : function(e){
32255         this.processEvent("touchstart", e);
32256     },
32257
32258     // private
32259     onContextMenu : function(e, t){
32260         this.processEvent("contextmenu", e);
32261     },
32262
32263     // private
32264     onDblClick : function(e){
32265         this.processEvent("dblclick", e);
32266     },
32267
32268     // private
32269     walkCells : function(row, col, step, fn, scope){
32270         var cm = this.colModel, clen = cm.getColumnCount();
32271         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32272         if(step < 0){
32273             if(col < 0){
32274                 row--;
32275                 first = false;
32276             }
32277             while(row >= 0){
32278                 if(!first){
32279                     col = clen-1;
32280                 }
32281                 first = false;
32282                 while(col >= 0){
32283                     if(fn.call(scope || this, row, col, cm) === true){
32284                         return [row, col];
32285                     }
32286                     col--;
32287                 }
32288                 row--;
32289             }
32290         } else {
32291             if(col >= clen){
32292                 row++;
32293                 first = false;
32294             }
32295             while(row < rlen){
32296                 if(!first){
32297                     col = 0;
32298                 }
32299                 first = false;
32300                 while(col < clen){
32301                     if(fn.call(scope || this, row, col, cm) === true){
32302                         return [row, col];
32303                     }
32304                     col++;
32305                 }
32306                 row++;
32307             }
32308         }
32309         return null;
32310     },
32311
32312     // private
32313     getSelections : function(){
32314         return this.selModel.getSelections();
32315     },
32316
32317     /**
32318      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32319      * but if manual update is required this method will initiate it.
32320      */
32321     autoSize : function(){
32322         if(this.rendered){
32323             this.view.layout();
32324             if(this.view.adjustForScroll){
32325                 this.view.adjustForScroll();
32326             }
32327         }
32328     },
32329
32330     /**
32331      * Returns the grid's underlying element.
32332      * @return {Element} The element
32333      */
32334     getGridEl : function(){
32335         return this.container;
32336     },
32337
32338     // private for compatibility, overridden by editor grid
32339     stopEditing : function(){},
32340
32341     /**
32342      * Returns the grid's SelectionModel.
32343      * @return {SelectionModel}
32344      */
32345     getSelectionModel : function(){
32346         if(!this.selModel){
32347             this.selModel = new Roo.grid.RowSelectionModel();
32348         }
32349         return this.selModel;
32350     },
32351
32352     /**
32353      * Returns the grid's DataSource.
32354      * @return {DataSource}
32355      */
32356     getDataSource : function(){
32357         return this.dataSource;
32358     },
32359
32360     /**
32361      * Returns the grid's ColumnModel.
32362      * @return {ColumnModel}
32363      */
32364     getColumnModel : function(){
32365         return this.colModel;
32366     },
32367
32368     /**
32369      * Returns the grid's GridView object.
32370      * @return {GridView}
32371      */
32372     getView : function(){
32373         if(!this.view){
32374             this.view = new Roo.grid.GridView(this.viewConfig);
32375         }
32376         return this.view;
32377     },
32378     /**
32379      * Called to get grid's drag proxy text, by default returns this.ddText.
32380      * @return {String}
32381      */
32382     getDragDropText : function(){
32383         var count = this.selModel.getCount();
32384         return String.format(this.ddText, count, count == 1 ? '' : 's');
32385     }
32386 });
32387 /**
32388  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32389  * %0 is replaced with the number of selected rows.
32390  * @type String
32391  */
32392 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32393  * Based on:
32394  * Ext JS Library 1.1.1
32395  * Copyright(c) 2006-2007, Ext JS, LLC.
32396  *
32397  * Originally Released Under LGPL - original licence link has changed is not relivant.
32398  *
32399  * Fork - LGPL
32400  * <script type="text/javascript">
32401  */
32402  
32403 Roo.grid.AbstractGridView = function(){
32404         this.grid = null;
32405         
32406         this.events = {
32407             "beforerowremoved" : true,
32408             "beforerowsinserted" : true,
32409             "beforerefresh" : true,
32410             "rowremoved" : true,
32411             "rowsinserted" : true,
32412             "rowupdated" : true,
32413             "refresh" : true
32414         };
32415     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32416 };
32417
32418 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32419     rowClass : "x-grid-row",
32420     cellClass : "x-grid-cell",
32421     tdClass : "x-grid-td",
32422     hdClass : "x-grid-hd",
32423     splitClass : "x-grid-hd-split",
32424     
32425     init: function(grid){
32426         this.grid = grid;
32427                 var cid = this.grid.getGridEl().id;
32428         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32429         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32430         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32431         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32432         },
32433         
32434     getColumnRenderers : function(){
32435         var renderers = [];
32436         var cm = this.grid.colModel;
32437         var colCount = cm.getColumnCount();
32438         for(var i = 0; i < colCount; i++){
32439             renderers[i] = cm.getRenderer(i);
32440         }
32441         return renderers;
32442     },
32443     
32444     getColumnIds : function(){
32445         var ids = [];
32446         var cm = this.grid.colModel;
32447         var colCount = cm.getColumnCount();
32448         for(var i = 0; i < colCount; i++){
32449             ids[i] = cm.getColumnId(i);
32450         }
32451         return ids;
32452     },
32453     
32454     getDataIndexes : function(){
32455         if(!this.indexMap){
32456             this.indexMap = this.buildIndexMap();
32457         }
32458         return this.indexMap.colToData;
32459     },
32460     
32461     getColumnIndexByDataIndex : function(dataIndex){
32462         if(!this.indexMap){
32463             this.indexMap = this.buildIndexMap();
32464         }
32465         return this.indexMap.dataToCol[dataIndex];
32466     },
32467     
32468     /**
32469      * Set a css style for a column dynamically. 
32470      * @param {Number} colIndex The index of the column
32471      * @param {String} name The css property name
32472      * @param {String} value The css value
32473      */
32474     setCSSStyle : function(colIndex, name, value){
32475         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32476         Roo.util.CSS.updateRule(selector, name, value);
32477     },
32478     
32479     generateRules : function(cm){
32480         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32481         Roo.util.CSS.removeStyleSheet(rulesId);
32482         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32483             var cid = cm.getColumnId(i);
32484             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32485                          this.tdSelector, cid, " {\n}\n",
32486                          this.hdSelector, cid, " {\n}\n",
32487                          this.splitSelector, cid, " {\n}\n");
32488         }
32489         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32490     }
32491 });/*
32492  * Based on:
32493  * Ext JS Library 1.1.1
32494  * Copyright(c) 2006-2007, Ext JS, LLC.
32495  *
32496  * Originally Released Under LGPL - original licence link has changed is not relivant.
32497  *
32498  * Fork - LGPL
32499  * <script type="text/javascript">
32500  */
32501
32502 // private
32503 // This is a support class used internally by the Grid components
32504 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32505     this.grid = grid;
32506     this.view = grid.getView();
32507     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32508     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32509     if(hd2){
32510         this.setHandleElId(Roo.id(hd));
32511         this.setOuterHandleElId(Roo.id(hd2));
32512     }
32513     this.scroll = false;
32514 };
32515 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32516     maxDragWidth: 120,
32517     getDragData : function(e){
32518         var t = Roo.lib.Event.getTarget(e);
32519         var h = this.view.findHeaderCell(t);
32520         if(h){
32521             return {ddel: h.firstChild, header:h};
32522         }
32523         return false;
32524     },
32525
32526     onInitDrag : function(e){
32527         this.view.headersDisabled = true;
32528         var clone = this.dragData.ddel.cloneNode(true);
32529         clone.id = Roo.id();
32530         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32531         this.proxy.update(clone);
32532         return true;
32533     },
32534
32535     afterValidDrop : function(){
32536         var v = this.view;
32537         setTimeout(function(){
32538             v.headersDisabled = false;
32539         }, 50);
32540     },
32541
32542     afterInvalidDrop : function(){
32543         var v = this.view;
32544         setTimeout(function(){
32545             v.headersDisabled = false;
32546         }, 50);
32547     }
32548 });
32549 /*
32550  * Based on:
32551  * Ext JS Library 1.1.1
32552  * Copyright(c) 2006-2007, Ext JS, LLC.
32553  *
32554  * Originally Released Under LGPL - original licence link has changed is not relivant.
32555  *
32556  * Fork - LGPL
32557  * <script type="text/javascript">
32558  */
32559 // private
32560 // This is a support class used internally by the Grid components
32561 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32562     this.grid = grid;
32563     this.view = grid.getView();
32564     // split the proxies so they don't interfere with mouse events
32565     this.proxyTop = Roo.DomHelper.append(document.body, {
32566         cls:"col-move-top", html:"&#160;"
32567     }, true);
32568     this.proxyBottom = Roo.DomHelper.append(document.body, {
32569         cls:"col-move-bottom", html:"&#160;"
32570     }, true);
32571     this.proxyTop.hide = this.proxyBottom.hide = function(){
32572         this.setLeftTop(-100,-100);
32573         this.setStyle("visibility", "hidden");
32574     };
32575     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32576     // temporarily disabled
32577     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32578     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32579 };
32580 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32581     proxyOffsets : [-4, -9],
32582     fly: Roo.Element.fly,
32583
32584     getTargetFromEvent : function(e){
32585         var t = Roo.lib.Event.getTarget(e);
32586         var cindex = this.view.findCellIndex(t);
32587         if(cindex !== false){
32588             return this.view.getHeaderCell(cindex);
32589         }
32590         return null;
32591     },
32592
32593     nextVisible : function(h){
32594         var v = this.view, cm = this.grid.colModel;
32595         h = h.nextSibling;
32596         while(h){
32597             if(!cm.isHidden(v.getCellIndex(h))){
32598                 return h;
32599             }
32600             h = h.nextSibling;
32601         }
32602         return null;
32603     },
32604
32605     prevVisible : function(h){
32606         var v = this.view, cm = this.grid.colModel;
32607         h = h.prevSibling;
32608         while(h){
32609             if(!cm.isHidden(v.getCellIndex(h))){
32610                 return h;
32611             }
32612             h = h.prevSibling;
32613         }
32614         return null;
32615     },
32616
32617     positionIndicator : function(h, n, e){
32618         var x = Roo.lib.Event.getPageX(e);
32619         var r = Roo.lib.Dom.getRegion(n.firstChild);
32620         var px, pt, py = r.top + this.proxyOffsets[1];
32621         if((r.right - x) <= (r.right-r.left)/2){
32622             px = r.right+this.view.borderWidth;
32623             pt = "after";
32624         }else{
32625             px = r.left;
32626             pt = "before";
32627         }
32628         var oldIndex = this.view.getCellIndex(h);
32629         var newIndex = this.view.getCellIndex(n);
32630
32631         if(this.grid.colModel.isFixed(newIndex)){
32632             return false;
32633         }
32634
32635         var locked = this.grid.colModel.isLocked(newIndex);
32636
32637         if(pt == "after"){
32638             newIndex++;
32639         }
32640         if(oldIndex < newIndex){
32641             newIndex--;
32642         }
32643         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32644             return false;
32645         }
32646         px +=  this.proxyOffsets[0];
32647         this.proxyTop.setLeftTop(px, py);
32648         this.proxyTop.show();
32649         if(!this.bottomOffset){
32650             this.bottomOffset = this.view.mainHd.getHeight();
32651         }
32652         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32653         this.proxyBottom.show();
32654         return pt;
32655     },
32656
32657     onNodeEnter : function(n, dd, e, data){
32658         if(data.header != n){
32659             this.positionIndicator(data.header, n, e);
32660         }
32661     },
32662
32663     onNodeOver : function(n, dd, e, data){
32664         var result = false;
32665         if(data.header != n){
32666             result = this.positionIndicator(data.header, n, e);
32667         }
32668         if(!result){
32669             this.proxyTop.hide();
32670             this.proxyBottom.hide();
32671         }
32672         return result ? this.dropAllowed : this.dropNotAllowed;
32673     },
32674
32675     onNodeOut : function(n, dd, e, data){
32676         this.proxyTop.hide();
32677         this.proxyBottom.hide();
32678     },
32679
32680     onNodeDrop : function(n, dd, e, data){
32681         var h = data.header;
32682         if(h != n){
32683             var cm = this.grid.colModel;
32684             var x = Roo.lib.Event.getPageX(e);
32685             var r = Roo.lib.Dom.getRegion(n.firstChild);
32686             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32687             var oldIndex = this.view.getCellIndex(h);
32688             var newIndex = this.view.getCellIndex(n);
32689             var locked = cm.isLocked(newIndex);
32690             if(pt == "after"){
32691                 newIndex++;
32692             }
32693             if(oldIndex < newIndex){
32694                 newIndex--;
32695             }
32696             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32697                 return false;
32698             }
32699             cm.setLocked(oldIndex, locked, true);
32700             cm.moveColumn(oldIndex, newIndex);
32701             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32702             return true;
32703         }
32704         return false;
32705     }
32706 });
32707 /*
32708  * Based on:
32709  * Ext JS Library 1.1.1
32710  * Copyright(c) 2006-2007, Ext JS, LLC.
32711  *
32712  * Originally Released Under LGPL - original licence link has changed is not relivant.
32713  *
32714  * Fork - LGPL
32715  * <script type="text/javascript">
32716  */
32717   
32718 /**
32719  * @class Roo.grid.GridView
32720  * @extends Roo.util.Observable
32721  *
32722  * @constructor
32723  * @param {Object} config
32724  */
32725 Roo.grid.GridView = function(config){
32726     Roo.grid.GridView.superclass.constructor.call(this);
32727     this.el = null;
32728
32729     Roo.apply(this, config);
32730 };
32731
32732 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32733
32734     unselectable :  'unselectable="on"',
32735     unselectableCls :  'x-unselectable',
32736     
32737     
32738     rowClass : "x-grid-row",
32739
32740     cellClass : "x-grid-col",
32741
32742     tdClass : "x-grid-td",
32743
32744     hdClass : "x-grid-hd",
32745
32746     splitClass : "x-grid-split",
32747
32748     sortClasses : ["sort-asc", "sort-desc"],
32749
32750     enableMoveAnim : false,
32751
32752     hlColor: "C3DAF9",
32753
32754     dh : Roo.DomHelper,
32755
32756     fly : Roo.Element.fly,
32757
32758     css : Roo.util.CSS,
32759
32760     borderWidth: 1,
32761
32762     splitOffset: 3,
32763
32764     scrollIncrement : 22,
32765
32766     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32767
32768     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32769
32770     bind : function(ds, cm){
32771         if(this.ds){
32772             this.ds.un("load", this.onLoad, this);
32773             this.ds.un("datachanged", this.onDataChange, this);
32774             this.ds.un("add", this.onAdd, this);
32775             this.ds.un("remove", this.onRemove, this);
32776             this.ds.un("update", this.onUpdate, this);
32777             this.ds.un("clear", this.onClear, this);
32778         }
32779         if(ds){
32780             ds.on("load", this.onLoad, this);
32781             ds.on("datachanged", this.onDataChange, this);
32782             ds.on("add", this.onAdd, this);
32783             ds.on("remove", this.onRemove, this);
32784             ds.on("update", this.onUpdate, this);
32785             ds.on("clear", this.onClear, this);
32786         }
32787         this.ds = ds;
32788
32789         if(this.cm){
32790             this.cm.un("widthchange", this.onColWidthChange, this);
32791             this.cm.un("headerchange", this.onHeaderChange, this);
32792             this.cm.un("hiddenchange", this.onHiddenChange, this);
32793             this.cm.un("columnmoved", this.onColumnMove, this);
32794             this.cm.un("columnlockchange", this.onColumnLock, this);
32795         }
32796         if(cm){
32797             this.generateRules(cm);
32798             cm.on("widthchange", this.onColWidthChange, this);
32799             cm.on("headerchange", this.onHeaderChange, this);
32800             cm.on("hiddenchange", this.onHiddenChange, this);
32801             cm.on("columnmoved", this.onColumnMove, this);
32802             cm.on("columnlockchange", this.onColumnLock, this);
32803         }
32804         this.cm = cm;
32805     },
32806
32807     init: function(grid){
32808         Roo.grid.GridView.superclass.init.call(this, grid);
32809
32810         this.bind(grid.dataSource, grid.colModel);
32811
32812         grid.on("headerclick", this.handleHeaderClick, this);
32813
32814         if(grid.trackMouseOver){
32815             grid.on("mouseover", this.onRowOver, this);
32816             grid.on("mouseout", this.onRowOut, this);
32817         }
32818         grid.cancelTextSelection = function(){};
32819         this.gridId = grid.id;
32820
32821         var tpls = this.templates || {};
32822
32823         if(!tpls.master){
32824             tpls.master = new Roo.Template(
32825                '<div class="x-grid" hidefocus="true">',
32826                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32827                   '<div class="x-grid-topbar"></div>',
32828                   '<div class="x-grid-scroller"><div></div></div>',
32829                   '<div class="x-grid-locked">',
32830                       '<div class="x-grid-header">{lockedHeader}</div>',
32831                       '<div class="x-grid-body">{lockedBody}</div>',
32832                   "</div>",
32833                   '<div class="x-grid-viewport">',
32834                       '<div class="x-grid-header">{header}</div>',
32835                       '<div class="x-grid-body">{body}</div>',
32836                   "</div>",
32837                   '<div class="x-grid-bottombar"></div>',
32838                  
32839                   '<div class="x-grid-resize-proxy">&#160;</div>',
32840                "</div>"
32841             );
32842             tpls.master.disableformats = true;
32843         }
32844
32845         if(!tpls.header){
32846             tpls.header = new Roo.Template(
32847                '<table border="0" cellspacing="0" cellpadding="0">',
32848                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32849                "</table>{splits}"
32850             );
32851             tpls.header.disableformats = true;
32852         }
32853         tpls.header.compile();
32854
32855         if(!tpls.hcell){
32856             tpls.hcell = new Roo.Template(
32857                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32858                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32859                 "</div></td>"
32860              );
32861              tpls.hcell.disableFormats = true;
32862         }
32863         tpls.hcell.compile();
32864
32865         if(!tpls.hsplit){
32866             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32867                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32868             tpls.hsplit.disableFormats = true;
32869         }
32870         tpls.hsplit.compile();
32871
32872         if(!tpls.body){
32873             tpls.body = new Roo.Template(
32874                '<table border="0" cellspacing="0" cellpadding="0">',
32875                "<tbody>{rows}</tbody>",
32876                "</table>"
32877             );
32878             tpls.body.disableFormats = true;
32879         }
32880         tpls.body.compile();
32881
32882         if(!tpls.row){
32883             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32884             tpls.row.disableFormats = true;
32885         }
32886         tpls.row.compile();
32887
32888         if(!tpls.cell){
32889             tpls.cell = new Roo.Template(
32890                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32891                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32892                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32893                 "</td>"
32894             );
32895             tpls.cell.disableFormats = true;
32896         }
32897         tpls.cell.compile();
32898
32899         this.templates = tpls;
32900     },
32901
32902     // remap these for backwards compat
32903     onColWidthChange : function(){
32904         this.updateColumns.apply(this, arguments);
32905     },
32906     onHeaderChange : function(){
32907         this.updateHeaders.apply(this, arguments);
32908     }, 
32909     onHiddenChange : function(){
32910         this.handleHiddenChange.apply(this, arguments);
32911     },
32912     onColumnMove : function(){
32913         this.handleColumnMove.apply(this, arguments);
32914     },
32915     onColumnLock : function(){
32916         this.handleLockChange.apply(this, arguments);
32917     },
32918
32919     onDataChange : function(){
32920         this.refresh();
32921         this.updateHeaderSortState();
32922     },
32923
32924     onClear : function(){
32925         this.refresh();
32926     },
32927
32928     onUpdate : function(ds, record){
32929         this.refreshRow(record);
32930     },
32931
32932     refreshRow : function(record){
32933         var ds = this.ds, index;
32934         if(typeof record == 'number'){
32935             index = record;
32936             record = ds.getAt(index);
32937         }else{
32938             index = ds.indexOf(record);
32939         }
32940         this.insertRows(ds, index, index, true);
32941         this.onRemove(ds, record, index+1, true);
32942         this.syncRowHeights(index, index);
32943         this.layout();
32944         this.fireEvent("rowupdated", this, index, record);
32945     },
32946
32947     onAdd : function(ds, records, index){
32948         this.insertRows(ds, index, index + (records.length-1));
32949     },
32950
32951     onRemove : function(ds, record, index, isUpdate){
32952         if(isUpdate !== true){
32953             this.fireEvent("beforerowremoved", this, index, record);
32954         }
32955         var bt = this.getBodyTable(), lt = this.getLockedTable();
32956         if(bt.rows[index]){
32957             bt.firstChild.removeChild(bt.rows[index]);
32958         }
32959         if(lt.rows[index]){
32960             lt.firstChild.removeChild(lt.rows[index]);
32961         }
32962         if(isUpdate !== true){
32963             this.stripeRows(index);
32964             this.syncRowHeights(index, index);
32965             this.layout();
32966             this.fireEvent("rowremoved", this, index, record);
32967         }
32968     },
32969
32970     onLoad : function(){
32971         this.scrollToTop();
32972     },
32973
32974     /**
32975      * Scrolls the grid to the top
32976      */
32977     scrollToTop : function(){
32978         if(this.scroller){
32979             this.scroller.dom.scrollTop = 0;
32980             this.syncScroll();
32981         }
32982     },
32983
32984     /**
32985      * Gets a panel in the header of the grid that can be used for toolbars etc.
32986      * After modifying the contents of this panel a call to grid.autoSize() may be
32987      * required to register any changes in size.
32988      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32989      * @return Roo.Element
32990      */
32991     getHeaderPanel : function(doShow){
32992         if(doShow){
32993             this.headerPanel.show();
32994         }
32995         return this.headerPanel;
32996     },
32997
32998     /**
32999      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33000      * After modifying the contents of this panel a call to grid.autoSize() may be
33001      * required to register any changes in size.
33002      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33003      * @return Roo.Element
33004      */
33005     getFooterPanel : function(doShow){
33006         if(doShow){
33007             this.footerPanel.show();
33008         }
33009         return this.footerPanel;
33010     },
33011
33012     initElements : function(){
33013         var E = Roo.Element;
33014         var el = this.grid.getGridEl().dom.firstChild;
33015         var cs = el.childNodes;
33016
33017         this.el = new E(el);
33018         
33019          this.focusEl = new E(el.firstChild);
33020         this.focusEl.swallowEvent("click", true);
33021         
33022         this.headerPanel = new E(cs[1]);
33023         this.headerPanel.enableDisplayMode("block");
33024
33025         this.scroller = new E(cs[2]);
33026         this.scrollSizer = new E(this.scroller.dom.firstChild);
33027
33028         this.lockedWrap = new E(cs[3]);
33029         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33030         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33031
33032         this.mainWrap = new E(cs[4]);
33033         this.mainHd = new E(this.mainWrap.dom.firstChild);
33034         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33035
33036         this.footerPanel = new E(cs[5]);
33037         this.footerPanel.enableDisplayMode("block");
33038
33039         this.resizeProxy = new E(cs[6]);
33040
33041         this.headerSelector = String.format(
33042            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33043            this.lockedHd.id, this.mainHd.id
33044         );
33045
33046         this.splitterSelector = String.format(
33047            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33048            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33049         );
33050     },
33051     idToCssName : function(s)
33052     {
33053         return s.replace(/[^a-z0-9]+/ig, '-');
33054     },
33055
33056     getHeaderCell : function(index){
33057         return Roo.DomQuery.select(this.headerSelector)[index];
33058     },
33059
33060     getHeaderCellMeasure : function(index){
33061         return this.getHeaderCell(index).firstChild;
33062     },
33063
33064     getHeaderCellText : function(index){
33065         return this.getHeaderCell(index).firstChild.firstChild;
33066     },
33067
33068     getLockedTable : function(){
33069         return this.lockedBody.dom.firstChild;
33070     },
33071
33072     getBodyTable : function(){
33073         return this.mainBody.dom.firstChild;
33074     },
33075
33076     getLockedRow : function(index){
33077         return this.getLockedTable().rows[index];
33078     },
33079
33080     getRow : function(index){
33081         return this.getBodyTable().rows[index];
33082     },
33083
33084     getRowComposite : function(index){
33085         if(!this.rowEl){
33086             this.rowEl = new Roo.CompositeElementLite();
33087         }
33088         var els = [], lrow, mrow;
33089         if(lrow = this.getLockedRow(index)){
33090             els.push(lrow);
33091         }
33092         if(mrow = this.getRow(index)){
33093             els.push(mrow);
33094         }
33095         this.rowEl.elements = els;
33096         return this.rowEl;
33097     },
33098     /**
33099      * Gets the 'td' of the cell
33100      * 
33101      * @param {Integer} rowIndex row to select
33102      * @param {Integer} colIndex column to select
33103      * 
33104      * @return {Object} 
33105      */
33106     getCell : function(rowIndex, colIndex){
33107         var locked = this.cm.getLockedCount();
33108         var source;
33109         if(colIndex < locked){
33110             source = this.lockedBody.dom.firstChild;
33111         }else{
33112             source = this.mainBody.dom.firstChild;
33113             colIndex -= locked;
33114         }
33115         return source.rows[rowIndex].childNodes[colIndex];
33116     },
33117
33118     getCellText : function(rowIndex, colIndex){
33119         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33120     },
33121
33122     getCellBox : function(cell){
33123         var b = this.fly(cell).getBox();
33124         if(Roo.isOpera){ // opera fails to report the Y
33125             b.y = cell.offsetTop + this.mainBody.getY();
33126         }
33127         return b;
33128     },
33129
33130     getCellIndex : function(cell){
33131         var id = String(cell.className).match(this.cellRE);
33132         if(id){
33133             return parseInt(id[1], 10);
33134         }
33135         return 0;
33136     },
33137
33138     findHeaderIndex : function(n){
33139         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33140         return r ? this.getCellIndex(r) : false;
33141     },
33142
33143     findHeaderCell : function(n){
33144         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33145         return r ? r : false;
33146     },
33147
33148     findRowIndex : function(n){
33149         if(!n){
33150             return false;
33151         }
33152         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33153         return r ? r.rowIndex : false;
33154     },
33155
33156     findCellIndex : function(node){
33157         var stop = this.el.dom;
33158         while(node && node != stop){
33159             if(this.findRE.test(node.className)){
33160                 return this.getCellIndex(node);
33161             }
33162             node = node.parentNode;
33163         }
33164         return false;
33165     },
33166
33167     getColumnId : function(index){
33168         return this.cm.getColumnId(index);
33169     },
33170
33171     getSplitters : function()
33172     {
33173         if(this.splitterSelector){
33174            return Roo.DomQuery.select(this.splitterSelector);
33175         }else{
33176             return null;
33177       }
33178     },
33179
33180     getSplitter : function(index){
33181         return this.getSplitters()[index];
33182     },
33183
33184     onRowOver : function(e, t){
33185         var row;
33186         if((row = this.findRowIndex(t)) !== false){
33187             this.getRowComposite(row).addClass("x-grid-row-over");
33188         }
33189     },
33190
33191     onRowOut : function(e, t){
33192         var row;
33193         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33194             this.getRowComposite(row).removeClass("x-grid-row-over");
33195         }
33196     },
33197
33198     renderHeaders : function(){
33199         var cm = this.cm;
33200         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33201         var cb = [], lb = [], sb = [], lsb = [], p = {};
33202         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33203             p.cellId = "x-grid-hd-0-" + i;
33204             p.splitId = "x-grid-csplit-0-" + i;
33205             p.id = cm.getColumnId(i);
33206             p.value = cm.getColumnHeader(i) || "";
33207             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33208             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33209             if(!cm.isLocked(i)){
33210                 cb[cb.length] = ct.apply(p);
33211                 sb[sb.length] = st.apply(p);
33212             }else{
33213                 lb[lb.length] = ct.apply(p);
33214                 lsb[lsb.length] = st.apply(p);
33215             }
33216         }
33217         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33218                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33219     },
33220
33221     updateHeaders : function(){
33222         var html = this.renderHeaders();
33223         this.lockedHd.update(html[0]);
33224         this.mainHd.update(html[1]);
33225     },
33226
33227     /**
33228      * Focuses the specified row.
33229      * @param {Number} row The row index
33230      */
33231     focusRow : function(row)
33232     {
33233         //Roo.log('GridView.focusRow');
33234         var x = this.scroller.dom.scrollLeft;
33235         this.focusCell(row, 0, false);
33236         this.scroller.dom.scrollLeft = x;
33237     },
33238
33239     /**
33240      * Focuses the specified cell.
33241      * @param {Number} row The row index
33242      * @param {Number} col The column index
33243      * @param {Boolean} hscroll false to disable horizontal scrolling
33244      */
33245     focusCell : function(row, col, hscroll)
33246     {
33247         //Roo.log('GridView.focusCell');
33248         var el = this.ensureVisible(row, col, hscroll);
33249         this.focusEl.alignTo(el, "tl-tl");
33250         if(Roo.isGecko){
33251             this.focusEl.focus();
33252         }else{
33253             this.focusEl.focus.defer(1, this.focusEl);
33254         }
33255     },
33256
33257     /**
33258      * Scrolls the specified cell into view
33259      * @param {Number} row The row index
33260      * @param {Number} col The column index
33261      * @param {Boolean} hscroll false to disable horizontal scrolling
33262      */
33263     ensureVisible : function(row, col, hscroll)
33264     {
33265         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33266         //return null; //disable for testing.
33267         if(typeof row != "number"){
33268             row = row.rowIndex;
33269         }
33270         if(row < 0 && row >= this.ds.getCount()){
33271             return  null;
33272         }
33273         col = (col !== undefined ? col : 0);
33274         var cm = this.grid.colModel;
33275         while(cm.isHidden(col)){
33276             col++;
33277         }
33278
33279         var el = this.getCell(row, col);
33280         if(!el){
33281             return null;
33282         }
33283         var c = this.scroller.dom;
33284
33285         var ctop = parseInt(el.offsetTop, 10);
33286         var cleft = parseInt(el.offsetLeft, 10);
33287         var cbot = ctop + el.offsetHeight;
33288         var cright = cleft + el.offsetWidth;
33289         
33290         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33291         var stop = parseInt(c.scrollTop, 10);
33292         var sleft = parseInt(c.scrollLeft, 10);
33293         var sbot = stop + ch;
33294         var sright = sleft + c.clientWidth;
33295         /*
33296         Roo.log('GridView.ensureVisible:' +
33297                 ' ctop:' + ctop +
33298                 ' c.clientHeight:' + c.clientHeight +
33299                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33300                 ' stop:' + stop +
33301                 ' cbot:' + cbot +
33302                 ' sbot:' + sbot +
33303                 ' ch:' + ch  
33304                 );
33305         */
33306         if(ctop < stop){
33307              c.scrollTop = ctop;
33308             //Roo.log("set scrolltop to ctop DISABLE?");
33309         }else if(cbot > sbot){
33310             //Roo.log("set scrolltop to cbot-ch");
33311             c.scrollTop = cbot-ch;
33312         }
33313         
33314         if(hscroll !== false){
33315             if(cleft < sleft){
33316                 c.scrollLeft = cleft;
33317             }else if(cright > sright){
33318                 c.scrollLeft = cright-c.clientWidth;
33319             }
33320         }
33321          
33322         return el;
33323     },
33324
33325     updateColumns : function(){
33326         this.grid.stopEditing();
33327         var cm = this.grid.colModel, colIds = this.getColumnIds();
33328         //var totalWidth = cm.getTotalWidth();
33329         var pos = 0;
33330         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33331             //if(cm.isHidden(i)) continue;
33332             var w = cm.getColumnWidth(i);
33333             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33334             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33335         }
33336         this.updateSplitters();
33337     },
33338
33339     generateRules : function(cm){
33340         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33341         Roo.util.CSS.removeStyleSheet(rulesId);
33342         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33343             var cid = cm.getColumnId(i);
33344             var align = '';
33345             if(cm.config[i].align){
33346                 align = 'text-align:'+cm.config[i].align+';';
33347             }
33348             var hidden = '';
33349             if(cm.isHidden(i)){
33350                 hidden = 'display:none;';
33351             }
33352             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33353             ruleBuf.push(
33354                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33355                     this.hdSelector, cid, " {\n", align, width, "}\n",
33356                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33357                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33358         }
33359         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33360     },
33361
33362     updateSplitters : function(){
33363         var cm = this.cm, s = this.getSplitters();
33364         if(s){ // splitters not created yet
33365             var pos = 0, locked = true;
33366             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33367                 if(cm.isHidden(i)) {
33368                     continue;
33369                 }
33370                 var w = cm.getColumnWidth(i); // make sure it's a number
33371                 if(!cm.isLocked(i) && locked){
33372                     pos = 0;
33373                     locked = false;
33374                 }
33375                 pos += w;
33376                 s[i].style.left = (pos-this.splitOffset) + "px";
33377             }
33378         }
33379     },
33380
33381     handleHiddenChange : function(colModel, colIndex, hidden){
33382         if(hidden){
33383             this.hideColumn(colIndex);
33384         }else{
33385             this.unhideColumn(colIndex);
33386         }
33387     },
33388
33389     hideColumn : function(colIndex){
33390         var cid = this.getColumnId(colIndex);
33391         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33392         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33393         if(Roo.isSafari){
33394             this.updateHeaders();
33395         }
33396         this.updateSplitters();
33397         this.layout();
33398     },
33399
33400     unhideColumn : function(colIndex){
33401         var cid = this.getColumnId(colIndex);
33402         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33403         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33404
33405         if(Roo.isSafari){
33406             this.updateHeaders();
33407         }
33408         this.updateSplitters();
33409         this.layout();
33410     },
33411
33412     insertRows : function(dm, firstRow, lastRow, isUpdate){
33413         if(firstRow == 0 && lastRow == dm.getCount()-1){
33414             this.refresh();
33415         }else{
33416             if(!isUpdate){
33417                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33418             }
33419             var s = this.getScrollState();
33420             var markup = this.renderRows(firstRow, lastRow);
33421             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33422             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33423             this.restoreScroll(s);
33424             if(!isUpdate){
33425                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33426                 this.syncRowHeights(firstRow, lastRow);
33427                 this.stripeRows(firstRow);
33428                 this.layout();
33429             }
33430         }
33431     },
33432
33433     bufferRows : function(markup, target, index){
33434         var before = null, trows = target.rows, tbody = target.tBodies[0];
33435         if(index < trows.length){
33436             before = trows[index];
33437         }
33438         var b = document.createElement("div");
33439         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33440         var rows = b.firstChild.rows;
33441         for(var i = 0, len = rows.length; i < len; i++){
33442             if(before){
33443                 tbody.insertBefore(rows[0], before);
33444             }else{
33445                 tbody.appendChild(rows[0]);
33446             }
33447         }
33448         b.innerHTML = "";
33449         b = null;
33450     },
33451
33452     deleteRows : function(dm, firstRow, lastRow){
33453         if(dm.getRowCount()<1){
33454             this.fireEvent("beforerefresh", this);
33455             this.mainBody.update("");
33456             this.lockedBody.update("");
33457             this.fireEvent("refresh", this);
33458         }else{
33459             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33460             var bt = this.getBodyTable();
33461             var tbody = bt.firstChild;
33462             var rows = bt.rows;
33463             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33464                 tbody.removeChild(rows[firstRow]);
33465             }
33466             this.stripeRows(firstRow);
33467             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33468         }
33469     },
33470
33471     updateRows : function(dataSource, firstRow, lastRow){
33472         var s = this.getScrollState();
33473         this.refresh();
33474         this.restoreScroll(s);
33475     },
33476
33477     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33478         if(!noRefresh){
33479            this.refresh();
33480         }
33481         this.updateHeaderSortState();
33482     },
33483
33484     getScrollState : function(){
33485         
33486         var sb = this.scroller.dom;
33487         return {left: sb.scrollLeft, top: sb.scrollTop};
33488     },
33489
33490     stripeRows : function(startRow){
33491         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33492             return;
33493         }
33494         startRow = startRow || 0;
33495         var rows = this.getBodyTable().rows;
33496         var lrows = this.getLockedTable().rows;
33497         var cls = ' x-grid-row-alt ';
33498         for(var i = startRow, len = rows.length; i < len; i++){
33499             var row = rows[i], lrow = lrows[i];
33500             var isAlt = ((i+1) % 2 == 0);
33501             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33502             if(isAlt == hasAlt){
33503                 continue;
33504             }
33505             if(isAlt){
33506                 row.className += " x-grid-row-alt";
33507             }else{
33508                 row.className = row.className.replace("x-grid-row-alt", "");
33509             }
33510             if(lrow){
33511                 lrow.className = row.className;
33512             }
33513         }
33514     },
33515
33516     restoreScroll : function(state){
33517         //Roo.log('GridView.restoreScroll');
33518         var sb = this.scroller.dom;
33519         sb.scrollLeft = state.left;
33520         sb.scrollTop = state.top;
33521         this.syncScroll();
33522     },
33523
33524     syncScroll : function(){
33525         //Roo.log('GridView.syncScroll');
33526         var sb = this.scroller.dom;
33527         var sh = this.mainHd.dom;
33528         var bs = this.mainBody.dom;
33529         var lv = this.lockedBody.dom;
33530         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33531         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33532     },
33533
33534     handleScroll : function(e){
33535         this.syncScroll();
33536         var sb = this.scroller.dom;
33537         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33538         e.stopEvent();
33539     },
33540
33541     handleWheel : function(e){
33542         var d = e.getWheelDelta();
33543         this.scroller.dom.scrollTop -= d*22;
33544         // set this here to prevent jumpy scrolling on large tables
33545         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33546         e.stopEvent();
33547     },
33548
33549     renderRows : function(startRow, endRow){
33550         // pull in all the crap needed to render rows
33551         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33552         var colCount = cm.getColumnCount();
33553
33554         if(ds.getCount() < 1){
33555             return ["", ""];
33556         }
33557
33558         // build a map for all the columns
33559         var cs = [];
33560         for(var i = 0; i < colCount; i++){
33561             var name = cm.getDataIndex(i);
33562             cs[i] = {
33563                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33564                 renderer : cm.getRenderer(i),
33565                 id : cm.getColumnId(i),
33566                 locked : cm.isLocked(i),
33567                 has_editor : cm.isCellEditable(i)
33568             };
33569         }
33570
33571         startRow = startRow || 0;
33572         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33573
33574         // records to render
33575         var rs = ds.getRange(startRow, endRow);
33576
33577         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33578     },
33579
33580     // As much as I hate to duplicate code, this was branched because FireFox really hates
33581     // [].join("") on strings. The performance difference was substantial enough to
33582     // branch this function
33583     doRender : Roo.isGecko ?
33584             function(cs, rs, ds, startRow, colCount, stripe){
33585                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33586                 // buffers
33587                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33588                 
33589                 var hasListener = this.grid.hasListener('rowclass');
33590                 var rowcfg = {};
33591                 for(var j = 0, len = rs.length; j < len; j++){
33592                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33593                     for(var i = 0; i < colCount; i++){
33594                         c = cs[i];
33595                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33596                         p.id = c.id;
33597                         p.css = p.attr = "";
33598                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33599                         if(p.value == undefined || p.value === "") {
33600                             p.value = "&#160;";
33601                         }
33602                         if(c.has_editor){
33603                             p.css += ' x-grid-editable-cell';
33604                         }
33605                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33606                             p.css +=  ' x-grid-dirty-cell';
33607                         }
33608                         var markup = ct.apply(p);
33609                         if(!c.locked){
33610                             cb+= markup;
33611                         }else{
33612                             lcb+= markup;
33613                         }
33614                     }
33615                     var alt = [];
33616                     if(stripe && ((rowIndex+1) % 2 == 0)){
33617                         alt.push("x-grid-row-alt")
33618                     }
33619                     if(r.dirty){
33620                         alt.push(  " x-grid-dirty-row");
33621                     }
33622                     rp.cells = lcb;
33623                     if(this.getRowClass){
33624                         alt.push(this.getRowClass(r, rowIndex));
33625                     }
33626                     if (hasListener) {
33627                         rowcfg = {
33628                              
33629                             record: r,
33630                             rowIndex : rowIndex,
33631                             rowClass : ''
33632                         };
33633                         this.grid.fireEvent('rowclass', this, rowcfg);
33634                         alt.push(rowcfg.rowClass);
33635                     }
33636                     rp.alt = alt.join(" ");
33637                     lbuf+= rt.apply(rp);
33638                     rp.cells = cb;
33639                     buf+=  rt.apply(rp);
33640                 }
33641                 return [lbuf, buf];
33642             } :
33643             function(cs, rs, ds, startRow, colCount, stripe){
33644                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33645                 // buffers
33646                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33647                 var hasListener = this.grid.hasListener('rowclass');
33648  
33649                 var rowcfg = {};
33650                 for(var j = 0, len = rs.length; j < len; j++){
33651                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33652                     for(var i = 0; i < colCount; i++){
33653                         c = cs[i];
33654                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33655                         p.id = c.id;
33656                         p.css = p.attr = "";
33657                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33658                         if(p.value == undefined || p.value === "") {
33659                             p.value = "&#160;";
33660                         }
33661                         //Roo.log(c);
33662                          if(c.has_editor){
33663                             p.css += ' x-grid-editable-cell';
33664                         }
33665                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33666                             p.css += ' x-grid-dirty-cell' 
33667                         }
33668                         
33669                         var markup = ct.apply(p);
33670                         if(!c.locked){
33671                             cb[cb.length] = markup;
33672                         }else{
33673                             lcb[lcb.length] = markup;
33674                         }
33675                     }
33676                     var alt = [];
33677                     if(stripe && ((rowIndex+1) % 2 == 0)){
33678                         alt.push( "x-grid-row-alt");
33679                     }
33680                     if(r.dirty){
33681                         alt.push(" x-grid-dirty-row");
33682                     }
33683                     rp.cells = lcb;
33684                     if(this.getRowClass){
33685                         alt.push( this.getRowClass(r, rowIndex));
33686                     }
33687                     if (hasListener) {
33688                         rowcfg = {
33689                              
33690                             record: r,
33691                             rowIndex : rowIndex,
33692                             rowClass : ''
33693                         };
33694                         this.grid.fireEvent('rowclass', this, rowcfg);
33695                         alt.push(rowcfg.rowClass);
33696                     }
33697                     
33698                     rp.alt = alt.join(" ");
33699                     rp.cells = lcb.join("");
33700                     lbuf[lbuf.length] = rt.apply(rp);
33701                     rp.cells = cb.join("");
33702                     buf[buf.length] =  rt.apply(rp);
33703                 }
33704                 return [lbuf.join(""), buf.join("")];
33705             },
33706
33707     renderBody : function(){
33708         var markup = this.renderRows();
33709         var bt = this.templates.body;
33710         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33711     },
33712
33713     /**
33714      * Refreshes the grid
33715      * @param {Boolean} headersToo
33716      */
33717     refresh : function(headersToo){
33718         this.fireEvent("beforerefresh", this);
33719         this.grid.stopEditing();
33720         var result = this.renderBody();
33721         this.lockedBody.update(result[0]);
33722         this.mainBody.update(result[1]);
33723         if(headersToo === true){
33724             this.updateHeaders();
33725             this.updateColumns();
33726             this.updateSplitters();
33727             this.updateHeaderSortState();
33728         }
33729         this.syncRowHeights();
33730         this.layout();
33731         this.fireEvent("refresh", this);
33732     },
33733
33734     handleColumnMove : function(cm, oldIndex, newIndex){
33735         this.indexMap = null;
33736         var s = this.getScrollState();
33737         this.refresh(true);
33738         this.restoreScroll(s);
33739         this.afterMove(newIndex);
33740     },
33741
33742     afterMove : function(colIndex){
33743         if(this.enableMoveAnim && Roo.enableFx){
33744             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33745         }
33746         // if multisort - fix sortOrder, and reload..
33747         if (this.grid.dataSource.multiSort) {
33748             // the we can call sort again..
33749             var dm = this.grid.dataSource;
33750             var cm = this.grid.colModel;
33751             var so = [];
33752             for(var i = 0; i < cm.config.length; i++ ) {
33753                 
33754                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33755                     continue; // dont' bother, it's not in sort list or being set.
33756                 }
33757                 
33758                 so.push(cm.config[i].dataIndex);
33759             };
33760             dm.sortOrder = so;
33761             dm.load(dm.lastOptions);
33762             
33763             
33764         }
33765         
33766     },
33767
33768     updateCell : function(dm, rowIndex, dataIndex){
33769         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33770         if(typeof colIndex == "undefined"){ // not present in grid
33771             return;
33772         }
33773         var cm = this.grid.colModel;
33774         var cell = this.getCell(rowIndex, colIndex);
33775         var cellText = this.getCellText(rowIndex, colIndex);
33776
33777         var p = {
33778             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33779             id : cm.getColumnId(colIndex),
33780             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33781         };
33782         var renderer = cm.getRenderer(colIndex);
33783         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33784         if(typeof val == "undefined" || val === "") {
33785             val = "&#160;";
33786         }
33787         cellText.innerHTML = val;
33788         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33789         this.syncRowHeights(rowIndex, rowIndex);
33790     },
33791
33792     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33793         var maxWidth = 0;
33794         if(this.grid.autoSizeHeaders){
33795             var h = this.getHeaderCellMeasure(colIndex);
33796             maxWidth = Math.max(maxWidth, h.scrollWidth);
33797         }
33798         var tb, index;
33799         if(this.cm.isLocked(colIndex)){
33800             tb = this.getLockedTable();
33801             index = colIndex;
33802         }else{
33803             tb = this.getBodyTable();
33804             index = colIndex - this.cm.getLockedCount();
33805         }
33806         if(tb && tb.rows){
33807             var rows = tb.rows;
33808             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33809             for(var i = 0; i < stopIndex; i++){
33810                 var cell = rows[i].childNodes[index].firstChild;
33811                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33812             }
33813         }
33814         return maxWidth + /*margin for error in IE*/ 5;
33815     },
33816     /**
33817      * Autofit a column to its content.
33818      * @param {Number} colIndex
33819      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33820      */
33821      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33822          if(this.cm.isHidden(colIndex)){
33823              return; // can't calc a hidden column
33824          }
33825         if(forceMinSize){
33826             var cid = this.cm.getColumnId(colIndex);
33827             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33828            if(this.grid.autoSizeHeaders){
33829                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33830            }
33831         }
33832         var newWidth = this.calcColumnWidth(colIndex);
33833         this.cm.setColumnWidth(colIndex,
33834             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33835         if(!suppressEvent){
33836             this.grid.fireEvent("columnresize", colIndex, newWidth);
33837         }
33838     },
33839
33840     /**
33841      * Autofits all columns to their content and then expands to fit any extra space in the grid
33842      */
33843      autoSizeColumns : function(){
33844         var cm = this.grid.colModel;
33845         var colCount = cm.getColumnCount();
33846         for(var i = 0; i < colCount; i++){
33847             this.autoSizeColumn(i, true, true);
33848         }
33849         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33850             this.fitColumns();
33851         }else{
33852             this.updateColumns();
33853             this.layout();
33854         }
33855     },
33856
33857     /**
33858      * Autofits all columns to the grid's width proportionate with their current size
33859      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33860      */
33861     fitColumns : function(reserveScrollSpace){
33862         var cm = this.grid.colModel;
33863         var colCount = cm.getColumnCount();
33864         var cols = [];
33865         var width = 0;
33866         var i, w;
33867         for (i = 0; i < colCount; i++){
33868             if(!cm.isHidden(i) && !cm.isFixed(i)){
33869                 w = cm.getColumnWidth(i);
33870                 cols.push(i);
33871                 cols.push(w);
33872                 width += w;
33873             }
33874         }
33875         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33876         if(reserveScrollSpace){
33877             avail -= 17;
33878         }
33879         var frac = (avail - cm.getTotalWidth())/width;
33880         while (cols.length){
33881             w = cols.pop();
33882             i = cols.pop();
33883             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33884         }
33885         this.updateColumns();
33886         this.layout();
33887     },
33888
33889     onRowSelect : function(rowIndex){
33890         var row = this.getRowComposite(rowIndex);
33891         row.addClass("x-grid-row-selected");
33892     },
33893
33894     onRowDeselect : function(rowIndex){
33895         var row = this.getRowComposite(rowIndex);
33896         row.removeClass("x-grid-row-selected");
33897     },
33898
33899     onCellSelect : function(row, col){
33900         var cell = this.getCell(row, col);
33901         if(cell){
33902             Roo.fly(cell).addClass("x-grid-cell-selected");
33903         }
33904     },
33905
33906     onCellDeselect : function(row, col){
33907         var cell = this.getCell(row, col);
33908         if(cell){
33909             Roo.fly(cell).removeClass("x-grid-cell-selected");
33910         }
33911     },
33912
33913     updateHeaderSortState : function(){
33914         
33915         // sort state can be single { field: xxx, direction : yyy}
33916         // or   { xxx=>ASC , yyy : DESC ..... }
33917         
33918         var mstate = {};
33919         if (!this.ds.multiSort) { 
33920             var state = this.ds.getSortState();
33921             if(!state){
33922                 return;
33923             }
33924             mstate[state.field] = state.direction;
33925             // FIXME... - this is not used here.. but might be elsewhere..
33926             this.sortState = state;
33927             
33928         } else {
33929             mstate = this.ds.sortToggle;
33930         }
33931         //remove existing sort classes..
33932         
33933         var sc = this.sortClasses;
33934         var hds = this.el.select(this.headerSelector).removeClass(sc);
33935         
33936         for(var f in mstate) {
33937         
33938             var sortColumn = this.cm.findColumnIndex(f);
33939             
33940             if(sortColumn != -1){
33941                 var sortDir = mstate[f];        
33942                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33943             }
33944         }
33945         
33946          
33947         
33948     },
33949
33950
33951     handleHeaderClick : function(g, index,e){
33952         
33953         Roo.log("header click");
33954         
33955         if (Roo.isTouch) {
33956             // touch events on header are handled by context
33957             this.handleHdCtx(g,index,e);
33958             return;
33959         }
33960         
33961         
33962         if(this.headersDisabled){
33963             return;
33964         }
33965         var dm = g.dataSource, cm = g.colModel;
33966         if(!cm.isSortable(index)){
33967             return;
33968         }
33969         g.stopEditing();
33970         
33971         if (dm.multiSort) {
33972             // update the sortOrder
33973             var so = [];
33974             for(var i = 0; i < cm.config.length; i++ ) {
33975                 
33976                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33977                     continue; // dont' bother, it's not in sort list or being set.
33978                 }
33979                 
33980                 so.push(cm.config[i].dataIndex);
33981             };
33982             dm.sortOrder = so;
33983         }
33984         
33985         
33986         dm.sort(cm.getDataIndex(index));
33987     },
33988
33989
33990     destroy : function(){
33991         if(this.colMenu){
33992             this.colMenu.removeAll();
33993             Roo.menu.MenuMgr.unregister(this.colMenu);
33994             this.colMenu.getEl().remove();
33995             delete this.colMenu;
33996         }
33997         if(this.hmenu){
33998             this.hmenu.removeAll();
33999             Roo.menu.MenuMgr.unregister(this.hmenu);
34000             this.hmenu.getEl().remove();
34001             delete this.hmenu;
34002         }
34003         if(this.grid.enableColumnMove){
34004             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34005             if(dds){
34006                 for(var dd in dds){
34007                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34008                         var elid = dds[dd].dragElId;
34009                         dds[dd].unreg();
34010                         Roo.get(elid).remove();
34011                     } else if(dds[dd].config.isTarget){
34012                         dds[dd].proxyTop.remove();
34013                         dds[dd].proxyBottom.remove();
34014                         dds[dd].unreg();
34015                     }
34016                     if(Roo.dd.DDM.locationCache[dd]){
34017                         delete Roo.dd.DDM.locationCache[dd];
34018                     }
34019                 }
34020                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34021             }
34022         }
34023         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34024         this.bind(null, null);
34025         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34026     },
34027
34028     handleLockChange : function(){
34029         this.refresh(true);
34030     },
34031
34032     onDenyColumnLock : function(){
34033
34034     },
34035
34036     onDenyColumnHide : function(){
34037
34038     },
34039
34040     handleHdMenuClick : function(item){
34041         var index = this.hdCtxIndex;
34042         var cm = this.cm, ds = this.ds;
34043         switch(item.id){
34044             case "asc":
34045                 ds.sort(cm.getDataIndex(index), "ASC");
34046                 break;
34047             case "desc":
34048                 ds.sort(cm.getDataIndex(index), "DESC");
34049                 break;
34050             case "lock":
34051                 var lc = cm.getLockedCount();
34052                 if(cm.getColumnCount(true) <= lc+1){
34053                     this.onDenyColumnLock();
34054                     return;
34055                 }
34056                 if(lc != index){
34057                     cm.setLocked(index, true, true);
34058                     cm.moveColumn(index, lc);
34059                     this.grid.fireEvent("columnmove", index, lc);
34060                 }else{
34061                     cm.setLocked(index, true);
34062                 }
34063             break;
34064             case "unlock":
34065                 var lc = cm.getLockedCount();
34066                 if((lc-1) != index){
34067                     cm.setLocked(index, false, true);
34068                     cm.moveColumn(index, lc-1);
34069                     this.grid.fireEvent("columnmove", index, lc-1);
34070                 }else{
34071                     cm.setLocked(index, false);
34072                 }
34073             break;
34074             case 'wider': // used to expand cols on touch..
34075             case 'narrow':
34076                 var cw = cm.getColumnWidth(index);
34077                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34078                 cw = Math.max(0, cw);
34079                 cw = Math.min(cw,4000);
34080                 cm.setColumnWidth(index, cw);
34081                 break;
34082                 
34083             default:
34084                 index = cm.getIndexById(item.id.substr(4));
34085                 if(index != -1){
34086                     if(item.checked && cm.getColumnCount(true) <= 1){
34087                         this.onDenyColumnHide();
34088                         return false;
34089                     }
34090                     cm.setHidden(index, item.checked);
34091                 }
34092         }
34093         return true;
34094     },
34095
34096     beforeColMenuShow : function(){
34097         var cm = this.cm,  colCount = cm.getColumnCount();
34098         this.colMenu.removeAll();
34099         for(var i = 0; i < colCount; i++){
34100             this.colMenu.add(new Roo.menu.CheckItem({
34101                 id: "col-"+cm.getColumnId(i),
34102                 text: cm.getColumnHeader(i),
34103                 checked: !cm.isHidden(i),
34104                 hideOnClick:false
34105             }));
34106         }
34107     },
34108
34109     handleHdCtx : function(g, index, e){
34110         e.stopEvent();
34111         var hd = this.getHeaderCell(index);
34112         this.hdCtxIndex = index;
34113         var ms = this.hmenu.items, cm = this.cm;
34114         ms.get("asc").setDisabled(!cm.isSortable(index));
34115         ms.get("desc").setDisabled(!cm.isSortable(index));
34116         if(this.grid.enableColLock !== false){
34117             ms.get("lock").setDisabled(cm.isLocked(index));
34118             ms.get("unlock").setDisabled(!cm.isLocked(index));
34119         }
34120         this.hmenu.show(hd, "tl-bl");
34121     },
34122
34123     handleHdOver : function(e){
34124         var hd = this.findHeaderCell(e.getTarget());
34125         if(hd && !this.headersDisabled){
34126             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34127                this.fly(hd).addClass("x-grid-hd-over");
34128             }
34129         }
34130     },
34131
34132     handleHdOut : function(e){
34133         var hd = this.findHeaderCell(e.getTarget());
34134         if(hd){
34135             this.fly(hd).removeClass("x-grid-hd-over");
34136         }
34137     },
34138
34139     handleSplitDblClick : function(e, t){
34140         var i = this.getCellIndex(t);
34141         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34142             this.autoSizeColumn(i, true);
34143             this.layout();
34144         }
34145     },
34146
34147     render : function(){
34148
34149         var cm = this.cm;
34150         var colCount = cm.getColumnCount();
34151
34152         if(this.grid.monitorWindowResize === true){
34153             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34154         }
34155         var header = this.renderHeaders();
34156         var body = this.templates.body.apply({rows:""});
34157         var html = this.templates.master.apply({
34158             lockedBody: body,
34159             body: body,
34160             lockedHeader: header[0],
34161             header: header[1]
34162         });
34163
34164         //this.updateColumns();
34165
34166         this.grid.getGridEl().dom.innerHTML = html;
34167
34168         this.initElements();
34169         
34170         // a kludge to fix the random scolling effect in webkit
34171         this.el.on("scroll", function() {
34172             this.el.dom.scrollTop=0; // hopefully not recursive..
34173         },this);
34174
34175         this.scroller.on("scroll", this.handleScroll, this);
34176         this.lockedBody.on("mousewheel", this.handleWheel, this);
34177         this.mainBody.on("mousewheel", this.handleWheel, this);
34178
34179         this.mainHd.on("mouseover", this.handleHdOver, this);
34180         this.mainHd.on("mouseout", this.handleHdOut, this);
34181         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34182                 {delegate: "."+this.splitClass});
34183
34184         this.lockedHd.on("mouseover", this.handleHdOver, this);
34185         this.lockedHd.on("mouseout", this.handleHdOut, this);
34186         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34187                 {delegate: "."+this.splitClass});
34188
34189         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34190             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34191         }
34192
34193         this.updateSplitters();
34194
34195         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34196             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34197             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34198         }
34199
34200         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34201             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34202             this.hmenu.add(
34203                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34204                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34205             );
34206             if(this.grid.enableColLock !== false){
34207                 this.hmenu.add('-',
34208                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34209                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34210                 );
34211             }
34212             if (Roo.isTouch) {
34213                  this.hmenu.add('-',
34214                     {id:"wider", text: this.columnsWiderText},
34215                     {id:"narrow", text: this.columnsNarrowText }
34216                 );
34217                 
34218                  
34219             }
34220             
34221             if(this.grid.enableColumnHide !== false){
34222
34223                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34224                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34225                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34226
34227                 this.hmenu.add('-',
34228                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34229                 );
34230             }
34231             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34232
34233             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34234         }
34235
34236         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34237             this.dd = new Roo.grid.GridDragZone(this.grid, {
34238                 ddGroup : this.grid.ddGroup || 'GridDD'
34239             });
34240             
34241         }
34242
34243         /*
34244         for(var i = 0; i < colCount; i++){
34245             if(cm.isHidden(i)){
34246                 this.hideColumn(i);
34247             }
34248             if(cm.config[i].align){
34249                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34250                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34251             }
34252         }*/
34253         
34254         this.updateHeaderSortState();
34255
34256         this.beforeInitialResize();
34257         this.layout(true);
34258
34259         // two part rendering gives faster view to the user
34260         this.renderPhase2.defer(1, this);
34261     },
34262
34263     renderPhase2 : function(){
34264         // render the rows now
34265         this.refresh();
34266         if(this.grid.autoSizeColumns){
34267             this.autoSizeColumns();
34268         }
34269     },
34270
34271     beforeInitialResize : function(){
34272
34273     },
34274
34275     onColumnSplitterMoved : function(i, w){
34276         this.userResized = true;
34277         var cm = this.grid.colModel;
34278         cm.setColumnWidth(i, w, true);
34279         var cid = cm.getColumnId(i);
34280         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34281         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34282         this.updateSplitters();
34283         this.layout();
34284         this.grid.fireEvent("columnresize", i, w);
34285     },
34286
34287     syncRowHeights : function(startIndex, endIndex){
34288         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34289             startIndex = startIndex || 0;
34290             var mrows = this.getBodyTable().rows;
34291             var lrows = this.getLockedTable().rows;
34292             var len = mrows.length-1;
34293             endIndex = Math.min(endIndex || len, len);
34294             for(var i = startIndex; i <= endIndex; i++){
34295                 var m = mrows[i], l = lrows[i];
34296                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34297                 m.style.height = l.style.height = h + "px";
34298             }
34299         }
34300     },
34301
34302     layout : function(initialRender, is2ndPass){
34303         var g = this.grid;
34304         var auto = g.autoHeight;
34305         var scrollOffset = 16;
34306         var c = g.getGridEl(), cm = this.cm,
34307                 expandCol = g.autoExpandColumn,
34308                 gv = this;
34309         //c.beginMeasure();
34310
34311         if(!c.dom.offsetWidth){ // display:none?
34312             if(initialRender){
34313                 this.lockedWrap.show();
34314                 this.mainWrap.show();
34315             }
34316             return;
34317         }
34318
34319         var hasLock = this.cm.isLocked(0);
34320
34321         var tbh = this.headerPanel.getHeight();
34322         var bbh = this.footerPanel.getHeight();
34323
34324         if(auto){
34325             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34326             var newHeight = ch + c.getBorderWidth("tb");
34327             if(g.maxHeight){
34328                 newHeight = Math.min(g.maxHeight, newHeight);
34329             }
34330             c.setHeight(newHeight);
34331         }
34332
34333         if(g.autoWidth){
34334             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34335         }
34336
34337         var s = this.scroller;
34338
34339         var csize = c.getSize(true);
34340
34341         this.el.setSize(csize.width, csize.height);
34342
34343         this.headerPanel.setWidth(csize.width);
34344         this.footerPanel.setWidth(csize.width);
34345
34346         var hdHeight = this.mainHd.getHeight();
34347         var vw = csize.width;
34348         var vh = csize.height - (tbh + bbh);
34349
34350         s.setSize(vw, vh);
34351
34352         var bt = this.getBodyTable();
34353         
34354         if(cm.getLockedCount() == cm.config.length){
34355             bt = this.getLockedTable();
34356         }
34357         
34358         var ltWidth = hasLock ?
34359                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34360
34361         var scrollHeight = bt.offsetHeight;
34362         var scrollWidth = ltWidth + bt.offsetWidth;
34363         var vscroll = false, hscroll = false;
34364
34365         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34366
34367         var lw = this.lockedWrap, mw = this.mainWrap;
34368         var lb = this.lockedBody, mb = this.mainBody;
34369
34370         setTimeout(function(){
34371             var t = s.dom.offsetTop;
34372             var w = s.dom.clientWidth,
34373                 h = s.dom.clientHeight;
34374
34375             lw.setTop(t);
34376             lw.setSize(ltWidth, h);
34377
34378             mw.setLeftTop(ltWidth, t);
34379             mw.setSize(w-ltWidth, h);
34380
34381             lb.setHeight(h-hdHeight);
34382             mb.setHeight(h-hdHeight);
34383
34384             if(is2ndPass !== true && !gv.userResized && expandCol){
34385                 // high speed resize without full column calculation
34386                 
34387                 var ci = cm.getIndexById(expandCol);
34388                 if (ci < 0) {
34389                     ci = cm.findColumnIndex(expandCol);
34390                 }
34391                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34392                 var expandId = cm.getColumnId(ci);
34393                 var  tw = cm.getTotalWidth(false);
34394                 var currentWidth = cm.getColumnWidth(ci);
34395                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34396                 if(currentWidth != cw){
34397                     cm.setColumnWidth(ci, cw, true);
34398                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34399                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34400                     gv.updateSplitters();
34401                     gv.layout(false, true);
34402                 }
34403             }
34404
34405             if(initialRender){
34406                 lw.show();
34407                 mw.show();
34408             }
34409             //c.endMeasure();
34410         }, 10);
34411     },
34412
34413     onWindowResize : function(){
34414         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34415             return;
34416         }
34417         this.layout();
34418     },
34419
34420     appendFooter : function(parentEl){
34421         return null;
34422     },
34423
34424     sortAscText : "Sort Ascending",
34425     sortDescText : "Sort Descending",
34426     lockText : "Lock Column",
34427     unlockText : "Unlock Column",
34428     columnsText : "Columns",
34429  
34430     columnsWiderText : "Wider",
34431     columnsNarrowText : "Thinner"
34432 });
34433
34434
34435 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34436     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34437     this.proxy.el.addClass('x-grid3-col-dd');
34438 };
34439
34440 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34441     handleMouseDown : function(e){
34442
34443     },
34444
34445     callHandleMouseDown : function(e){
34446         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34447     }
34448 });
34449 /*
34450  * Based on:
34451  * Ext JS Library 1.1.1
34452  * Copyright(c) 2006-2007, Ext JS, LLC.
34453  *
34454  * Originally Released Under LGPL - original licence link has changed is not relivant.
34455  *
34456  * Fork - LGPL
34457  * <script type="text/javascript">
34458  */
34459  
34460 // private
34461 // This is a support class used internally by the Grid components
34462 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34463     this.grid = grid;
34464     this.view = grid.getView();
34465     this.proxy = this.view.resizeProxy;
34466     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34467         "gridSplitters" + this.grid.getGridEl().id, {
34468         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34469     });
34470     this.setHandleElId(Roo.id(hd));
34471     this.setOuterHandleElId(Roo.id(hd2));
34472     this.scroll = false;
34473 };
34474 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34475     fly: Roo.Element.fly,
34476
34477     b4StartDrag : function(x, y){
34478         this.view.headersDisabled = true;
34479         this.proxy.setHeight(this.view.mainWrap.getHeight());
34480         var w = this.cm.getColumnWidth(this.cellIndex);
34481         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34482         this.resetConstraints();
34483         this.setXConstraint(minw, 1000);
34484         this.setYConstraint(0, 0);
34485         this.minX = x - minw;
34486         this.maxX = x + 1000;
34487         this.startPos = x;
34488         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34489     },
34490
34491
34492     handleMouseDown : function(e){
34493         ev = Roo.EventObject.setEvent(e);
34494         var t = this.fly(ev.getTarget());
34495         if(t.hasClass("x-grid-split")){
34496             this.cellIndex = this.view.getCellIndex(t.dom);
34497             this.split = t.dom;
34498             this.cm = this.grid.colModel;
34499             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34500                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34501             }
34502         }
34503     },
34504
34505     endDrag : function(e){
34506         this.view.headersDisabled = false;
34507         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34508         var diff = endX - this.startPos;
34509         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34510     },
34511
34512     autoOffset : function(){
34513         this.setDelta(0,0);
34514     }
34515 });/*
34516  * Based on:
34517  * Ext JS Library 1.1.1
34518  * Copyright(c) 2006-2007, Ext JS, LLC.
34519  *
34520  * Originally Released Under LGPL - original licence link has changed is not relivant.
34521  *
34522  * Fork - LGPL
34523  * <script type="text/javascript">
34524  */
34525  
34526 // private
34527 // This is a support class used internally by the Grid components
34528 Roo.grid.GridDragZone = function(grid, config){
34529     this.view = grid.getView();
34530     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34531     if(this.view.lockedBody){
34532         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34533         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34534     }
34535     this.scroll = false;
34536     this.grid = grid;
34537     this.ddel = document.createElement('div');
34538     this.ddel.className = 'x-grid-dd-wrap';
34539 };
34540
34541 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34542     ddGroup : "GridDD",
34543
34544     getDragData : function(e){
34545         var t = Roo.lib.Event.getTarget(e);
34546         var rowIndex = this.view.findRowIndex(t);
34547         var sm = this.grid.selModel;
34548             
34549         //Roo.log(rowIndex);
34550         
34551         if (sm.getSelectedCell) {
34552             // cell selection..
34553             if (!sm.getSelectedCell()) {
34554                 return false;
34555             }
34556             if (rowIndex != sm.getSelectedCell()[0]) {
34557                 return false;
34558             }
34559         
34560         }
34561         
34562         if(rowIndex !== false){
34563             
34564             // if editorgrid.. 
34565             
34566             
34567             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34568                
34569             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34570               //  
34571             //}
34572             if (e.hasModifier()){
34573                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34574             }
34575             
34576             Roo.log("getDragData");
34577             
34578             return {
34579                 grid: this.grid,
34580                 ddel: this.ddel,
34581                 rowIndex: rowIndex,
34582                 selections:sm.getSelections ? sm.getSelections() : (
34583                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34584                 )
34585             };
34586         }
34587         return false;
34588     },
34589
34590     onInitDrag : function(e){
34591         var data = this.dragData;
34592         this.ddel.innerHTML = this.grid.getDragDropText();
34593         this.proxy.update(this.ddel);
34594         // fire start drag?
34595     },
34596
34597     afterRepair : function(){
34598         this.dragging = false;
34599     },
34600
34601     getRepairXY : function(e, data){
34602         return false;
34603     },
34604
34605     onEndDrag : function(data, e){
34606         // fire end drag?
34607     },
34608
34609     onValidDrop : function(dd, e, id){
34610         // fire drag drop?
34611         this.hideProxy();
34612     },
34613
34614     beforeInvalidDrop : function(e, id){
34615
34616     }
34617 });/*
34618  * Based on:
34619  * Ext JS Library 1.1.1
34620  * Copyright(c) 2006-2007, Ext JS, LLC.
34621  *
34622  * Originally Released Under LGPL - original licence link has changed is not relivant.
34623  *
34624  * Fork - LGPL
34625  * <script type="text/javascript">
34626  */
34627  
34628
34629 /**
34630  * @class Roo.grid.ColumnModel
34631  * @extends Roo.util.Observable
34632  * This is the default implementation of a ColumnModel used by the Grid. It defines
34633  * the columns in the grid.
34634  * <br>Usage:<br>
34635  <pre><code>
34636  var colModel = new Roo.grid.ColumnModel([
34637         {header: "Ticker", width: 60, sortable: true, locked: true},
34638         {header: "Company Name", width: 150, sortable: true},
34639         {header: "Market Cap.", width: 100, sortable: true},
34640         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34641         {header: "Employees", width: 100, sortable: true, resizable: false}
34642  ]);
34643  </code></pre>
34644  * <p>
34645  
34646  * The config options listed for this class are options which may appear in each
34647  * individual column definition.
34648  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34649  * @constructor
34650  * @param {Object} config An Array of column config objects. See this class's
34651  * config objects for details.
34652 */
34653 Roo.grid.ColumnModel = function(config){
34654         /**
34655      * The config passed into the constructor
34656      */
34657     this.config = config;
34658     this.lookup = {};
34659
34660     // if no id, create one
34661     // if the column does not have a dataIndex mapping,
34662     // map it to the order it is in the config
34663     for(var i = 0, len = config.length; i < len; i++){
34664         var c = config[i];
34665         if(typeof c.dataIndex == "undefined"){
34666             c.dataIndex = i;
34667         }
34668         if(typeof c.renderer == "string"){
34669             c.renderer = Roo.util.Format[c.renderer];
34670         }
34671         if(typeof c.id == "undefined"){
34672             c.id = Roo.id();
34673         }
34674         if(c.editor && c.editor.xtype){
34675             c.editor  = Roo.factory(c.editor, Roo.grid);
34676         }
34677         if(c.editor && c.editor.isFormField){
34678             c.editor = new Roo.grid.GridEditor(c.editor);
34679         }
34680         this.lookup[c.id] = c;
34681     }
34682
34683     /**
34684      * The width of columns which have no width specified (defaults to 100)
34685      * @type Number
34686      */
34687     this.defaultWidth = 100;
34688
34689     /**
34690      * Default sortable of columns which have no sortable specified (defaults to false)
34691      * @type Boolean
34692      */
34693     this.defaultSortable = false;
34694
34695     this.addEvents({
34696         /**
34697              * @event widthchange
34698              * Fires when the width of a column changes.
34699              * @param {ColumnModel} this
34700              * @param {Number} columnIndex The column index
34701              * @param {Number} newWidth The new width
34702              */
34703             "widthchange": true,
34704         /**
34705              * @event headerchange
34706              * Fires when the text of a header changes.
34707              * @param {ColumnModel} this
34708              * @param {Number} columnIndex The column index
34709              * @param {Number} newText The new header text
34710              */
34711             "headerchange": true,
34712         /**
34713              * @event hiddenchange
34714              * Fires when a column is hidden or "unhidden".
34715              * @param {ColumnModel} this
34716              * @param {Number} columnIndex The column index
34717              * @param {Boolean} hidden true if hidden, false otherwise
34718              */
34719             "hiddenchange": true,
34720             /**
34721          * @event columnmoved
34722          * Fires when a column is moved.
34723          * @param {ColumnModel} this
34724          * @param {Number} oldIndex
34725          * @param {Number} newIndex
34726          */
34727         "columnmoved" : true,
34728         /**
34729          * @event columlockchange
34730          * Fires when a column's locked state is changed
34731          * @param {ColumnModel} this
34732          * @param {Number} colIndex
34733          * @param {Boolean} locked true if locked
34734          */
34735         "columnlockchange" : true
34736     });
34737     Roo.grid.ColumnModel.superclass.constructor.call(this);
34738 };
34739 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34740     /**
34741      * @cfg {String} header The header text to display in the Grid view.
34742      */
34743     /**
34744      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34745      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34746      * specified, the column's index is used as an index into the Record's data Array.
34747      */
34748     /**
34749      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34750      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34751      */
34752     /**
34753      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34754      * Defaults to the value of the {@link #defaultSortable} property.
34755      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34756      */
34757     /**
34758      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34759      */
34760     /**
34761      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34762      */
34763     /**
34764      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34765      */
34766     /**
34767      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34768      */
34769     /**
34770      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34771      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34772      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34773      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34774      */
34775        /**
34776      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34777      */
34778     /**
34779      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34780      */
34781     /**
34782      * @cfg {String} cursor (Optional)
34783      */
34784     /**
34785      * @cfg {String} tooltip (Optional)
34786      */
34787     /**
34788      * @cfg {Number} xs (Optional)
34789      */
34790     /**
34791      * @cfg {Number} sm (Optional)
34792      */
34793     /**
34794      * @cfg {Number} md (Optional)
34795      */
34796     /**
34797      * @cfg {Number} lg (Optional)
34798      */
34799     /**
34800      * Returns the id of the column at the specified index.
34801      * @param {Number} index The column index
34802      * @return {String} the id
34803      */
34804     getColumnId : function(index){
34805         return this.config[index].id;
34806     },
34807
34808     /**
34809      * Returns the column for a specified id.
34810      * @param {String} id The column id
34811      * @return {Object} the column
34812      */
34813     getColumnById : function(id){
34814         return this.lookup[id];
34815     },
34816
34817     
34818     /**
34819      * Returns the column for a specified dataIndex.
34820      * @param {String} dataIndex The column dataIndex
34821      * @return {Object|Boolean} the column or false if not found
34822      */
34823     getColumnByDataIndex: function(dataIndex){
34824         var index = this.findColumnIndex(dataIndex);
34825         return index > -1 ? this.config[index] : false;
34826     },
34827     
34828     /**
34829      * Returns the index for a specified column id.
34830      * @param {String} id The column id
34831      * @return {Number} the index, or -1 if not found
34832      */
34833     getIndexById : function(id){
34834         for(var i = 0, len = this.config.length; i < len; i++){
34835             if(this.config[i].id == id){
34836                 return i;
34837             }
34838         }
34839         return -1;
34840     },
34841     
34842     /**
34843      * Returns the index for a specified column dataIndex.
34844      * @param {String} dataIndex The column dataIndex
34845      * @return {Number} the index, or -1 if not found
34846      */
34847     
34848     findColumnIndex : function(dataIndex){
34849         for(var i = 0, len = this.config.length; i < len; i++){
34850             if(this.config[i].dataIndex == dataIndex){
34851                 return i;
34852             }
34853         }
34854         return -1;
34855     },
34856     
34857     
34858     moveColumn : function(oldIndex, newIndex){
34859         var c = this.config[oldIndex];
34860         this.config.splice(oldIndex, 1);
34861         this.config.splice(newIndex, 0, c);
34862         this.dataMap = null;
34863         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34864     },
34865
34866     isLocked : function(colIndex){
34867         return this.config[colIndex].locked === true;
34868     },
34869
34870     setLocked : function(colIndex, value, suppressEvent){
34871         if(this.isLocked(colIndex) == value){
34872             return;
34873         }
34874         this.config[colIndex].locked = value;
34875         if(!suppressEvent){
34876             this.fireEvent("columnlockchange", this, colIndex, value);
34877         }
34878     },
34879
34880     getTotalLockedWidth : function(){
34881         var totalWidth = 0;
34882         for(var i = 0; i < this.config.length; i++){
34883             if(this.isLocked(i) && !this.isHidden(i)){
34884                 this.totalWidth += this.getColumnWidth(i);
34885             }
34886         }
34887         return totalWidth;
34888     },
34889
34890     getLockedCount : function(){
34891         for(var i = 0, len = this.config.length; i < len; i++){
34892             if(!this.isLocked(i)){
34893                 return i;
34894             }
34895         }
34896         
34897         return this.config.length;
34898     },
34899
34900     /**
34901      * Returns the number of columns.
34902      * @return {Number}
34903      */
34904     getColumnCount : function(visibleOnly){
34905         if(visibleOnly === true){
34906             var c = 0;
34907             for(var i = 0, len = this.config.length; i < len; i++){
34908                 if(!this.isHidden(i)){
34909                     c++;
34910                 }
34911             }
34912             return c;
34913         }
34914         return this.config.length;
34915     },
34916
34917     /**
34918      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34919      * @param {Function} fn
34920      * @param {Object} scope (optional)
34921      * @return {Array} result
34922      */
34923     getColumnsBy : function(fn, scope){
34924         var r = [];
34925         for(var i = 0, len = this.config.length; i < len; i++){
34926             var c = this.config[i];
34927             if(fn.call(scope||this, c, i) === true){
34928                 r[r.length] = c;
34929             }
34930         }
34931         return r;
34932     },
34933
34934     /**
34935      * Returns true if the specified column is sortable.
34936      * @param {Number} col The column index
34937      * @return {Boolean}
34938      */
34939     isSortable : function(col){
34940         if(typeof this.config[col].sortable == "undefined"){
34941             return this.defaultSortable;
34942         }
34943         return this.config[col].sortable;
34944     },
34945
34946     /**
34947      * Returns the rendering (formatting) function defined for the column.
34948      * @param {Number} col The column index.
34949      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34950      */
34951     getRenderer : function(col){
34952         if(!this.config[col].renderer){
34953             return Roo.grid.ColumnModel.defaultRenderer;
34954         }
34955         return this.config[col].renderer;
34956     },
34957
34958     /**
34959      * Sets the rendering (formatting) function for a column.
34960      * @param {Number} col The column index
34961      * @param {Function} fn The function to use to process the cell's raw data
34962      * to return HTML markup for the grid view. The render function is called with
34963      * the following parameters:<ul>
34964      * <li>Data value.</li>
34965      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34966      * <li>css A CSS style string to apply to the table cell.</li>
34967      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34968      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34969      * <li>Row index</li>
34970      * <li>Column index</li>
34971      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34972      */
34973     setRenderer : function(col, fn){
34974         this.config[col].renderer = fn;
34975     },
34976
34977     /**
34978      * Returns the width for the specified column.
34979      * @param {Number} col The column index
34980      * @return {Number}
34981      */
34982     getColumnWidth : function(col){
34983         return this.config[col].width * 1 || this.defaultWidth;
34984     },
34985
34986     /**
34987      * Sets the width for a column.
34988      * @param {Number} col The column index
34989      * @param {Number} width The new width
34990      */
34991     setColumnWidth : function(col, width, suppressEvent){
34992         this.config[col].width = width;
34993         this.totalWidth = null;
34994         if(!suppressEvent){
34995              this.fireEvent("widthchange", this, col, width);
34996         }
34997     },
34998
34999     /**
35000      * Returns the total width of all columns.
35001      * @param {Boolean} includeHidden True to include hidden column widths
35002      * @return {Number}
35003      */
35004     getTotalWidth : function(includeHidden){
35005         if(!this.totalWidth){
35006             this.totalWidth = 0;
35007             for(var i = 0, len = this.config.length; i < len; i++){
35008                 if(includeHidden || !this.isHidden(i)){
35009                     this.totalWidth += this.getColumnWidth(i);
35010                 }
35011             }
35012         }
35013         return this.totalWidth;
35014     },
35015
35016     /**
35017      * Returns the header for the specified column.
35018      * @param {Number} col The column index
35019      * @return {String}
35020      */
35021     getColumnHeader : function(col){
35022         return this.config[col].header;
35023     },
35024
35025     /**
35026      * Sets the header for a column.
35027      * @param {Number} col The column index
35028      * @param {String} header The new header
35029      */
35030     setColumnHeader : function(col, header){
35031         this.config[col].header = header;
35032         this.fireEvent("headerchange", this, col, header);
35033     },
35034
35035     /**
35036      * Returns the tooltip for the specified column.
35037      * @param {Number} col The column index
35038      * @return {String}
35039      */
35040     getColumnTooltip : function(col){
35041             return this.config[col].tooltip;
35042     },
35043     /**
35044      * Sets the tooltip for a column.
35045      * @param {Number} col The column index
35046      * @param {String} tooltip The new tooltip
35047      */
35048     setColumnTooltip : function(col, tooltip){
35049             this.config[col].tooltip = tooltip;
35050     },
35051
35052     /**
35053      * Returns the dataIndex for the specified column.
35054      * @param {Number} col The column index
35055      * @return {Number}
35056      */
35057     getDataIndex : function(col){
35058         return this.config[col].dataIndex;
35059     },
35060
35061     /**
35062      * Sets the dataIndex for a column.
35063      * @param {Number} col The column index
35064      * @param {Number} dataIndex The new dataIndex
35065      */
35066     setDataIndex : function(col, dataIndex){
35067         this.config[col].dataIndex = dataIndex;
35068     },
35069
35070     
35071     
35072     /**
35073      * Returns true if the cell is editable.
35074      * @param {Number} colIndex The column index
35075      * @param {Number} rowIndex The row index - this is nto actually used..?
35076      * @return {Boolean}
35077      */
35078     isCellEditable : function(colIndex, rowIndex){
35079         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35080     },
35081
35082     /**
35083      * Returns the editor defined for the cell/column.
35084      * return false or null to disable editing.
35085      * @param {Number} colIndex The column index
35086      * @param {Number} rowIndex The row index
35087      * @return {Object}
35088      */
35089     getCellEditor : function(colIndex, rowIndex){
35090         return this.config[colIndex].editor;
35091     },
35092
35093     /**
35094      * Sets if a column is editable.
35095      * @param {Number} col The column index
35096      * @param {Boolean} editable True if the column is editable
35097      */
35098     setEditable : function(col, editable){
35099         this.config[col].editable = editable;
35100     },
35101
35102
35103     /**
35104      * Returns true if the column is hidden.
35105      * @param {Number} colIndex The column index
35106      * @return {Boolean}
35107      */
35108     isHidden : function(colIndex){
35109         return this.config[colIndex].hidden;
35110     },
35111
35112
35113     /**
35114      * Returns true if the column width cannot be changed
35115      */
35116     isFixed : function(colIndex){
35117         return this.config[colIndex].fixed;
35118     },
35119
35120     /**
35121      * Returns true if the column can be resized
35122      * @return {Boolean}
35123      */
35124     isResizable : function(colIndex){
35125         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35126     },
35127     /**
35128      * Sets if a column is hidden.
35129      * @param {Number} colIndex The column index
35130      * @param {Boolean} hidden True if the column is hidden
35131      */
35132     setHidden : function(colIndex, hidden){
35133         this.config[colIndex].hidden = hidden;
35134         this.totalWidth = null;
35135         this.fireEvent("hiddenchange", this, colIndex, hidden);
35136     },
35137
35138     /**
35139      * Sets the editor for a column.
35140      * @param {Number} col The column index
35141      * @param {Object} editor The editor object
35142      */
35143     setEditor : function(col, editor){
35144         this.config[col].editor = editor;
35145     }
35146 });
35147
35148 Roo.grid.ColumnModel.defaultRenderer = function(value)
35149 {
35150     if(typeof value == "object") {
35151         return value;
35152     }
35153         if(typeof value == "string" && value.length < 1){
35154             return "&#160;";
35155         }
35156     
35157         return String.format("{0}", value);
35158 };
35159
35160 // Alias for backwards compatibility
35161 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35162 /*
35163  * Based on:
35164  * Ext JS Library 1.1.1
35165  * Copyright(c) 2006-2007, Ext JS, LLC.
35166  *
35167  * Originally Released Under LGPL - original licence link has changed is not relivant.
35168  *
35169  * Fork - LGPL
35170  * <script type="text/javascript">
35171  */
35172
35173 /**
35174  * @class Roo.grid.AbstractSelectionModel
35175  * @extends Roo.util.Observable
35176  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35177  * implemented by descendant classes.  This class should not be directly instantiated.
35178  * @constructor
35179  */
35180 Roo.grid.AbstractSelectionModel = function(){
35181     this.locked = false;
35182     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35183 };
35184
35185 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35186     /** @ignore Called by the grid automatically. Do not call directly. */
35187     init : function(grid){
35188         this.grid = grid;
35189         this.initEvents();
35190     },
35191
35192     /**
35193      * Locks the selections.
35194      */
35195     lock : function(){
35196         this.locked = true;
35197     },
35198
35199     /**
35200      * Unlocks the selections.
35201      */
35202     unlock : function(){
35203         this.locked = false;
35204     },
35205
35206     /**
35207      * Returns true if the selections are locked.
35208      * @return {Boolean}
35209      */
35210     isLocked : function(){
35211         return this.locked;
35212     }
35213 });/*
35214  * Based on:
35215  * Ext JS Library 1.1.1
35216  * Copyright(c) 2006-2007, Ext JS, LLC.
35217  *
35218  * Originally Released Under LGPL - original licence link has changed is not relivant.
35219  *
35220  * Fork - LGPL
35221  * <script type="text/javascript">
35222  */
35223 /**
35224  * @extends Roo.grid.AbstractSelectionModel
35225  * @class Roo.grid.RowSelectionModel
35226  * The default SelectionModel used by {@link Roo.grid.Grid}.
35227  * It supports multiple selections and keyboard selection/navigation. 
35228  * @constructor
35229  * @param {Object} config
35230  */
35231 Roo.grid.RowSelectionModel = function(config){
35232     Roo.apply(this, config);
35233     this.selections = new Roo.util.MixedCollection(false, function(o){
35234         return o.id;
35235     });
35236
35237     this.last = false;
35238     this.lastActive = false;
35239
35240     this.addEvents({
35241         /**
35242              * @event selectionchange
35243              * Fires when the selection changes
35244              * @param {SelectionModel} this
35245              */
35246             "selectionchange" : true,
35247         /**
35248              * @event afterselectionchange
35249              * Fires after the selection changes (eg. by key press or clicking)
35250              * @param {SelectionModel} this
35251              */
35252             "afterselectionchange" : true,
35253         /**
35254              * @event beforerowselect
35255              * Fires when a row is selected being selected, return false to cancel.
35256              * @param {SelectionModel} this
35257              * @param {Number} rowIndex The selected index
35258              * @param {Boolean} keepExisting False if other selections will be cleared
35259              */
35260             "beforerowselect" : true,
35261         /**
35262              * @event rowselect
35263              * Fires when a row is selected.
35264              * @param {SelectionModel} this
35265              * @param {Number} rowIndex The selected index
35266              * @param {Roo.data.Record} r The record
35267              */
35268             "rowselect" : true,
35269         /**
35270              * @event rowdeselect
35271              * Fires when a row is deselected.
35272              * @param {SelectionModel} this
35273              * @param {Number} rowIndex The selected index
35274              */
35275         "rowdeselect" : true
35276     });
35277     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35278     this.locked = false;
35279 };
35280
35281 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35282     /**
35283      * @cfg {Boolean} singleSelect
35284      * True to allow selection of only one row at a time (defaults to false)
35285      */
35286     singleSelect : false,
35287
35288     // private
35289     initEvents : function(){
35290
35291         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35292             this.grid.on("mousedown", this.handleMouseDown, this);
35293         }else{ // allow click to work like normal
35294             this.grid.on("rowclick", this.handleDragableRowClick, this);
35295         }
35296
35297         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35298             "up" : function(e){
35299                 if(!e.shiftKey){
35300                     this.selectPrevious(e.shiftKey);
35301                 }else if(this.last !== false && this.lastActive !== false){
35302                     var last = this.last;
35303                     this.selectRange(this.last,  this.lastActive-1);
35304                     this.grid.getView().focusRow(this.lastActive);
35305                     if(last !== false){
35306                         this.last = last;
35307                     }
35308                 }else{
35309                     this.selectFirstRow();
35310                 }
35311                 this.fireEvent("afterselectionchange", this);
35312             },
35313             "down" : function(e){
35314                 if(!e.shiftKey){
35315                     this.selectNext(e.shiftKey);
35316                 }else if(this.last !== false && this.lastActive !== false){
35317                     var last = this.last;
35318                     this.selectRange(this.last,  this.lastActive+1);
35319                     this.grid.getView().focusRow(this.lastActive);
35320                     if(last !== false){
35321                         this.last = last;
35322                     }
35323                 }else{
35324                     this.selectFirstRow();
35325                 }
35326                 this.fireEvent("afterselectionchange", this);
35327             },
35328             scope: this
35329         });
35330
35331         var view = this.grid.view;
35332         view.on("refresh", this.onRefresh, this);
35333         view.on("rowupdated", this.onRowUpdated, this);
35334         view.on("rowremoved", this.onRemove, this);
35335     },
35336
35337     // private
35338     onRefresh : function(){
35339         var ds = this.grid.dataSource, i, v = this.grid.view;
35340         var s = this.selections;
35341         s.each(function(r){
35342             if((i = ds.indexOfId(r.id)) != -1){
35343                 v.onRowSelect(i);
35344                 s.add(ds.getAt(i)); // updating the selection relate data
35345             }else{
35346                 s.remove(r);
35347             }
35348         });
35349     },
35350
35351     // private
35352     onRemove : function(v, index, r){
35353         this.selections.remove(r);
35354     },
35355
35356     // private
35357     onRowUpdated : function(v, index, r){
35358         if(this.isSelected(r)){
35359             v.onRowSelect(index);
35360         }
35361     },
35362
35363     /**
35364      * Select records.
35365      * @param {Array} records The records to select
35366      * @param {Boolean} keepExisting (optional) True to keep existing selections
35367      */
35368     selectRecords : function(records, keepExisting){
35369         if(!keepExisting){
35370             this.clearSelections();
35371         }
35372         var ds = this.grid.dataSource;
35373         for(var i = 0, len = records.length; i < len; i++){
35374             this.selectRow(ds.indexOf(records[i]), true);
35375         }
35376     },
35377
35378     /**
35379      * Gets the number of selected rows.
35380      * @return {Number}
35381      */
35382     getCount : function(){
35383         return this.selections.length;
35384     },
35385
35386     /**
35387      * Selects the first row in the grid.
35388      */
35389     selectFirstRow : function(){
35390         this.selectRow(0);
35391     },
35392
35393     /**
35394      * Select the last row.
35395      * @param {Boolean} keepExisting (optional) True to keep existing selections
35396      */
35397     selectLastRow : function(keepExisting){
35398         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35399     },
35400
35401     /**
35402      * Selects the row immediately following the last selected row.
35403      * @param {Boolean} keepExisting (optional) True to keep existing selections
35404      */
35405     selectNext : function(keepExisting){
35406         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35407             this.selectRow(this.last+1, keepExisting);
35408             this.grid.getView().focusRow(this.last);
35409         }
35410     },
35411
35412     /**
35413      * Selects the row that precedes the last selected row.
35414      * @param {Boolean} keepExisting (optional) True to keep existing selections
35415      */
35416     selectPrevious : function(keepExisting){
35417         if(this.last){
35418             this.selectRow(this.last-1, keepExisting);
35419             this.grid.getView().focusRow(this.last);
35420         }
35421     },
35422
35423     /**
35424      * Returns the selected records
35425      * @return {Array} Array of selected records
35426      */
35427     getSelections : function(){
35428         return [].concat(this.selections.items);
35429     },
35430
35431     /**
35432      * Returns the first selected record.
35433      * @return {Record}
35434      */
35435     getSelected : function(){
35436         return this.selections.itemAt(0);
35437     },
35438
35439
35440     /**
35441      * Clears all selections.
35442      */
35443     clearSelections : function(fast){
35444         if(this.locked) {
35445             return;
35446         }
35447         if(fast !== true){
35448             var ds = this.grid.dataSource;
35449             var s = this.selections;
35450             s.each(function(r){
35451                 this.deselectRow(ds.indexOfId(r.id));
35452             }, this);
35453             s.clear();
35454         }else{
35455             this.selections.clear();
35456         }
35457         this.last = false;
35458     },
35459
35460
35461     /**
35462      * Selects all rows.
35463      */
35464     selectAll : function(){
35465         if(this.locked) {
35466             return;
35467         }
35468         this.selections.clear();
35469         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35470             this.selectRow(i, true);
35471         }
35472     },
35473
35474     /**
35475      * Returns True if there is a selection.
35476      * @return {Boolean}
35477      */
35478     hasSelection : function(){
35479         return this.selections.length > 0;
35480     },
35481
35482     /**
35483      * Returns True if the specified row is selected.
35484      * @param {Number/Record} record The record or index of the record to check
35485      * @return {Boolean}
35486      */
35487     isSelected : function(index){
35488         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35489         return (r && this.selections.key(r.id) ? true : false);
35490     },
35491
35492     /**
35493      * Returns True if the specified record id is selected.
35494      * @param {String} id The id of record to check
35495      * @return {Boolean}
35496      */
35497     isIdSelected : function(id){
35498         return (this.selections.key(id) ? true : false);
35499     },
35500
35501     // private
35502     handleMouseDown : function(e, t){
35503         var view = this.grid.getView(), rowIndex;
35504         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35505             return;
35506         };
35507         if(e.shiftKey && this.last !== false){
35508             var last = this.last;
35509             this.selectRange(last, rowIndex, e.ctrlKey);
35510             this.last = last; // reset the last
35511             view.focusRow(rowIndex);
35512         }else{
35513             var isSelected = this.isSelected(rowIndex);
35514             if(e.button !== 0 && isSelected){
35515                 view.focusRow(rowIndex);
35516             }else if(e.ctrlKey && isSelected){
35517                 this.deselectRow(rowIndex);
35518             }else if(!isSelected){
35519                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35520                 view.focusRow(rowIndex);
35521             }
35522         }
35523         this.fireEvent("afterselectionchange", this);
35524     },
35525     // private
35526     handleDragableRowClick :  function(grid, rowIndex, e) 
35527     {
35528         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35529             this.selectRow(rowIndex, false);
35530             grid.view.focusRow(rowIndex);
35531              this.fireEvent("afterselectionchange", this);
35532         }
35533     },
35534     
35535     /**
35536      * Selects multiple rows.
35537      * @param {Array} rows Array of the indexes of the row to select
35538      * @param {Boolean} keepExisting (optional) True to keep existing selections
35539      */
35540     selectRows : function(rows, keepExisting){
35541         if(!keepExisting){
35542             this.clearSelections();
35543         }
35544         for(var i = 0, len = rows.length; i < len; i++){
35545             this.selectRow(rows[i], true);
35546         }
35547     },
35548
35549     /**
35550      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35551      * @param {Number} startRow The index of the first row in the range
35552      * @param {Number} endRow The index of the last row in the range
35553      * @param {Boolean} keepExisting (optional) True to retain existing selections
35554      */
35555     selectRange : function(startRow, endRow, keepExisting){
35556         if(this.locked) {
35557             return;
35558         }
35559         if(!keepExisting){
35560             this.clearSelections();
35561         }
35562         if(startRow <= endRow){
35563             for(var i = startRow; i <= endRow; i++){
35564                 this.selectRow(i, true);
35565             }
35566         }else{
35567             for(var i = startRow; i >= endRow; i--){
35568                 this.selectRow(i, true);
35569             }
35570         }
35571     },
35572
35573     /**
35574      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35575      * @param {Number} startRow The index of the first row in the range
35576      * @param {Number} endRow The index of the last row in the range
35577      */
35578     deselectRange : function(startRow, endRow, preventViewNotify){
35579         if(this.locked) {
35580             return;
35581         }
35582         for(var i = startRow; i <= endRow; i++){
35583             this.deselectRow(i, preventViewNotify);
35584         }
35585     },
35586
35587     /**
35588      * Selects a row.
35589      * @param {Number} row The index of the row to select
35590      * @param {Boolean} keepExisting (optional) True to keep existing selections
35591      */
35592     selectRow : function(index, keepExisting, preventViewNotify){
35593         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35594             return;
35595         }
35596         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35597             if(!keepExisting || this.singleSelect){
35598                 this.clearSelections();
35599             }
35600             var r = this.grid.dataSource.getAt(index);
35601             this.selections.add(r);
35602             this.last = this.lastActive = index;
35603             if(!preventViewNotify){
35604                 this.grid.getView().onRowSelect(index);
35605             }
35606             this.fireEvent("rowselect", this, index, r);
35607             this.fireEvent("selectionchange", this);
35608         }
35609     },
35610
35611     /**
35612      * Deselects a row.
35613      * @param {Number} row The index of the row to deselect
35614      */
35615     deselectRow : function(index, preventViewNotify){
35616         if(this.locked) {
35617             return;
35618         }
35619         if(this.last == index){
35620             this.last = false;
35621         }
35622         if(this.lastActive == index){
35623             this.lastActive = false;
35624         }
35625         var r = this.grid.dataSource.getAt(index);
35626         this.selections.remove(r);
35627         if(!preventViewNotify){
35628             this.grid.getView().onRowDeselect(index);
35629         }
35630         this.fireEvent("rowdeselect", this, index);
35631         this.fireEvent("selectionchange", this);
35632     },
35633
35634     // private
35635     restoreLast : function(){
35636         if(this._last){
35637             this.last = this._last;
35638         }
35639     },
35640
35641     // private
35642     acceptsNav : function(row, col, cm){
35643         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35644     },
35645
35646     // private
35647     onEditorKey : function(field, e){
35648         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35649         if(k == e.TAB){
35650             e.stopEvent();
35651             ed.completeEdit();
35652             if(e.shiftKey){
35653                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35654             }else{
35655                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35656             }
35657         }else if(k == e.ENTER && !e.ctrlKey){
35658             e.stopEvent();
35659             ed.completeEdit();
35660             if(e.shiftKey){
35661                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35662             }else{
35663                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35664             }
35665         }else if(k == e.ESC){
35666             ed.cancelEdit();
35667         }
35668         if(newCell){
35669             g.startEditing(newCell[0], newCell[1]);
35670         }
35671     }
35672 });/*
35673  * Based on:
35674  * Ext JS Library 1.1.1
35675  * Copyright(c) 2006-2007, Ext JS, LLC.
35676  *
35677  * Originally Released Under LGPL - original licence link has changed is not relivant.
35678  *
35679  * Fork - LGPL
35680  * <script type="text/javascript">
35681  */
35682 /**
35683  * @class Roo.grid.CellSelectionModel
35684  * @extends Roo.grid.AbstractSelectionModel
35685  * This class provides the basic implementation for cell selection in a grid.
35686  * @constructor
35687  * @param {Object} config The object containing the configuration of this model.
35688  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35689  */
35690 Roo.grid.CellSelectionModel = function(config){
35691     Roo.apply(this, config);
35692
35693     this.selection = null;
35694
35695     this.addEvents({
35696         /**
35697              * @event beforerowselect
35698              * Fires before a cell is selected.
35699              * @param {SelectionModel} this
35700              * @param {Number} rowIndex The selected row index
35701              * @param {Number} colIndex The selected cell index
35702              */
35703             "beforecellselect" : true,
35704         /**
35705              * @event cellselect
35706              * Fires when a cell is selected.
35707              * @param {SelectionModel} this
35708              * @param {Number} rowIndex The selected row index
35709              * @param {Number} colIndex The selected cell index
35710              */
35711             "cellselect" : true,
35712         /**
35713              * @event selectionchange
35714              * Fires when the active selection changes.
35715              * @param {SelectionModel} this
35716              * @param {Object} selection null for no selection or an object (o) with two properties
35717                 <ul>
35718                 <li>o.record: the record object for the row the selection is in</li>
35719                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35720                 </ul>
35721              */
35722             "selectionchange" : true,
35723         /**
35724              * @event tabend
35725              * Fires when the tab (or enter) was pressed on the last editable cell
35726              * You can use this to trigger add new row.
35727              * @param {SelectionModel} this
35728              */
35729             "tabend" : true,
35730          /**
35731              * @event beforeeditnext
35732              * Fires before the next editable sell is made active
35733              * You can use this to skip to another cell or fire the tabend
35734              *    if you set cell to false
35735              * @param {Object} eventdata object : { cell : [ row, col ] } 
35736              */
35737             "beforeeditnext" : true
35738     });
35739     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35740 };
35741
35742 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35743     
35744     enter_is_tab: false,
35745
35746     /** @ignore */
35747     initEvents : function(){
35748         this.grid.on("mousedown", this.handleMouseDown, this);
35749         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35750         var view = this.grid.view;
35751         view.on("refresh", this.onViewChange, this);
35752         view.on("rowupdated", this.onRowUpdated, this);
35753         view.on("beforerowremoved", this.clearSelections, this);
35754         view.on("beforerowsinserted", this.clearSelections, this);
35755         if(this.grid.isEditor){
35756             this.grid.on("beforeedit", this.beforeEdit,  this);
35757         }
35758     },
35759
35760         //private
35761     beforeEdit : function(e){
35762         this.select(e.row, e.column, false, true, e.record);
35763     },
35764
35765         //private
35766     onRowUpdated : function(v, index, r){
35767         if(this.selection && this.selection.record == r){
35768             v.onCellSelect(index, this.selection.cell[1]);
35769         }
35770     },
35771
35772         //private
35773     onViewChange : function(){
35774         this.clearSelections(true);
35775     },
35776
35777         /**
35778          * Returns the currently selected cell,.
35779          * @return {Array} The selected cell (row, column) or null if none selected.
35780          */
35781     getSelectedCell : function(){
35782         return this.selection ? this.selection.cell : null;
35783     },
35784
35785     /**
35786      * Clears all selections.
35787      * @param {Boolean} true to prevent the gridview from being notified about the change.
35788      */
35789     clearSelections : function(preventNotify){
35790         var s = this.selection;
35791         if(s){
35792             if(preventNotify !== true){
35793                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35794             }
35795             this.selection = null;
35796             this.fireEvent("selectionchange", this, null);
35797         }
35798     },
35799
35800     /**
35801      * Returns true if there is a selection.
35802      * @return {Boolean}
35803      */
35804     hasSelection : function(){
35805         return this.selection ? true : false;
35806     },
35807
35808     /** @ignore */
35809     handleMouseDown : function(e, t){
35810         var v = this.grid.getView();
35811         if(this.isLocked()){
35812             return;
35813         };
35814         var row = v.findRowIndex(t);
35815         var cell = v.findCellIndex(t);
35816         if(row !== false && cell !== false){
35817             this.select(row, cell);
35818         }
35819     },
35820
35821     /**
35822      * Selects a cell.
35823      * @param {Number} rowIndex
35824      * @param {Number} collIndex
35825      */
35826     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35827         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35828             this.clearSelections();
35829             r = r || this.grid.dataSource.getAt(rowIndex);
35830             this.selection = {
35831                 record : r,
35832                 cell : [rowIndex, colIndex]
35833             };
35834             if(!preventViewNotify){
35835                 var v = this.grid.getView();
35836                 v.onCellSelect(rowIndex, colIndex);
35837                 if(preventFocus !== true){
35838                     v.focusCell(rowIndex, colIndex);
35839                 }
35840             }
35841             this.fireEvent("cellselect", this, rowIndex, colIndex);
35842             this.fireEvent("selectionchange", this, this.selection);
35843         }
35844     },
35845
35846         //private
35847     isSelectable : function(rowIndex, colIndex, cm){
35848         return !cm.isHidden(colIndex);
35849     },
35850
35851     /** @ignore */
35852     handleKeyDown : function(e){
35853         //Roo.log('Cell Sel Model handleKeyDown');
35854         if(!e.isNavKeyPress()){
35855             return;
35856         }
35857         var g = this.grid, s = this.selection;
35858         if(!s){
35859             e.stopEvent();
35860             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35861             if(cell){
35862                 this.select(cell[0], cell[1]);
35863             }
35864             return;
35865         }
35866         var sm = this;
35867         var walk = function(row, col, step){
35868             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35869         };
35870         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35871         var newCell;
35872
35873       
35874
35875         switch(k){
35876             case e.TAB:
35877                 // handled by onEditorKey
35878                 if (g.isEditor && g.editing) {
35879                     return;
35880                 }
35881                 if(e.shiftKey) {
35882                     newCell = walk(r, c-1, -1);
35883                 } else {
35884                     newCell = walk(r, c+1, 1);
35885                 }
35886                 break;
35887             
35888             case e.DOWN:
35889                newCell = walk(r+1, c, 1);
35890                 break;
35891             
35892             case e.UP:
35893                 newCell = walk(r-1, c, -1);
35894                 break;
35895             
35896             case e.RIGHT:
35897                 newCell = walk(r, c+1, 1);
35898                 break;
35899             
35900             case e.LEFT:
35901                 newCell = walk(r, c-1, -1);
35902                 break;
35903             
35904             case e.ENTER:
35905                 
35906                 if(g.isEditor && !g.editing){
35907                    g.startEditing(r, c);
35908                    e.stopEvent();
35909                    return;
35910                 }
35911                 
35912                 
35913              break;
35914         };
35915         if(newCell){
35916             this.select(newCell[0], newCell[1]);
35917             e.stopEvent();
35918             
35919         }
35920     },
35921
35922     acceptsNav : function(row, col, cm){
35923         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35924     },
35925     /**
35926      * Selects a cell.
35927      * @param {Number} field (not used) - as it's normally used as a listener
35928      * @param {Number} e - event - fake it by using
35929      *
35930      * var e = Roo.EventObjectImpl.prototype;
35931      * e.keyCode = e.TAB
35932      *
35933      * 
35934      */
35935     onEditorKey : function(field, e){
35936         
35937         var k = e.getKey(),
35938             newCell,
35939             g = this.grid,
35940             ed = g.activeEditor,
35941             forward = false;
35942         ///Roo.log('onEditorKey' + k);
35943         
35944         
35945         if (this.enter_is_tab && k == e.ENTER) {
35946             k = e.TAB;
35947         }
35948         
35949         if(k == e.TAB){
35950             if(e.shiftKey){
35951                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35952             }else{
35953                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35954                 forward = true;
35955             }
35956             
35957             e.stopEvent();
35958             
35959         } else if(k == e.ENTER &&  !e.ctrlKey){
35960             ed.completeEdit();
35961             e.stopEvent();
35962             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35963         
35964                 } else if(k == e.ESC){
35965             ed.cancelEdit();
35966         }
35967                 
35968         if (newCell) {
35969             var ecall = { cell : newCell, forward : forward };
35970             this.fireEvent('beforeeditnext', ecall );
35971             newCell = ecall.cell;
35972                         forward = ecall.forward;
35973         }
35974                 
35975         if(newCell){
35976             //Roo.log('next cell after edit');
35977             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35978         } else if (forward) {
35979             // tabbed past last
35980             this.fireEvent.defer(100, this, ['tabend',this]);
35981         }
35982     }
35983 });/*
35984  * Based on:
35985  * Ext JS Library 1.1.1
35986  * Copyright(c) 2006-2007, Ext JS, LLC.
35987  *
35988  * Originally Released Under LGPL - original licence link has changed is not relivant.
35989  *
35990  * Fork - LGPL
35991  * <script type="text/javascript">
35992  */
35993  
35994 /**
35995  * @class Roo.grid.EditorGrid
35996  * @extends Roo.grid.Grid
35997  * Class for creating and editable grid.
35998  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35999  * The container MUST have some type of size defined for the grid to fill. The container will be 
36000  * automatically set to position relative if it isn't already.
36001  * @param {Object} dataSource The data model to bind to
36002  * @param {Object} colModel The column model with info about this grid's columns
36003  */
36004 Roo.grid.EditorGrid = function(container, config){
36005     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36006     this.getGridEl().addClass("xedit-grid");
36007
36008     if(!this.selModel){
36009         this.selModel = new Roo.grid.CellSelectionModel();
36010     }
36011
36012     this.activeEditor = null;
36013
36014         this.addEvents({
36015             /**
36016              * @event beforeedit
36017              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36018              * <ul style="padding:5px;padding-left:16px;">
36019              * <li>grid - This grid</li>
36020              * <li>record - The record being edited</li>
36021              * <li>field - The field name being edited</li>
36022              * <li>value - The value for the field being edited.</li>
36023              * <li>row - The grid row index</li>
36024              * <li>column - The grid column index</li>
36025              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36026              * </ul>
36027              * @param {Object} e An edit event (see above for description)
36028              */
36029             "beforeedit" : true,
36030             /**
36031              * @event afteredit
36032              * Fires after a cell is edited. <br />
36033              * <ul style="padding:5px;padding-left:16px;">
36034              * <li>grid - This grid</li>
36035              * <li>record - The record being edited</li>
36036              * <li>field - The field name being edited</li>
36037              * <li>value - The value being set</li>
36038              * <li>originalValue - The original value for the field, before the edit.</li>
36039              * <li>row - The grid row index</li>
36040              * <li>column - The grid column index</li>
36041              * </ul>
36042              * @param {Object} e An edit event (see above for description)
36043              */
36044             "afteredit" : true,
36045             /**
36046              * @event validateedit
36047              * Fires after a cell is edited, but before the value is set in the record. 
36048          * You can use this to modify the value being set in the field, Return false
36049              * to cancel the change. The edit event object has the following properties <br />
36050              * <ul style="padding:5px;padding-left:16px;">
36051          * <li>editor - This editor</li>
36052              * <li>grid - This grid</li>
36053              * <li>record - The record being edited</li>
36054              * <li>field - The field name being edited</li>
36055              * <li>value - The value being set</li>
36056              * <li>originalValue - The original value for the field, before the edit.</li>
36057              * <li>row - The grid row index</li>
36058              * <li>column - The grid column index</li>
36059              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36060              * </ul>
36061              * @param {Object} e An edit event (see above for description)
36062              */
36063             "validateedit" : true
36064         });
36065     this.on("bodyscroll", this.stopEditing,  this);
36066     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36067 };
36068
36069 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36070     /**
36071      * @cfg {Number} clicksToEdit
36072      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36073      */
36074     clicksToEdit: 2,
36075
36076     // private
36077     isEditor : true,
36078     // private
36079     trackMouseOver: false, // causes very odd FF errors
36080
36081     onCellDblClick : function(g, row, col){
36082         this.startEditing(row, col);
36083     },
36084
36085     onEditComplete : function(ed, value, startValue){
36086         this.editing = false;
36087         this.activeEditor = null;
36088         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36089         var r = ed.record;
36090         var field = this.colModel.getDataIndex(ed.col);
36091         var e = {
36092             grid: this,
36093             record: r,
36094             field: field,
36095             originalValue: startValue,
36096             value: value,
36097             row: ed.row,
36098             column: ed.col,
36099             cancel:false,
36100             editor: ed
36101         };
36102         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36103         cell.show();
36104           
36105         if(String(value) !== String(startValue)){
36106             
36107             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36108                 r.set(field, e.value);
36109                 // if we are dealing with a combo box..
36110                 // then we also set the 'name' colum to be the displayField
36111                 if (ed.field.displayField && ed.field.name) {
36112                     r.set(ed.field.name, ed.field.el.dom.value);
36113                 }
36114                 
36115                 delete e.cancel; //?? why!!!
36116                 this.fireEvent("afteredit", e);
36117             }
36118         } else {
36119             this.fireEvent("afteredit", e); // always fire it!
36120         }
36121         this.view.focusCell(ed.row, ed.col);
36122     },
36123
36124     /**
36125      * Starts editing the specified for the specified row/column
36126      * @param {Number} rowIndex
36127      * @param {Number} colIndex
36128      */
36129     startEditing : function(row, col){
36130         this.stopEditing();
36131         if(this.colModel.isCellEditable(col, row)){
36132             this.view.ensureVisible(row, col, true);
36133           
36134             var r = this.dataSource.getAt(row);
36135             var field = this.colModel.getDataIndex(col);
36136             var cell = Roo.get(this.view.getCell(row,col));
36137             var e = {
36138                 grid: this,
36139                 record: r,
36140                 field: field,
36141                 value: r.data[field],
36142                 row: row,
36143                 column: col,
36144                 cancel:false 
36145             };
36146             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36147                 this.editing = true;
36148                 var ed = this.colModel.getCellEditor(col, row);
36149                 
36150                 if (!ed) {
36151                     return;
36152                 }
36153                 if(!ed.rendered){
36154                     ed.render(ed.parentEl || document.body);
36155                 }
36156                 ed.field.reset();
36157                
36158                 cell.hide();
36159                 
36160                 (function(){ // complex but required for focus issues in safari, ie and opera
36161                     ed.row = row;
36162                     ed.col = col;
36163                     ed.record = r;
36164                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36165                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36166                     this.activeEditor = ed;
36167                     var v = r.data[field];
36168                     ed.startEdit(this.view.getCell(row, col), v);
36169                     // combo's with 'displayField and name set
36170                     if (ed.field.displayField && ed.field.name) {
36171                         ed.field.el.dom.value = r.data[ed.field.name];
36172                     }
36173                     
36174                     
36175                 }).defer(50, this);
36176             }
36177         }
36178     },
36179         
36180     /**
36181      * Stops any active editing
36182      */
36183     stopEditing : function(){
36184         if(this.activeEditor){
36185             this.activeEditor.completeEdit();
36186         }
36187         this.activeEditor = null;
36188     },
36189         
36190          /**
36191      * Called to get grid's drag proxy text, by default returns this.ddText.
36192      * @return {String}
36193      */
36194     getDragDropText : function(){
36195         var count = this.selModel.getSelectedCell() ? 1 : 0;
36196         return String.format(this.ddText, count, count == 1 ? '' : 's');
36197     }
36198         
36199 });/*
36200  * Based on:
36201  * Ext JS Library 1.1.1
36202  * Copyright(c) 2006-2007, Ext JS, LLC.
36203  *
36204  * Originally Released Under LGPL - original licence link has changed is not relivant.
36205  *
36206  * Fork - LGPL
36207  * <script type="text/javascript">
36208  */
36209
36210 // private - not really -- you end up using it !
36211 // This is a support class used internally by the Grid components
36212
36213 /**
36214  * @class Roo.grid.GridEditor
36215  * @extends Roo.Editor
36216  * Class for creating and editable grid elements.
36217  * @param {Object} config any settings (must include field)
36218  */
36219 Roo.grid.GridEditor = function(field, config){
36220     if (!config && field.field) {
36221         config = field;
36222         field = Roo.factory(config.field, Roo.form);
36223     }
36224     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36225     field.monitorTab = false;
36226 };
36227
36228 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36229     
36230     /**
36231      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36232      */
36233     
36234     alignment: "tl-tl",
36235     autoSize: "width",
36236     hideEl : false,
36237     cls: "x-small-editor x-grid-editor",
36238     shim:false,
36239     shadow:"frame"
36240 });/*
36241  * Based on:
36242  * Ext JS Library 1.1.1
36243  * Copyright(c) 2006-2007, Ext JS, LLC.
36244  *
36245  * Originally Released Under LGPL - original licence link has changed is not relivant.
36246  *
36247  * Fork - LGPL
36248  * <script type="text/javascript">
36249  */
36250   
36251
36252   
36253 Roo.grid.PropertyRecord = Roo.data.Record.create([
36254     {name:'name',type:'string'},  'value'
36255 ]);
36256
36257
36258 Roo.grid.PropertyStore = function(grid, source){
36259     this.grid = grid;
36260     this.store = new Roo.data.Store({
36261         recordType : Roo.grid.PropertyRecord
36262     });
36263     this.store.on('update', this.onUpdate,  this);
36264     if(source){
36265         this.setSource(source);
36266     }
36267     Roo.grid.PropertyStore.superclass.constructor.call(this);
36268 };
36269
36270
36271
36272 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36273     setSource : function(o){
36274         this.source = o;
36275         this.store.removeAll();
36276         var data = [];
36277         for(var k in o){
36278             if(this.isEditableValue(o[k])){
36279                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36280             }
36281         }
36282         this.store.loadRecords({records: data}, {}, true);
36283     },
36284
36285     onUpdate : function(ds, record, type){
36286         if(type == Roo.data.Record.EDIT){
36287             var v = record.data['value'];
36288             var oldValue = record.modified['value'];
36289             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36290                 this.source[record.id] = v;
36291                 record.commit();
36292                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36293             }else{
36294                 record.reject();
36295             }
36296         }
36297     },
36298
36299     getProperty : function(row){
36300        return this.store.getAt(row);
36301     },
36302
36303     isEditableValue: function(val){
36304         if(val && val instanceof Date){
36305             return true;
36306         }else if(typeof val == 'object' || typeof val == 'function'){
36307             return false;
36308         }
36309         return true;
36310     },
36311
36312     setValue : function(prop, value){
36313         this.source[prop] = value;
36314         this.store.getById(prop).set('value', value);
36315     },
36316
36317     getSource : function(){
36318         return this.source;
36319     }
36320 });
36321
36322 Roo.grid.PropertyColumnModel = function(grid, store){
36323     this.grid = grid;
36324     var g = Roo.grid;
36325     g.PropertyColumnModel.superclass.constructor.call(this, [
36326         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36327         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36328     ]);
36329     this.store = store;
36330     this.bselect = Roo.DomHelper.append(document.body, {
36331         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36332             {tag: 'option', value: 'true', html: 'true'},
36333             {tag: 'option', value: 'false', html: 'false'}
36334         ]
36335     });
36336     Roo.id(this.bselect);
36337     var f = Roo.form;
36338     this.editors = {
36339         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36340         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36341         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36342         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36343         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36344     };
36345     this.renderCellDelegate = this.renderCell.createDelegate(this);
36346     this.renderPropDelegate = this.renderProp.createDelegate(this);
36347 };
36348
36349 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36350     
36351     
36352     nameText : 'Name',
36353     valueText : 'Value',
36354     
36355     dateFormat : 'm/j/Y',
36356     
36357     
36358     renderDate : function(dateVal){
36359         return dateVal.dateFormat(this.dateFormat);
36360     },
36361
36362     renderBool : function(bVal){
36363         return bVal ? 'true' : 'false';
36364     },
36365
36366     isCellEditable : function(colIndex, rowIndex){
36367         return colIndex == 1;
36368     },
36369
36370     getRenderer : function(col){
36371         return col == 1 ?
36372             this.renderCellDelegate : this.renderPropDelegate;
36373     },
36374
36375     renderProp : function(v){
36376         return this.getPropertyName(v);
36377     },
36378
36379     renderCell : function(val){
36380         var rv = val;
36381         if(val instanceof Date){
36382             rv = this.renderDate(val);
36383         }else if(typeof val == 'boolean'){
36384             rv = this.renderBool(val);
36385         }
36386         return Roo.util.Format.htmlEncode(rv);
36387     },
36388
36389     getPropertyName : function(name){
36390         var pn = this.grid.propertyNames;
36391         return pn && pn[name] ? pn[name] : name;
36392     },
36393
36394     getCellEditor : function(colIndex, rowIndex){
36395         var p = this.store.getProperty(rowIndex);
36396         var n = p.data['name'], val = p.data['value'];
36397         
36398         if(typeof(this.grid.customEditors[n]) == 'string'){
36399             return this.editors[this.grid.customEditors[n]];
36400         }
36401         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36402             return this.grid.customEditors[n];
36403         }
36404         if(val instanceof Date){
36405             return this.editors['date'];
36406         }else if(typeof val == 'number'){
36407             return this.editors['number'];
36408         }else if(typeof val == 'boolean'){
36409             return this.editors['boolean'];
36410         }else{
36411             return this.editors['string'];
36412         }
36413     }
36414 });
36415
36416 /**
36417  * @class Roo.grid.PropertyGrid
36418  * @extends Roo.grid.EditorGrid
36419  * This class represents the  interface of a component based property grid control.
36420  * <br><br>Usage:<pre><code>
36421  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36422       
36423  });
36424  // set any options
36425  grid.render();
36426  * </code></pre>
36427   
36428  * @constructor
36429  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36430  * The container MUST have some type of size defined for the grid to fill. The container will be
36431  * automatically set to position relative if it isn't already.
36432  * @param {Object} config A config object that sets properties on this grid.
36433  */
36434 Roo.grid.PropertyGrid = function(container, config){
36435     config = config || {};
36436     var store = new Roo.grid.PropertyStore(this);
36437     this.store = store;
36438     var cm = new Roo.grid.PropertyColumnModel(this, store);
36439     store.store.sort('name', 'ASC');
36440     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36441         ds: store.store,
36442         cm: cm,
36443         enableColLock:false,
36444         enableColumnMove:false,
36445         stripeRows:false,
36446         trackMouseOver: false,
36447         clicksToEdit:1
36448     }, config));
36449     this.getGridEl().addClass('x-props-grid');
36450     this.lastEditRow = null;
36451     this.on('columnresize', this.onColumnResize, this);
36452     this.addEvents({
36453          /**
36454              * @event beforepropertychange
36455              * Fires before a property changes (return false to stop?)
36456              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36457              * @param {String} id Record Id
36458              * @param {String} newval New Value
36459          * @param {String} oldval Old Value
36460              */
36461         "beforepropertychange": true,
36462         /**
36463              * @event propertychange
36464              * Fires after a property changes
36465              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36466              * @param {String} id Record Id
36467              * @param {String} newval New Value
36468          * @param {String} oldval Old Value
36469              */
36470         "propertychange": true
36471     });
36472     this.customEditors = this.customEditors || {};
36473 };
36474 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36475     
36476      /**
36477      * @cfg {Object} customEditors map of colnames=> custom editors.
36478      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36479      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36480      * false disables editing of the field.
36481          */
36482     
36483       /**
36484      * @cfg {Object} propertyNames map of property Names to their displayed value
36485          */
36486     
36487     render : function(){
36488         Roo.grid.PropertyGrid.superclass.render.call(this);
36489         this.autoSize.defer(100, this);
36490     },
36491
36492     autoSize : function(){
36493         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36494         if(this.view){
36495             this.view.fitColumns();
36496         }
36497     },
36498
36499     onColumnResize : function(){
36500         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36501         this.autoSize();
36502     },
36503     /**
36504      * Sets the data for the Grid
36505      * accepts a Key => Value object of all the elements avaiable.
36506      * @param {Object} data  to appear in grid.
36507      */
36508     setSource : function(source){
36509         this.store.setSource(source);
36510         //this.autoSize();
36511     },
36512     /**
36513      * Gets all the data from the grid.
36514      * @return {Object} data  data stored in grid
36515      */
36516     getSource : function(){
36517         return this.store.getSource();
36518     }
36519 });/*
36520   
36521  * Licence LGPL
36522  
36523  */
36524  
36525 /**
36526  * @class Roo.grid.Calendar
36527  * @extends Roo.util.Grid
36528  * This class extends the Grid to provide a calendar widget
36529  * <br><br>Usage:<pre><code>
36530  var grid = new Roo.grid.Calendar("my-container-id", {
36531      ds: myDataStore,
36532      cm: myColModel,
36533      selModel: mySelectionModel,
36534      autoSizeColumns: true,
36535      monitorWindowResize: false,
36536      trackMouseOver: true
36537      eventstore : real data store..
36538  });
36539  // set any options
36540  grid.render();
36541   
36542   * @constructor
36543  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36544  * The container MUST have some type of size defined for the grid to fill. The container will be
36545  * automatically set to position relative if it isn't already.
36546  * @param {Object} config A config object that sets properties on this grid.
36547  */
36548 Roo.grid.Calendar = function(container, config){
36549         // initialize the container
36550         this.container = Roo.get(container);
36551         this.container.update("");
36552         this.container.setStyle("overflow", "hidden");
36553     this.container.addClass('x-grid-container');
36554
36555     this.id = this.container.id;
36556
36557     Roo.apply(this, config);
36558     // check and correct shorthanded configs
36559     
36560     var rows = [];
36561     var d =1;
36562     for (var r = 0;r < 6;r++) {
36563         
36564         rows[r]=[];
36565         for (var c =0;c < 7;c++) {
36566             rows[r][c]= '';
36567         }
36568     }
36569     if (this.eventStore) {
36570         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36571         this.eventStore.on('load',this.onLoad, this);
36572         this.eventStore.on('beforeload',this.clearEvents, this);
36573          
36574     }
36575     
36576     this.dataSource = new Roo.data.Store({
36577             proxy: new Roo.data.MemoryProxy(rows),
36578             reader: new Roo.data.ArrayReader({}, [
36579                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36580     });
36581
36582     this.dataSource.load();
36583     this.ds = this.dataSource;
36584     this.ds.xmodule = this.xmodule || false;
36585     
36586     
36587     var cellRender = function(v,x,r)
36588     {
36589         return String.format(
36590             '<div class="fc-day  fc-widget-content"><div>' +
36591                 '<div class="fc-event-container"></div>' +
36592                 '<div class="fc-day-number">{0}</div>'+
36593                 
36594                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36595             '</div></div>', v);
36596     
36597     }
36598     
36599     
36600     this.colModel = new Roo.grid.ColumnModel( [
36601         {
36602             xtype: 'ColumnModel',
36603             xns: Roo.grid,
36604             dataIndex : 'weekday0',
36605             header : 'Sunday',
36606             renderer : cellRender
36607         },
36608         {
36609             xtype: 'ColumnModel',
36610             xns: Roo.grid,
36611             dataIndex : 'weekday1',
36612             header : 'Monday',
36613             renderer : cellRender
36614         },
36615         {
36616             xtype: 'ColumnModel',
36617             xns: Roo.grid,
36618             dataIndex : 'weekday2',
36619             header : 'Tuesday',
36620             renderer : cellRender
36621         },
36622         {
36623             xtype: 'ColumnModel',
36624             xns: Roo.grid,
36625             dataIndex : 'weekday3',
36626             header : 'Wednesday',
36627             renderer : cellRender
36628         },
36629         {
36630             xtype: 'ColumnModel',
36631             xns: Roo.grid,
36632             dataIndex : 'weekday4',
36633             header : 'Thursday',
36634             renderer : cellRender
36635         },
36636         {
36637             xtype: 'ColumnModel',
36638             xns: Roo.grid,
36639             dataIndex : 'weekday5',
36640             header : 'Friday',
36641             renderer : cellRender
36642         },
36643         {
36644             xtype: 'ColumnModel',
36645             xns: Roo.grid,
36646             dataIndex : 'weekday6',
36647             header : 'Saturday',
36648             renderer : cellRender
36649         }
36650     ]);
36651     this.cm = this.colModel;
36652     this.cm.xmodule = this.xmodule || false;
36653  
36654         
36655           
36656     //this.selModel = new Roo.grid.CellSelectionModel();
36657     //this.sm = this.selModel;
36658     //this.selModel.init(this);
36659     
36660     
36661     if(this.width){
36662         this.container.setWidth(this.width);
36663     }
36664
36665     if(this.height){
36666         this.container.setHeight(this.height);
36667     }
36668     /** @private */
36669         this.addEvents({
36670         // raw events
36671         /**
36672          * @event click
36673          * The raw click event for the entire grid.
36674          * @param {Roo.EventObject} e
36675          */
36676         "click" : true,
36677         /**
36678          * @event dblclick
36679          * The raw dblclick event for the entire grid.
36680          * @param {Roo.EventObject} e
36681          */
36682         "dblclick" : true,
36683         /**
36684          * @event contextmenu
36685          * The raw contextmenu event for the entire grid.
36686          * @param {Roo.EventObject} e
36687          */
36688         "contextmenu" : true,
36689         /**
36690          * @event mousedown
36691          * The raw mousedown event for the entire grid.
36692          * @param {Roo.EventObject} e
36693          */
36694         "mousedown" : true,
36695         /**
36696          * @event mouseup
36697          * The raw mouseup event for the entire grid.
36698          * @param {Roo.EventObject} e
36699          */
36700         "mouseup" : true,
36701         /**
36702          * @event mouseover
36703          * The raw mouseover event for the entire grid.
36704          * @param {Roo.EventObject} e
36705          */
36706         "mouseover" : true,
36707         /**
36708          * @event mouseout
36709          * The raw mouseout event for the entire grid.
36710          * @param {Roo.EventObject} e
36711          */
36712         "mouseout" : true,
36713         /**
36714          * @event keypress
36715          * The raw keypress event for the entire grid.
36716          * @param {Roo.EventObject} e
36717          */
36718         "keypress" : true,
36719         /**
36720          * @event keydown
36721          * The raw keydown event for the entire grid.
36722          * @param {Roo.EventObject} e
36723          */
36724         "keydown" : true,
36725
36726         // custom events
36727
36728         /**
36729          * @event cellclick
36730          * Fires when a cell is clicked
36731          * @param {Grid} this
36732          * @param {Number} rowIndex
36733          * @param {Number} columnIndex
36734          * @param {Roo.EventObject} e
36735          */
36736         "cellclick" : true,
36737         /**
36738          * @event celldblclick
36739          * Fires when a cell is double clicked
36740          * @param {Grid} this
36741          * @param {Number} rowIndex
36742          * @param {Number} columnIndex
36743          * @param {Roo.EventObject} e
36744          */
36745         "celldblclick" : true,
36746         /**
36747          * @event rowclick
36748          * Fires when a row is clicked
36749          * @param {Grid} this
36750          * @param {Number} rowIndex
36751          * @param {Roo.EventObject} e
36752          */
36753         "rowclick" : true,
36754         /**
36755          * @event rowdblclick
36756          * Fires when a row is double clicked
36757          * @param {Grid} this
36758          * @param {Number} rowIndex
36759          * @param {Roo.EventObject} e
36760          */
36761         "rowdblclick" : true,
36762         /**
36763          * @event headerclick
36764          * Fires when a header is clicked
36765          * @param {Grid} this
36766          * @param {Number} columnIndex
36767          * @param {Roo.EventObject} e
36768          */
36769         "headerclick" : true,
36770         /**
36771          * @event headerdblclick
36772          * Fires when a header cell is double clicked
36773          * @param {Grid} this
36774          * @param {Number} columnIndex
36775          * @param {Roo.EventObject} e
36776          */
36777         "headerdblclick" : true,
36778         /**
36779          * @event rowcontextmenu
36780          * Fires when a row is right clicked
36781          * @param {Grid} this
36782          * @param {Number} rowIndex
36783          * @param {Roo.EventObject} e
36784          */
36785         "rowcontextmenu" : true,
36786         /**
36787          * @event cellcontextmenu
36788          * Fires when a cell is right clicked
36789          * @param {Grid} this
36790          * @param {Number} rowIndex
36791          * @param {Number} cellIndex
36792          * @param {Roo.EventObject} e
36793          */
36794          "cellcontextmenu" : true,
36795         /**
36796          * @event headercontextmenu
36797          * Fires when a header is right clicked
36798          * @param {Grid} this
36799          * @param {Number} columnIndex
36800          * @param {Roo.EventObject} e
36801          */
36802         "headercontextmenu" : true,
36803         /**
36804          * @event bodyscroll
36805          * Fires when the body element is scrolled
36806          * @param {Number} scrollLeft
36807          * @param {Number} scrollTop
36808          */
36809         "bodyscroll" : true,
36810         /**
36811          * @event columnresize
36812          * Fires when the user resizes a column
36813          * @param {Number} columnIndex
36814          * @param {Number} newSize
36815          */
36816         "columnresize" : true,
36817         /**
36818          * @event columnmove
36819          * Fires when the user moves a column
36820          * @param {Number} oldIndex
36821          * @param {Number} newIndex
36822          */
36823         "columnmove" : true,
36824         /**
36825          * @event startdrag
36826          * Fires when row(s) start being dragged
36827          * @param {Grid} this
36828          * @param {Roo.GridDD} dd The drag drop object
36829          * @param {event} e The raw browser event
36830          */
36831         "startdrag" : true,
36832         /**
36833          * @event enddrag
36834          * Fires when a drag operation is complete
36835          * @param {Grid} this
36836          * @param {Roo.GridDD} dd The drag drop object
36837          * @param {event} e The raw browser event
36838          */
36839         "enddrag" : true,
36840         /**
36841          * @event dragdrop
36842          * Fires when dragged row(s) are dropped on a valid DD target
36843          * @param {Grid} this
36844          * @param {Roo.GridDD} dd The drag drop object
36845          * @param {String} targetId The target drag drop object
36846          * @param {event} e The raw browser event
36847          */
36848         "dragdrop" : true,
36849         /**
36850          * @event dragover
36851          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36852          * @param {Grid} this
36853          * @param {Roo.GridDD} dd The drag drop object
36854          * @param {String} targetId The target drag drop object
36855          * @param {event} e The raw browser event
36856          */
36857         "dragover" : true,
36858         /**
36859          * @event dragenter
36860          *  Fires when the dragged row(s) first cross another DD target while being dragged
36861          * @param {Grid} this
36862          * @param {Roo.GridDD} dd The drag drop object
36863          * @param {String} targetId The target drag drop object
36864          * @param {event} e The raw browser event
36865          */
36866         "dragenter" : true,
36867         /**
36868          * @event dragout
36869          * Fires when the dragged row(s) leave another DD target while being dragged
36870          * @param {Grid} this
36871          * @param {Roo.GridDD} dd The drag drop object
36872          * @param {String} targetId The target drag drop object
36873          * @param {event} e The raw browser event
36874          */
36875         "dragout" : true,
36876         /**
36877          * @event rowclass
36878          * Fires when a row is rendered, so you can change add a style to it.
36879          * @param {GridView} gridview   The grid view
36880          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36881          */
36882         'rowclass' : true,
36883
36884         /**
36885          * @event render
36886          * Fires when the grid is rendered
36887          * @param {Grid} grid
36888          */
36889         'render' : true,
36890             /**
36891              * @event select
36892              * Fires when a date is selected
36893              * @param {DatePicker} this
36894              * @param {Date} date The selected date
36895              */
36896         'select': true,
36897         /**
36898              * @event monthchange
36899              * Fires when the displayed month changes 
36900              * @param {DatePicker} this
36901              * @param {Date} date The selected month
36902              */
36903         'monthchange': true,
36904         /**
36905              * @event evententer
36906              * Fires when mouse over an event
36907              * @param {Calendar} this
36908              * @param {event} Event
36909              */
36910         'evententer': true,
36911         /**
36912              * @event eventleave
36913              * Fires when the mouse leaves an
36914              * @param {Calendar} this
36915              * @param {event}
36916              */
36917         'eventleave': true,
36918         /**
36919              * @event eventclick
36920              * Fires when the mouse click an
36921              * @param {Calendar} this
36922              * @param {event}
36923              */
36924         'eventclick': true,
36925         /**
36926              * @event eventrender
36927              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36928              * @param {Calendar} this
36929              * @param {data} data to be modified
36930              */
36931         'eventrender': true
36932         
36933     });
36934
36935     Roo.grid.Grid.superclass.constructor.call(this);
36936     this.on('render', function() {
36937         this.view.el.addClass('x-grid-cal'); 
36938         
36939         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36940
36941     },this);
36942     
36943     if (!Roo.grid.Calendar.style) {
36944         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36945             
36946             
36947             '.x-grid-cal .x-grid-col' :  {
36948                 height: 'auto !important',
36949                 'vertical-align': 'top'
36950             },
36951             '.x-grid-cal  .fc-event-hori' : {
36952                 height: '14px'
36953             }
36954              
36955             
36956         }, Roo.id());
36957     }
36958
36959     
36960     
36961 };
36962 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36963     /**
36964      * @cfg {Store} eventStore The store that loads events.
36965      */
36966     eventStore : 25,
36967
36968      
36969     activeDate : false,
36970     startDay : 0,
36971     autoWidth : true,
36972     monitorWindowResize : false,
36973
36974     
36975     resizeColumns : function() {
36976         var col = (this.view.el.getWidth() / 7) - 3;
36977         // loop through cols, and setWidth
36978         for(var i =0 ; i < 7 ; i++){
36979             this.cm.setColumnWidth(i, col);
36980         }
36981     },
36982      setDate :function(date) {
36983         
36984         Roo.log('setDate?');
36985         
36986         this.resizeColumns();
36987         var vd = this.activeDate;
36988         this.activeDate = date;
36989 //        if(vd && this.el){
36990 //            var t = date.getTime();
36991 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36992 //                Roo.log('using add remove');
36993 //                
36994 //                this.fireEvent('monthchange', this, date);
36995 //                
36996 //                this.cells.removeClass("fc-state-highlight");
36997 //                this.cells.each(function(c){
36998 //                   if(c.dateValue == t){
36999 //                       c.addClass("fc-state-highlight");
37000 //                       setTimeout(function(){
37001 //                            try{c.dom.firstChild.focus();}catch(e){}
37002 //                       }, 50);
37003 //                       return false;
37004 //                   }
37005 //                   return true;
37006 //                });
37007 //                return;
37008 //            }
37009 //        }
37010         
37011         var days = date.getDaysInMonth();
37012         
37013         var firstOfMonth = date.getFirstDateOfMonth();
37014         var startingPos = firstOfMonth.getDay()-this.startDay;
37015         
37016         if(startingPos < this.startDay){
37017             startingPos += 7;
37018         }
37019         
37020         var pm = date.add(Date.MONTH, -1);
37021         var prevStart = pm.getDaysInMonth()-startingPos;
37022 //        
37023         
37024         
37025         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37026         
37027         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37028         //this.cells.addClassOnOver('fc-state-hover');
37029         
37030         var cells = this.cells.elements;
37031         var textEls = this.textNodes;
37032         
37033         //Roo.each(cells, function(cell){
37034         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37035         //});
37036         
37037         days += startingPos;
37038
37039         // convert everything to numbers so it's fast
37040         var day = 86400000;
37041         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37042         //Roo.log(d);
37043         //Roo.log(pm);
37044         //Roo.log(prevStart);
37045         
37046         var today = new Date().clearTime().getTime();
37047         var sel = date.clearTime().getTime();
37048         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37049         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37050         var ddMatch = this.disabledDatesRE;
37051         var ddText = this.disabledDatesText;
37052         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37053         var ddaysText = this.disabledDaysText;
37054         var format = this.format;
37055         
37056         var setCellClass = function(cal, cell){
37057             
37058             //Roo.log('set Cell Class');
37059             cell.title = "";
37060             var t = d.getTime();
37061             
37062             //Roo.log(d);
37063             
37064             
37065             cell.dateValue = t;
37066             if(t == today){
37067                 cell.className += " fc-today";
37068                 cell.className += " fc-state-highlight";
37069                 cell.title = cal.todayText;
37070             }
37071             if(t == sel){
37072                 // disable highlight in other month..
37073                 cell.className += " fc-state-highlight";
37074                 
37075             }
37076             // disabling
37077             if(t < min) {
37078                 //cell.className = " fc-state-disabled";
37079                 cell.title = cal.minText;
37080                 return;
37081             }
37082             if(t > max) {
37083                 //cell.className = " fc-state-disabled";
37084                 cell.title = cal.maxText;
37085                 return;
37086             }
37087             if(ddays){
37088                 if(ddays.indexOf(d.getDay()) != -1){
37089                     // cell.title = ddaysText;
37090                    // cell.className = " fc-state-disabled";
37091                 }
37092             }
37093             if(ddMatch && format){
37094                 var fvalue = d.dateFormat(format);
37095                 if(ddMatch.test(fvalue)){
37096                     cell.title = ddText.replace("%0", fvalue);
37097                    cell.className = " fc-state-disabled";
37098                 }
37099             }
37100             
37101             if (!cell.initialClassName) {
37102                 cell.initialClassName = cell.dom.className;
37103             }
37104             
37105             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37106         };
37107
37108         var i = 0;
37109         
37110         for(; i < startingPos; i++) {
37111             cells[i].dayName =  (++prevStart);
37112             Roo.log(textEls[i]);
37113             d.setDate(d.getDate()+1);
37114             
37115             //cells[i].className = "fc-past fc-other-month";
37116             setCellClass(this, cells[i]);
37117         }
37118         
37119         var intDay = 0;
37120         
37121         for(; i < days; i++){
37122             intDay = i - startingPos + 1;
37123             cells[i].dayName =  (intDay);
37124             d.setDate(d.getDate()+1);
37125             
37126             cells[i].className = ''; // "x-date-active";
37127             setCellClass(this, cells[i]);
37128         }
37129         var extraDays = 0;
37130         
37131         for(; i < 42; i++) {
37132             //textEls[i].innerHTML = (++extraDays);
37133             
37134             d.setDate(d.getDate()+1);
37135             cells[i].dayName = (++extraDays);
37136             cells[i].className = "fc-future fc-other-month";
37137             setCellClass(this, cells[i]);
37138         }
37139         
37140         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37141         
37142         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37143         
37144         // this will cause all the cells to mis
37145         var rows= [];
37146         var i =0;
37147         for (var r = 0;r < 6;r++) {
37148             for (var c =0;c < 7;c++) {
37149                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37150             }    
37151         }
37152         
37153         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37154         for(i=0;i<cells.length;i++) {
37155             
37156             this.cells.elements[i].dayName = cells[i].dayName ;
37157             this.cells.elements[i].className = cells[i].className;
37158             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37159             this.cells.elements[i].title = cells[i].title ;
37160             this.cells.elements[i].dateValue = cells[i].dateValue ;
37161         }
37162         
37163         
37164         
37165         
37166         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37167         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37168         
37169         ////if(totalRows != 6){
37170             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37171            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37172        // }
37173         
37174         this.fireEvent('monthchange', this, date);
37175         
37176         
37177     },
37178  /**
37179      * Returns the grid's SelectionModel.
37180      * @return {SelectionModel}
37181      */
37182     getSelectionModel : function(){
37183         if(!this.selModel){
37184             this.selModel = new Roo.grid.CellSelectionModel();
37185         }
37186         return this.selModel;
37187     },
37188
37189     load: function() {
37190         this.eventStore.load()
37191         
37192         
37193         
37194     },
37195     
37196     findCell : function(dt) {
37197         dt = dt.clearTime().getTime();
37198         var ret = false;
37199         this.cells.each(function(c){
37200             //Roo.log("check " +c.dateValue + '?=' + dt);
37201             if(c.dateValue == dt){
37202                 ret = c;
37203                 return false;
37204             }
37205             return true;
37206         });
37207         
37208         return ret;
37209     },
37210     
37211     findCells : function(rec) {
37212         var s = rec.data.start_dt.clone().clearTime().getTime();
37213        // Roo.log(s);
37214         var e= rec.data.end_dt.clone().clearTime().getTime();
37215        // Roo.log(e);
37216         var ret = [];
37217         this.cells.each(function(c){
37218              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37219             
37220             if(c.dateValue > e){
37221                 return ;
37222             }
37223             if(c.dateValue < s){
37224                 return ;
37225             }
37226             ret.push(c);
37227         });
37228         
37229         return ret;    
37230     },
37231     
37232     findBestRow: function(cells)
37233     {
37234         var ret = 0;
37235         
37236         for (var i =0 ; i < cells.length;i++) {
37237             ret  = Math.max(cells[i].rows || 0,ret);
37238         }
37239         return ret;
37240         
37241     },
37242     
37243     
37244     addItem : function(rec)
37245     {
37246         // look for vertical location slot in
37247         var cells = this.findCells(rec);
37248         
37249         rec.row = this.findBestRow(cells);
37250         
37251         // work out the location.
37252         
37253         var crow = false;
37254         var rows = [];
37255         for(var i =0; i < cells.length; i++) {
37256             if (!crow) {
37257                 crow = {
37258                     start : cells[i],
37259                     end :  cells[i]
37260                 };
37261                 continue;
37262             }
37263             if (crow.start.getY() == cells[i].getY()) {
37264                 // on same row.
37265                 crow.end = cells[i];
37266                 continue;
37267             }
37268             // different row.
37269             rows.push(crow);
37270             crow = {
37271                 start: cells[i],
37272                 end : cells[i]
37273             };
37274             
37275         }
37276         
37277         rows.push(crow);
37278         rec.els = [];
37279         rec.rows = rows;
37280         rec.cells = cells;
37281         for (var i = 0; i < cells.length;i++) {
37282             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37283             
37284         }
37285         
37286         
37287     },
37288     
37289     clearEvents: function() {
37290         
37291         if (!this.eventStore.getCount()) {
37292             return;
37293         }
37294         // reset number of rows in cells.
37295         Roo.each(this.cells.elements, function(c){
37296             c.rows = 0;
37297         });
37298         
37299         this.eventStore.each(function(e) {
37300             this.clearEvent(e);
37301         },this);
37302         
37303     },
37304     
37305     clearEvent : function(ev)
37306     {
37307         if (ev.els) {
37308             Roo.each(ev.els, function(el) {
37309                 el.un('mouseenter' ,this.onEventEnter, this);
37310                 el.un('mouseleave' ,this.onEventLeave, this);
37311                 el.remove();
37312             },this);
37313             ev.els = [];
37314         }
37315     },
37316     
37317     
37318     renderEvent : function(ev,ctr) {
37319         if (!ctr) {
37320              ctr = this.view.el.select('.fc-event-container',true).first();
37321         }
37322         
37323          
37324         this.clearEvent(ev);
37325             //code
37326        
37327         
37328         
37329         ev.els = [];
37330         var cells = ev.cells;
37331         var rows = ev.rows;
37332         this.fireEvent('eventrender', this, ev);
37333         
37334         for(var i =0; i < rows.length; i++) {
37335             
37336             cls = '';
37337             if (i == 0) {
37338                 cls += ' fc-event-start';
37339             }
37340             if ((i+1) == rows.length) {
37341                 cls += ' fc-event-end';
37342             }
37343             
37344             //Roo.log(ev.data);
37345             // how many rows should it span..
37346             var cg = this.eventTmpl.append(ctr,Roo.apply({
37347                 fccls : cls
37348                 
37349             }, ev.data) , true);
37350             
37351             
37352             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37353             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37354             cg.on('click', this.onEventClick, this, ev);
37355             
37356             ev.els.push(cg);
37357             
37358             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37359             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37360             //Roo.log(cg);
37361              
37362             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37363             cg.setWidth(ebox.right - sbox.x -2);
37364         }
37365     },
37366     
37367     renderEvents: function()
37368     {   
37369         // first make sure there is enough space..
37370         
37371         if (!this.eventTmpl) {
37372             this.eventTmpl = new Roo.Template(
37373                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37374                     '<div class="fc-event-inner">' +
37375                         '<span class="fc-event-time">{time}</span>' +
37376                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37377                     '</div>' +
37378                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37379                 '</div>'
37380             );
37381                 
37382         }
37383                
37384         
37385         
37386         this.cells.each(function(c) {
37387             //Roo.log(c.select('.fc-day-content div',true).first());
37388             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37389         });
37390         
37391         var ctr = this.view.el.select('.fc-event-container',true).first();
37392         
37393         var cls;
37394         this.eventStore.each(function(ev){
37395             
37396             this.renderEvent(ev);
37397              
37398              
37399         }, this);
37400         this.view.layout();
37401         
37402     },
37403     
37404     onEventEnter: function (e, el,event,d) {
37405         this.fireEvent('evententer', this, el, event);
37406     },
37407     
37408     onEventLeave: function (e, el,event,d) {
37409         this.fireEvent('eventleave', this, el, event);
37410     },
37411     
37412     onEventClick: function (e, el,event,d) {
37413         this.fireEvent('eventclick', this, el, event);
37414     },
37415     
37416     onMonthChange: function () {
37417         this.store.load();
37418     },
37419     
37420     onLoad: function () {
37421         
37422         //Roo.log('calendar onload');
37423 //         
37424         if(this.eventStore.getCount() > 0){
37425             
37426            
37427             
37428             this.eventStore.each(function(d){
37429                 
37430                 
37431                 // FIXME..
37432                 var add =   d.data;
37433                 if (typeof(add.end_dt) == 'undefined')  {
37434                     Roo.log("Missing End time in calendar data: ");
37435                     Roo.log(d);
37436                     return;
37437                 }
37438                 if (typeof(add.start_dt) == 'undefined')  {
37439                     Roo.log("Missing Start time in calendar data: ");
37440                     Roo.log(d);
37441                     return;
37442                 }
37443                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37444                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37445                 add.id = add.id || d.id;
37446                 add.title = add.title || '??';
37447                 
37448                 this.addItem(d);
37449                 
37450              
37451             },this);
37452         }
37453         
37454         this.renderEvents();
37455     }
37456     
37457
37458 });
37459 /*
37460  grid : {
37461                 xtype: 'Grid',
37462                 xns: Roo.grid,
37463                 listeners : {
37464                     render : function ()
37465                     {
37466                         _this.grid = this;
37467                         
37468                         if (!this.view.el.hasClass('course-timesheet')) {
37469                             this.view.el.addClass('course-timesheet');
37470                         }
37471                         if (this.tsStyle) {
37472                             this.ds.load({});
37473                             return; 
37474                         }
37475                         Roo.log('width');
37476                         Roo.log(_this.grid.view.el.getWidth());
37477                         
37478                         
37479                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37480                             '.course-timesheet .x-grid-row' : {
37481                                 height: '80px'
37482                             },
37483                             '.x-grid-row td' : {
37484                                 'vertical-align' : 0
37485                             },
37486                             '.course-edit-link' : {
37487                                 'color' : 'blue',
37488                                 'text-overflow' : 'ellipsis',
37489                                 'overflow' : 'hidden',
37490                                 'white-space' : 'nowrap',
37491                                 'cursor' : 'pointer'
37492                             },
37493                             '.sub-link' : {
37494                                 'color' : 'green'
37495                             },
37496                             '.de-act-sup-link' : {
37497                                 'color' : 'purple',
37498                                 'text-decoration' : 'line-through'
37499                             },
37500                             '.de-act-link' : {
37501                                 'color' : 'red',
37502                                 'text-decoration' : 'line-through'
37503                             },
37504                             '.course-timesheet .course-highlight' : {
37505                                 'border-top-style': 'dashed !important',
37506                                 'border-bottom-bottom': 'dashed !important'
37507                             },
37508                             '.course-timesheet .course-item' : {
37509                                 'font-family'   : 'tahoma, arial, helvetica',
37510                                 'font-size'     : '11px',
37511                                 'overflow'      : 'hidden',
37512                                 'padding-left'  : '10px',
37513                                 'padding-right' : '10px',
37514                                 'padding-top' : '10px' 
37515                             }
37516                             
37517                         }, Roo.id());
37518                                 this.ds.load({});
37519                     }
37520                 },
37521                 autoWidth : true,
37522                 monitorWindowResize : false,
37523                 cellrenderer : function(v,x,r)
37524                 {
37525                     return v;
37526                 },
37527                 sm : {
37528                     xtype: 'CellSelectionModel',
37529                     xns: Roo.grid
37530                 },
37531                 dataSource : {
37532                     xtype: 'Store',
37533                     xns: Roo.data,
37534                     listeners : {
37535                         beforeload : function (_self, options)
37536                         {
37537                             options.params = options.params || {};
37538                             options.params._month = _this.monthField.getValue();
37539                             options.params.limit = 9999;
37540                             options.params['sort'] = 'when_dt';    
37541                             options.params['dir'] = 'ASC';    
37542                             this.proxy.loadResponse = this.loadResponse;
37543                             Roo.log("load?");
37544                             //this.addColumns();
37545                         },
37546                         load : function (_self, records, options)
37547                         {
37548                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37549                                 // if you click on the translation.. you can edit it...
37550                                 var el = Roo.get(this);
37551                                 var id = el.dom.getAttribute('data-id');
37552                                 var d = el.dom.getAttribute('data-date');
37553                                 var t = el.dom.getAttribute('data-time');
37554                                 //var id = this.child('span').dom.textContent;
37555                                 
37556                                 //Roo.log(this);
37557                                 Pman.Dialog.CourseCalendar.show({
37558                                     id : id,
37559                                     when_d : d,
37560                                     when_t : t,
37561                                     productitem_active : id ? 1 : 0
37562                                 }, function() {
37563                                     _this.grid.ds.load({});
37564                                 });
37565                            
37566                            });
37567                            
37568                            _this.panel.fireEvent('resize', [ '', '' ]);
37569                         }
37570                     },
37571                     loadResponse : function(o, success, response){
37572                             // this is overridden on before load..
37573                             
37574                             Roo.log("our code?");       
37575                             //Roo.log(success);
37576                             //Roo.log(response)
37577                             delete this.activeRequest;
37578                             if(!success){
37579                                 this.fireEvent("loadexception", this, o, response);
37580                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37581                                 return;
37582                             }
37583                             var result;
37584                             try {
37585                                 result = o.reader.read(response);
37586                             }catch(e){
37587                                 Roo.log("load exception?");
37588                                 this.fireEvent("loadexception", this, o, response, e);
37589                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37590                                 return;
37591                             }
37592                             Roo.log("ready...");        
37593                             // loop through result.records;
37594                             // and set this.tdate[date] = [] << array of records..
37595                             _this.tdata  = {};
37596                             Roo.each(result.records, function(r){
37597                                 //Roo.log(r.data);
37598                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37599                                     _this.tdata[r.data.when_dt.format('j')] = [];
37600                                 }
37601                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37602                             });
37603                             
37604                             //Roo.log(_this.tdata);
37605                             
37606                             result.records = [];
37607                             result.totalRecords = 6;
37608                     
37609                             // let's generate some duumy records for the rows.
37610                             //var st = _this.dateField.getValue();
37611                             
37612                             // work out monday..
37613                             //st = st.add(Date.DAY, -1 * st.format('w'));
37614                             
37615                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37616                             
37617                             var firstOfMonth = date.getFirstDayOfMonth();
37618                             var days = date.getDaysInMonth();
37619                             var d = 1;
37620                             var firstAdded = false;
37621                             for (var i = 0; i < result.totalRecords ; i++) {
37622                                 //var d= st.add(Date.DAY, i);
37623                                 var row = {};
37624                                 var added = 0;
37625                                 for(var w = 0 ; w < 7 ; w++){
37626                                     if(!firstAdded && firstOfMonth != w){
37627                                         continue;
37628                                     }
37629                                     if(d > days){
37630                                         continue;
37631                                     }
37632                                     firstAdded = true;
37633                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37634                                     row['weekday'+w] = String.format(
37635                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37636                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37637                                                     d,
37638                                                     date.format('Y-m-')+dd
37639                                                 );
37640                                     added++;
37641                                     if(typeof(_this.tdata[d]) != 'undefined'){
37642                                         Roo.each(_this.tdata[d], function(r){
37643                                             var is_sub = '';
37644                                             var deactive = '';
37645                                             var id = r.id;
37646                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37647                                             if(r.parent_id*1>0){
37648                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37649                                                 id = r.parent_id;
37650                                             }
37651                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37652                                                 deactive = 'de-act-link';
37653                                             }
37654                                             
37655                                             row['weekday'+w] += String.format(
37656                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37657                                                     id, //0
37658                                                     r.product_id_name, //1
37659                                                     r.when_dt.format('h:ia'), //2
37660                                                     is_sub, //3
37661                                                     deactive, //4
37662                                                     desc // 5
37663                                             );
37664                                         });
37665                                     }
37666                                     d++;
37667                                 }
37668                                 
37669                                 // only do this if something added..
37670                                 if(added > 0){ 
37671                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37672                                 }
37673                                 
37674                                 
37675                                 // push it twice. (second one with an hour..
37676                                 
37677                             }
37678                             //Roo.log(result);
37679                             this.fireEvent("load", this, o, o.request.arg);
37680                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37681                         },
37682                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37683                     proxy : {
37684                         xtype: 'HttpProxy',
37685                         xns: Roo.data,
37686                         method : 'GET',
37687                         url : baseURL + '/Roo/Shop_course.php'
37688                     },
37689                     reader : {
37690                         xtype: 'JsonReader',
37691                         xns: Roo.data,
37692                         id : 'id',
37693                         fields : [
37694                             {
37695                                 'name': 'id',
37696                                 'type': 'int'
37697                             },
37698                             {
37699                                 'name': 'when_dt',
37700                                 'type': 'string'
37701                             },
37702                             {
37703                                 'name': 'end_dt',
37704                                 'type': 'string'
37705                             },
37706                             {
37707                                 'name': 'parent_id',
37708                                 'type': 'int'
37709                             },
37710                             {
37711                                 'name': 'product_id',
37712                                 'type': 'int'
37713                             },
37714                             {
37715                                 'name': 'productitem_id',
37716                                 'type': 'int'
37717                             },
37718                             {
37719                                 'name': 'guid',
37720                                 'type': 'int'
37721                             }
37722                         ]
37723                     }
37724                 },
37725                 toolbar : {
37726                     xtype: 'Toolbar',
37727                     xns: Roo,
37728                     items : [
37729                         {
37730                             xtype: 'Button',
37731                             xns: Roo.Toolbar,
37732                             listeners : {
37733                                 click : function (_self, e)
37734                                 {
37735                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37736                                     sd.setMonth(sd.getMonth()-1);
37737                                     _this.monthField.setValue(sd.format('Y-m-d'));
37738                                     _this.grid.ds.load({});
37739                                 }
37740                             },
37741                             text : "Back"
37742                         },
37743                         {
37744                             xtype: 'Separator',
37745                             xns: Roo.Toolbar
37746                         },
37747                         {
37748                             xtype: 'MonthField',
37749                             xns: Roo.form,
37750                             listeners : {
37751                                 render : function (_self)
37752                                 {
37753                                     _this.monthField = _self;
37754                                    // _this.monthField.set  today
37755                                 },
37756                                 select : function (combo, date)
37757                                 {
37758                                     _this.grid.ds.load({});
37759                                 }
37760                             },
37761                             value : (function() { return new Date(); })()
37762                         },
37763                         {
37764                             xtype: 'Separator',
37765                             xns: Roo.Toolbar
37766                         },
37767                         {
37768                             xtype: 'TextItem',
37769                             xns: Roo.Toolbar,
37770                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37771                         },
37772                         {
37773                             xtype: 'Fill',
37774                             xns: Roo.Toolbar
37775                         },
37776                         {
37777                             xtype: 'Button',
37778                             xns: Roo.Toolbar,
37779                             listeners : {
37780                                 click : function (_self, e)
37781                                 {
37782                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37783                                     sd.setMonth(sd.getMonth()+1);
37784                                     _this.monthField.setValue(sd.format('Y-m-d'));
37785                                     _this.grid.ds.load({});
37786                                 }
37787                             },
37788                             text : "Next"
37789                         }
37790                     ]
37791                 },
37792                  
37793             }
37794         };
37795         
37796         *//*
37797  * Based on:
37798  * Ext JS Library 1.1.1
37799  * Copyright(c) 2006-2007, Ext JS, LLC.
37800  *
37801  * Originally Released Under LGPL - original licence link has changed is not relivant.
37802  *
37803  * Fork - LGPL
37804  * <script type="text/javascript">
37805  */
37806  
37807 /**
37808  * @class Roo.LoadMask
37809  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37810  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37811  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37812  * element's UpdateManager load indicator and will be destroyed after the initial load.
37813  * @constructor
37814  * Create a new LoadMask
37815  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37816  * @param {Object} config The config object
37817  */
37818 Roo.LoadMask = function(el, config){
37819     this.el = Roo.get(el);
37820     Roo.apply(this, config);
37821     if(this.store){
37822         this.store.on('beforeload', this.onBeforeLoad, this);
37823         this.store.on('load', this.onLoad, this);
37824         this.store.on('loadexception', this.onLoadException, this);
37825         this.removeMask = false;
37826     }else{
37827         var um = this.el.getUpdateManager();
37828         um.showLoadIndicator = false; // disable the default indicator
37829         um.on('beforeupdate', this.onBeforeLoad, this);
37830         um.on('update', this.onLoad, this);
37831         um.on('failure', this.onLoad, this);
37832         this.removeMask = true;
37833     }
37834 };
37835
37836 Roo.LoadMask.prototype = {
37837     /**
37838      * @cfg {Boolean} removeMask
37839      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37840      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37841      */
37842     /**
37843      * @cfg {String} msg
37844      * The text to display in a centered loading message box (defaults to 'Loading...')
37845      */
37846     msg : 'Loading...',
37847     /**
37848      * @cfg {String} msgCls
37849      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37850      */
37851     msgCls : 'x-mask-loading',
37852
37853     /**
37854      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37855      * @type Boolean
37856      */
37857     disabled: false,
37858
37859     /**
37860      * Disables the mask to prevent it from being displayed
37861      */
37862     disable : function(){
37863        this.disabled = true;
37864     },
37865
37866     /**
37867      * Enables the mask so that it can be displayed
37868      */
37869     enable : function(){
37870         this.disabled = false;
37871     },
37872     
37873     onLoadException : function()
37874     {
37875         Roo.log(arguments);
37876         
37877         if (typeof(arguments[3]) != 'undefined') {
37878             Roo.MessageBox.alert("Error loading",arguments[3]);
37879         } 
37880         /*
37881         try {
37882             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37883                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37884             }   
37885         } catch(e) {
37886             
37887         }
37888         */
37889     
37890         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37891     },
37892     // private
37893     onLoad : function()
37894     {
37895         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37896     },
37897
37898     // private
37899     onBeforeLoad : function(){
37900         if(!this.disabled){
37901             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37902         }
37903     },
37904
37905     // private
37906     destroy : function(){
37907         if(this.store){
37908             this.store.un('beforeload', this.onBeforeLoad, this);
37909             this.store.un('load', this.onLoad, this);
37910             this.store.un('loadexception', this.onLoadException, this);
37911         }else{
37912             var um = this.el.getUpdateManager();
37913             um.un('beforeupdate', this.onBeforeLoad, this);
37914             um.un('update', this.onLoad, this);
37915             um.un('failure', this.onLoad, this);
37916         }
37917     }
37918 };/*
37919  * Based on:
37920  * Ext JS Library 1.1.1
37921  * Copyright(c) 2006-2007, Ext JS, LLC.
37922  *
37923  * Originally Released Under LGPL - original licence link has changed is not relivant.
37924  *
37925  * Fork - LGPL
37926  * <script type="text/javascript">
37927  */
37928
37929
37930 /**
37931  * @class Roo.XTemplate
37932  * @extends Roo.Template
37933  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37934 <pre><code>
37935 var t = new Roo.XTemplate(
37936         '&lt;select name="{name}"&gt;',
37937                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37938         '&lt;/select&gt;'
37939 );
37940  
37941 // then append, applying the master template values
37942  </code></pre>
37943  *
37944  * Supported features:
37945  *
37946  *  Tags:
37947
37948 <pre><code>
37949       {a_variable} - output encoded.
37950       {a_variable.format:("Y-m-d")} - call a method on the variable
37951       {a_variable:raw} - unencoded output
37952       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37953       {a_variable:this.method_on_template(...)} - call a method on the template object.
37954  
37955 </code></pre>
37956  *  The tpl tag:
37957 <pre><code>
37958         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37959         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37960         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37961         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37962   
37963         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37964         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37965 </code></pre>
37966  *      
37967  */
37968 Roo.XTemplate = function()
37969 {
37970     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37971     if (this.html) {
37972         this.compile();
37973     }
37974 };
37975
37976
37977 Roo.extend(Roo.XTemplate, Roo.Template, {
37978
37979     /**
37980      * The various sub templates
37981      */
37982     tpls : false,
37983     /**
37984      *
37985      * basic tag replacing syntax
37986      * WORD:WORD()
37987      *
37988      * // you can fake an object call by doing this
37989      *  x.t:(test,tesT) 
37990      * 
37991      */
37992     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37993
37994     /**
37995      * compile the template
37996      *
37997      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37998      *
37999      */
38000     compile: function()
38001     {
38002         var s = this.html;
38003      
38004         s = ['<tpl>', s, '</tpl>'].join('');
38005     
38006         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38007             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38008             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38009             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38010             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38011             m,
38012             id     = 0,
38013             tpls   = [];
38014     
38015         while(true == !!(m = s.match(re))){
38016             var forMatch   = m[0].match(nameRe),
38017                 ifMatch   = m[0].match(ifRe),
38018                 execMatch   = m[0].match(execRe),
38019                 namedMatch   = m[0].match(namedRe),
38020                 
38021                 exp  = null, 
38022                 fn   = null,
38023                 exec = null,
38024                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38025                 
38026             if (ifMatch) {
38027                 // if - puts fn into test..
38028                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38029                 if(exp){
38030                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38031                 }
38032             }
38033             
38034             if (execMatch) {
38035                 // exec - calls a function... returns empty if true is  returned.
38036                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38037                 if(exp){
38038                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38039                 }
38040             }
38041             
38042             
38043             if (name) {
38044                 // for = 
38045                 switch(name){
38046                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38047                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38048                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38049                 }
38050             }
38051             var uid = namedMatch ? namedMatch[1] : id;
38052             
38053             
38054             tpls.push({
38055                 id:     namedMatch ? namedMatch[1] : id,
38056                 target: name,
38057                 exec:   exec,
38058                 test:   fn,
38059                 body:   m[1] || ''
38060             });
38061             if (namedMatch) {
38062                 s = s.replace(m[0], '');
38063             } else { 
38064                 s = s.replace(m[0], '{xtpl'+ id + '}');
38065             }
38066             ++id;
38067         }
38068         this.tpls = [];
38069         for(var i = tpls.length-1; i >= 0; --i){
38070             this.compileTpl(tpls[i]);
38071             this.tpls[tpls[i].id] = tpls[i];
38072         }
38073         this.master = tpls[tpls.length-1];
38074         return this;
38075     },
38076     /**
38077      * same as applyTemplate, except it's done to one of the subTemplates
38078      * when using named templates, you can do:
38079      *
38080      * var str = pl.applySubTemplate('your-name', values);
38081      *
38082      * 
38083      * @param {Number} id of the template
38084      * @param {Object} values to apply to template
38085      * @param {Object} parent (normaly the instance of this object)
38086      */
38087     applySubTemplate : function(id, values, parent)
38088     {
38089         
38090         
38091         var t = this.tpls[id];
38092         
38093         
38094         try { 
38095             if(t.test && !t.test.call(this, values, parent)){
38096                 return '';
38097             }
38098         } catch(e) {
38099             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38100             Roo.log(e.toString());
38101             Roo.log(t.test);
38102             return ''
38103         }
38104         try { 
38105             
38106             if(t.exec && t.exec.call(this, values, parent)){
38107                 return '';
38108             }
38109         } catch(e) {
38110             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38111             Roo.log(e.toString());
38112             Roo.log(t.exec);
38113             return ''
38114         }
38115         try {
38116             var vs = t.target ? t.target.call(this, values, parent) : values;
38117             parent = t.target ? values : parent;
38118             if(t.target && vs instanceof Array){
38119                 var buf = [];
38120                 for(var i = 0, len = vs.length; i < len; i++){
38121                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38122                 }
38123                 return buf.join('');
38124             }
38125             return t.compiled.call(this, vs, parent);
38126         } catch (e) {
38127             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38128             Roo.log(e.toString());
38129             Roo.log(t.compiled);
38130             return '';
38131         }
38132     },
38133
38134     compileTpl : function(tpl)
38135     {
38136         var fm = Roo.util.Format;
38137         var useF = this.disableFormats !== true;
38138         var sep = Roo.isGecko ? "+" : ",";
38139         var undef = function(str) {
38140             Roo.log("Property not found :"  + str);
38141             return '';
38142         };
38143         
38144         var fn = function(m, name, format, args)
38145         {
38146             //Roo.log(arguments);
38147             args = args ? args.replace(/\\'/g,"'") : args;
38148             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38149             if (typeof(format) == 'undefined') {
38150                 format= 'htmlEncode';
38151             }
38152             if (format == 'raw' ) {
38153                 format = false;
38154             }
38155             
38156             if(name.substr(0, 4) == 'xtpl'){
38157                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38158             }
38159             
38160             // build an array of options to determine if value is undefined..
38161             
38162             // basically get 'xxxx.yyyy' then do
38163             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38164             //    (function () { Roo.log("Property not found"); return ''; })() :
38165             //    ......
38166             
38167             var udef_ar = [];
38168             var lookfor = '';
38169             Roo.each(name.split('.'), function(st) {
38170                 lookfor += (lookfor.length ? '.': '') + st;
38171                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38172             });
38173             
38174             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38175             
38176             
38177             if(format && useF){
38178                 
38179                 args = args ? ',' + args : "";
38180                  
38181                 if(format.substr(0, 5) != "this."){
38182                     format = "fm." + format + '(';
38183                 }else{
38184                     format = 'this.call("'+ format.substr(5) + '", ';
38185                     args = ", values";
38186                 }
38187                 
38188                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38189             }
38190              
38191             if (args.length) {
38192                 // called with xxyx.yuu:(test,test)
38193                 // change to ()
38194                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38195             }
38196             // raw.. - :raw modifier..
38197             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38198             
38199         };
38200         var body;
38201         // branched to use + in gecko and [].join() in others
38202         if(Roo.isGecko){
38203             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38204                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38205                     "';};};";
38206         }else{
38207             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38208             body.push(tpl.body.replace(/(\r\n|\n)/g,
38209                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38210             body.push("'].join('');};};");
38211             body = body.join('');
38212         }
38213         
38214         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38215        
38216         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38217         eval(body);
38218         
38219         return this;
38220     },
38221
38222     applyTemplate : function(values){
38223         return this.master.compiled.call(this, values, {});
38224         //var s = this.subs;
38225     },
38226
38227     apply : function(){
38228         return this.applyTemplate.apply(this, arguments);
38229     }
38230
38231  });
38232
38233 Roo.XTemplate.from = function(el){
38234     el = Roo.getDom(el);
38235     return new Roo.XTemplate(el.value || el.innerHTML);
38236 };