Changed roojs-all.jsroojs-debug.jsroojs-ui-debug.jsroojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Array} data The multi-dimensional array of data
1092  * @constructor
1093  * @param {Object} config
1094  */
1095 Roo.data.SimpleStore = function(config){
1096     Roo.data.SimpleStore.superclass.constructor.call(this, {
1097         isLocal : true,
1098         reader: new Roo.data.ArrayReader({
1099                 id: config.id
1100             },
1101             Roo.data.Record.create(config.fields)
1102         ),
1103         proxy : new Roo.data.MemoryProxy(config.data)
1104     });
1105     this.load();
1106 };
1107 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1108  * Based on:
1109  * Ext JS Library 1.1.1
1110  * Copyright(c) 2006-2007, Ext JS, LLC.
1111  *
1112  * Originally Released Under LGPL - original licence link has changed is not relivant.
1113  *
1114  * Fork - LGPL
1115  * <script type="text/javascript">
1116  */
1117
1118 /**
1119 /**
1120  * @extends Roo.data.Store
1121  * @class Roo.data.JsonStore
1122  * Small helper class to make creating Stores for JSON data easier. <br/>
1123 <pre><code>
1124 var store = new Roo.data.JsonStore({
1125     url: 'get-images.php',
1126     root: 'images',
1127     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1128 });
1129 </code></pre>
1130  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1131  * JsonReader and HttpProxy (unless inline data is provided).</b>
1132  * @cfg {Array} fields An array of field definition objects, or field name strings.
1133  * @constructor
1134  * @param {Object} config
1135  */
1136 Roo.data.JsonStore = function(c){
1137     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1138         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1139         reader: new Roo.data.JsonReader(c, c.fields)
1140     }));
1141 };
1142 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1143  * Based on:
1144  * Ext JS Library 1.1.1
1145  * Copyright(c) 2006-2007, Ext JS, LLC.
1146  *
1147  * Originally Released Under LGPL - original licence link has changed is not relivant.
1148  *
1149  * Fork - LGPL
1150  * <script type="text/javascript">
1151  */
1152
1153  
1154 Roo.data.Field = function(config){
1155     if(typeof config == "string"){
1156         config = {name: config};
1157     }
1158     Roo.apply(this, config);
1159     
1160     if(!this.type){
1161         this.type = "auto";
1162     }
1163     
1164     var st = Roo.data.SortTypes;
1165     // named sortTypes are supported, here we look them up
1166     if(typeof this.sortType == "string"){
1167         this.sortType = st[this.sortType];
1168     }
1169     
1170     // set default sortType for strings and dates
1171     if(!this.sortType){
1172         switch(this.type){
1173             case "string":
1174                 this.sortType = st.asUCString;
1175                 break;
1176             case "date":
1177                 this.sortType = st.asDate;
1178                 break;
1179             default:
1180                 this.sortType = st.none;
1181         }
1182     }
1183
1184     // define once
1185     var stripRe = /[\$,%]/g;
1186
1187     // prebuilt conversion function for this field, instead of
1188     // switching every time we're reading a value
1189     if(!this.convert){
1190         var cv, dateFormat = this.dateFormat;
1191         switch(this.type){
1192             case "":
1193             case "auto":
1194             case undefined:
1195                 cv = function(v){ return v; };
1196                 break;
1197             case "string":
1198                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1199                 break;
1200             case "int":
1201                 cv = function(v){
1202                     return v !== undefined && v !== null && v !== '' ?
1203                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1204                     };
1205                 break;
1206             case "float":
1207                 cv = function(v){
1208                     return v !== undefined && v !== null && v !== '' ?
1209                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1210                     };
1211                 break;
1212             case "bool":
1213             case "boolean":
1214                 cv = function(v){ return v === true || v === "true" || v == 1; };
1215                 break;
1216             case "date":
1217                 cv = function(v){
1218                     if(!v){
1219                         return '';
1220                     }
1221                     if(v instanceof Date){
1222                         return v;
1223                     }
1224                     if(dateFormat){
1225                         if(dateFormat == "timestamp"){
1226                             return new Date(v*1000);
1227                         }
1228                         return Date.parseDate(v, dateFormat);
1229                     }
1230                     var parsed = Date.parse(v);
1231                     return parsed ? new Date(parsed) : null;
1232                 };
1233              break;
1234             
1235         }
1236         this.convert = cv;
1237     }
1238 };
1239
1240 Roo.data.Field.prototype = {
1241     dateFormat: null,
1242     defaultValue: "",
1243     mapping: null,
1244     sortType : null,
1245     sortDir : "ASC"
1246 };/*
1247  * Based on:
1248  * Ext JS Library 1.1.1
1249  * Copyright(c) 2006-2007, Ext JS, LLC.
1250  *
1251  * Originally Released Under LGPL - original licence link has changed is not relivant.
1252  *
1253  * Fork - LGPL
1254  * <script type="text/javascript">
1255  */
1256  
1257 // Base class for reading structured data from a data source.  This class is intended to be
1258 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1259
1260 /**
1261  * @class Roo.data.DataReader
1262  * Base class for reading structured data from a data source.  This class is intended to be
1263  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1264  */
1265
1266 Roo.data.DataReader = function(meta, recordType){
1267     
1268     this.meta = meta;
1269     
1270     this.recordType = recordType instanceof Array ? 
1271         Roo.data.Record.create(recordType) : recordType;
1272 };
1273
1274 Roo.data.DataReader.prototype = {
1275      /**
1276      * Create an empty record
1277      * @param {Object} data (optional) - overlay some values
1278      * @return {Roo.data.Record} record created.
1279      */
1280     newRow :  function(d) {
1281         var da =  {};
1282         this.recordType.prototype.fields.each(function(c) {
1283             switch( c.type) {
1284                 case 'int' : da[c.name] = 0; break;
1285                 case 'date' : da[c.name] = new Date(); break;
1286                 case 'float' : da[c.name] = 0.0; break;
1287                 case 'boolean' : da[c.name] = false; break;
1288                 default : da[c.name] = ""; break;
1289             }
1290             
1291         });
1292         return new this.recordType(Roo.apply(da, d));
1293     }
1294     
1295 };/*
1296  * Based on:
1297  * Ext JS Library 1.1.1
1298  * Copyright(c) 2006-2007, Ext JS, LLC.
1299  *
1300  * Originally Released Under LGPL - original licence link has changed is not relivant.
1301  *
1302  * Fork - LGPL
1303  * <script type="text/javascript">
1304  */
1305
1306 /**
1307  * @class Roo.data.DataProxy
1308  * @extends Roo.data.Observable
1309  * This class is an abstract base class for implementations which provide retrieval of
1310  * unformatted data objects.<br>
1311  * <p>
1312  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1313  * (of the appropriate type which knows how to parse the data object) to provide a block of
1314  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1315  * <p>
1316  * Custom implementations must implement the load method as described in
1317  * {@link Roo.data.HttpProxy#load}.
1318  */
1319 Roo.data.DataProxy = function(){
1320     this.addEvents({
1321         /**
1322          * @event beforeload
1323          * Fires before a network request is made to retrieve a data object.
1324          * @param {Object} This DataProxy object.
1325          * @param {Object} params The params parameter to the load function.
1326          */
1327         beforeload : true,
1328         /**
1329          * @event load
1330          * Fires before the load method's callback is called.
1331          * @param {Object} This DataProxy object.
1332          * @param {Object} o The data object.
1333          * @param {Object} arg The callback argument object passed to the load function.
1334          */
1335         load : true,
1336         /**
1337          * @event loadexception
1338          * Fires if an Exception occurs during data retrieval.
1339          * @param {Object} This DataProxy object.
1340          * @param {Object} o The data object.
1341          * @param {Object} arg The callback argument object passed to the load function.
1342          * @param {Object} e The Exception.
1343          */
1344         loadexception : true
1345     });
1346     Roo.data.DataProxy.superclass.constructor.call(this);
1347 };
1348
1349 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1350
1351     /**
1352      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1353      */
1354 /*
1355  * Based on:
1356  * Ext JS Library 1.1.1
1357  * Copyright(c) 2006-2007, Ext JS, LLC.
1358  *
1359  * Originally Released Under LGPL - original licence link has changed is not relivant.
1360  *
1361  * Fork - LGPL
1362  * <script type="text/javascript">
1363  */
1364 /**
1365  * @class Roo.data.MemoryProxy
1366  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1367  * to the Reader when its load method is called.
1368  * @constructor
1369  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1370  */
1371 Roo.data.MemoryProxy = function(data){
1372     if (data.data) {
1373         data = data.data;
1374     }
1375     Roo.data.MemoryProxy.superclass.constructor.call(this);
1376     this.data = data;
1377 };
1378
1379 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1380     
1381     /**
1382      * Load data from the requested source (in this case an in-memory
1383      * data object passed to the constructor), read the data object into
1384      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1385      * process that block using the passed callback.
1386      * @param {Object} params This parameter is not used by the MemoryProxy class.
1387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1388      * object into a block of Roo.data.Records.
1389      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1390      * The function must be passed <ul>
1391      * <li>The Record block object</li>
1392      * <li>The "arg" argument from the load function</li>
1393      * <li>A boolean success indicator</li>
1394      * </ul>
1395      * @param {Object} scope The scope in which to call the callback
1396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1397      */
1398     load : function(params, reader, callback, scope, arg){
1399         params = params || {};
1400         var result;
1401         try {
1402             result = reader.readRecords(params.data ? params.data :this.data);
1403         }catch(e){
1404             this.fireEvent("loadexception", this, arg, null, e);
1405             callback.call(scope, null, arg, false);
1406             return;
1407         }
1408         callback.call(scope, result, arg, true);
1409     },
1410     
1411     // private
1412     update : function(params, records){
1413         
1414     }
1415 });/*
1416  * Based on:
1417  * Ext JS Library 1.1.1
1418  * Copyright(c) 2006-2007, Ext JS, LLC.
1419  *
1420  * Originally Released Under LGPL - original licence link has changed is not relivant.
1421  *
1422  * Fork - LGPL
1423  * <script type="text/javascript">
1424  */
1425 /**
1426  * @class Roo.data.HttpProxy
1427  * @extends Roo.data.DataProxy
1428  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1429  * configured to reference a certain URL.<br><br>
1430  * <p>
1431  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1432  * from which the running page was served.<br><br>
1433  * <p>
1434  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1435  * <p>
1436  * Be aware that to enable the browser to parse an XML document, the server must set
1437  * the Content-Type header in the HTTP response to "text/xml".
1438  * @constructor
1439  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1440  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1441  * will be used to make the request.
1442  */
1443 Roo.data.HttpProxy = function(conn){
1444     Roo.data.HttpProxy.superclass.constructor.call(this);
1445     // is conn a conn config or a real conn?
1446     this.conn = conn;
1447     this.useAjax = !conn || !conn.events;
1448   
1449 };
1450
1451 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1452     // thse are take from connection...
1453     
1454     /**
1455      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1456      */
1457     /**
1458      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1459      * extra parameters to each request made by this object. (defaults to undefined)
1460      */
1461     /**
1462      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1463      *  to each request made by this object. (defaults to undefined)
1464      */
1465     /**
1466      * @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)
1467      */
1468     /**
1469      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1470      */
1471      /**
1472      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1473      * @type Boolean
1474      */
1475   
1476
1477     /**
1478      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1479      * @type Boolean
1480      */
1481     /**
1482      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1483      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1484      * a finer-grained basis than the DataProxy events.
1485      */
1486     getConnection : function(){
1487         return this.useAjax ? Roo.Ajax : this.conn;
1488     },
1489
1490     /**
1491      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1492      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1493      * process that block using the passed callback.
1494      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1495      * for the request to the remote server.
1496      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1497      * object into a block of Roo.data.Records.
1498      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1499      * The function must be passed <ul>
1500      * <li>The Record block object</li>
1501      * <li>The "arg" argument from the load function</li>
1502      * <li>A boolean success indicator</li>
1503      * </ul>
1504      * @param {Object} scope The scope in which to call the callback
1505      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1506      */
1507     load : function(params, reader, callback, scope, arg){
1508         if(this.fireEvent("beforeload", this, params) !== false){
1509             var  o = {
1510                 params : params || {},
1511                 request: {
1512                     callback : callback,
1513                     scope : scope,
1514                     arg : arg
1515                 },
1516                 reader: reader,
1517                 callback : this.loadResponse,
1518                 scope: this
1519             };
1520             if(this.useAjax){
1521                 Roo.applyIf(o, this.conn);
1522                 if(this.activeRequest){
1523                     Roo.Ajax.abort(this.activeRequest);
1524                 }
1525                 this.activeRequest = Roo.Ajax.request(o);
1526             }else{
1527                 this.conn.request(o);
1528             }
1529         }else{
1530             callback.call(scope||this, null, arg, false);
1531         }
1532     },
1533
1534     // private
1535     loadResponse : function(o, success, response){
1536         delete this.activeRequest;
1537         if(!success){
1538             this.fireEvent("loadexception", this, o, response);
1539             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1540             return;
1541         }
1542         var result;
1543         try {
1544             result = o.reader.read(response);
1545         }catch(e){
1546             this.fireEvent("loadexception", this, o, response, e);
1547             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1548             return;
1549         }
1550         
1551         this.fireEvent("load", this, o, o.request.arg);
1552         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1553     },
1554
1555     // private
1556     update : function(dataSet){
1557
1558     },
1559
1560     // private
1561     updateResponse : function(dataSet){
1562
1563     }
1564 });/*
1565  * Based on:
1566  * Ext JS Library 1.1.1
1567  * Copyright(c) 2006-2007, Ext JS, LLC.
1568  *
1569  * Originally Released Under LGPL - original licence link has changed is not relivant.
1570  *
1571  * Fork - LGPL
1572  * <script type="text/javascript">
1573  */
1574
1575 /**
1576  * @class Roo.data.ScriptTagProxy
1577  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1578  * other than the originating domain of the running page.<br><br>
1579  * <p>
1580  * <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
1581  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1582  * <p>
1583  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1584  * source code that is used as the source inside a &lt;script> tag.<br><br>
1585  * <p>
1586  * In order for the browser to process the returned data, the server must wrap the data object
1587  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1588  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1589  * depending on whether the callback name was passed:
1590  * <p>
1591  * <pre><code>
1592 boolean scriptTag = false;
1593 String cb = request.getParameter("callback");
1594 if (cb != null) {
1595     scriptTag = true;
1596     response.setContentType("text/javascript");
1597 } else {
1598     response.setContentType("application/x-json");
1599 }
1600 Writer out = response.getWriter();
1601 if (scriptTag) {
1602     out.write(cb + "(");
1603 }
1604 out.print(dataBlock.toJsonString());
1605 if (scriptTag) {
1606     out.write(");");
1607 }
1608 </pre></code>
1609  *
1610  * @constructor
1611  * @param {Object} config A configuration object.
1612  */
1613 Roo.data.ScriptTagProxy = function(config){
1614     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1615     Roo.apply(this, config);
1616     this.head = document.getElementsByTagName("head")[0];
1617 };
1618
1619 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1620
1621 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1622     /**
1623      * @cfg {String} url The URL from which to request the data object.
1624      */
1625     /**
1626      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1627      */
1628     timeout : 30000,
1629     /**
1630      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1631      * the server the name of the callback function set up by the load call to process the returned data object.
1632      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1633      * javascript output which calls this named function passing the data object as its only parameter.
1634      */
1635     callbackParam : "callback",
1636     /**
1637      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1638      * name to the request.
1639      */
1640     nocache : true,
1641
1642     /**
1643      * Load data from the configured URL, read the data object into
1644      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1645      * process that block using the passed callback.
1646      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1647      * for the request to the remote server.
1648      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1649      * object into a block of Roo.data.Records.
1650      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1651      * The function must be passed <ul>
1652      * <li>The Record block object</li>
1653      * <li>The "arg" argument from the load function</li>
1654      * <li>A boolean success indicator</li>
1655      * </ul>
1656      * @param {Object} scope The scope in which to call the callback
1657      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1658      */
1659     load : function(params, reader, callback, scope, arg){
1660         if(this.fireEvent("beforeload", this, params) !== false){
1661
1662             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1663
1664             var url = this.url;
1665             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1666             if(this.nocache){
1667                 url += "&_dc=" + (new Date().getTime());
1668             }
1669             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1670             var trans = {
1671                 id : transId,
1672                 cb : "stcCallback"+transId,
1673                 scriptId : "stcScript"+transId,
1674                 params : params,
1675                 arg : arg,
1676                 url : url,
1677                 callback : callback,
1678                 scope : scope,
1679                 reader : reader
1680             };
1681             var conn = this;
1682
1683             window[trans.cb] = function(o){
1684                 conn.handleResponse(o, trans);
1685             };
1686
1687             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1688
1689             if(this.autoAbort !== false){
1690                 this.abort();
1691             }
1692
1693             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1694
1695             var script = document.createElement("script");
1696             script.setAttribute("src", url);
1697             script.setAttribute("type", "text/javascript");
1698             script.setAttribute("id", trans.scriptId);
1699             this.head.appendChild(script);
1700
1701             this.trans = trans;
1702         }else{
1703             callback.call(scope||this, null, arg, false);
1704         }
1705     },
1706
1707     // private
1708     isLoading : function(){
1709         return this.trans ? true : false;
1710     },
1711
1712     /**
1713      * Abort the current server request.
1714      */
1715     abort : function(){
1716         if(this.isLoading()){
1717             this.destroyTrans(this.trans);
1718         }
1719     },
1720
1721     // private
1722     destroyTrans : function(trans, isLoaded){
1723         this.head.removeChild(document.getElementById(trans.scriptId));
1724         clearTimeout(trans.timeoutId);
1725         if(isLoaded){
1726             window[trans.cb] = undefined;
1727             try{
1728                 delete window[trans.cb];
1729             }catch(e){}
1730         }else{
1731             // if hasn't been loaded, wait for load to remove it to prevent script error
1732             window[trans.cb] = function(){
1733                 window[trans.cb] = undefined;
1734                 try{
1735                     delete window[trans.cb];
1736                 }catch(e){}
1737             };
1738         }
1739     },
1740
1741     // private
1742     handleResponse : function(o, trans){
1743         this.trans = false;
1744         this.destroyTrans(trans, true);
1745         var result;
1746         try {
1747             result = trans.reader.readRecords(o);
1748         }catch(e){
1749             this.fireEvent("loadexception", this, o, trans.arg, e);
1750             trans.callback.call(trans.scope||window, null, trans.arg, false);
1751             return;
1752         }
1753         this.fireEvent("load", this, o, trans.arg);
1754         trans.callback.call(trans.scope||window, result, trans.arg, true);
1755     },
1756
1757     // private
1758     handleFailure : function(trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, false);
1761         this.fireEvent("loadexception", this, null, trans.arg);
1762         trans.callback.call(trans.scope||window, null, trans.arg, false);
1763     }
1764 });/*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 /**
1776  * @class Roo.data.JsonReader
1777  * @extends Roo.data.DataReader
1778  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1779  * based on mappings in a provided Roo.data.Record constructor.
1780  * 
1781  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1782  * in the reply previously. 
1783  * 
1784  * <p>
1785  * Example code:
1786  * <pre><code>
1787 var RecordDef = Roo.data.Record.create([
1788     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1789     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1790 ]);
1791 var myReader = new Roo.data.JsonReader({
1792     totalProperty: "results",    // The property which contains the total dataset size (optional)
1793     root: "rows",                // The property which contains an Array of row objects
1794     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1795 }, RecordDef);
1796 </code></pre>
1797  * <p>
1798  * This would consume a JSON file like this:
1799  * <pre><code>
1800 { 'results': 2, 'rows': [
1801     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1802     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1803 }
1804 </code></pre>
1805  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1806  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1807  * paged from the remote server.
1808  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1809  * @cfg {String} root name of the property which contains the Array of row objects.
1810  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1811  * @cfg {Array} fields Array of field definition objects
1812  * @constructor
1813  * Create a new JsonReader
1814  * @param {Object} meta Metadata configuration options
1815  * @param {Object} recordType Either an Array of field definition objects,
1816  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1817  */
1818 Roo.data.JsonReader = function(meta, recordType){
1819     
1820     meta = meta || {};
1821     // set some defaults:
1822     Roo.applyIf(meta, {
1823         totalProperty: 'total',
1824         successProperty : 'success',
1825         root : 'data',
1826         id : 'id'
1827     });
1828     
1829     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1830 };
1831 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1832     
1833     /**
1834      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1835      * Used by Store query builder to append _requestMeta to params.
1836      * 
1837      */
1838     metaFromRemote : false,
1839     /**
1840      * This method is only used by a DataProxy which has retrieved data from a remote server.
1841      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1842      * @return {Object} data A data block which is used by an Roo.data.Store object as
1843      * a cache of Roo.data.Records.
1844      */
1845     read : function(response){
1846         var json = response.responseText;
1847        
1848         var o = /* eval:var:o */ eval("("+json+")");
1849         if(!o) {
1850             throw {message: "JsonReader.read: Json object not found"};
1851         }
1852         
1853         if(o.metaData){
1854             
1855             delete this.ef;
1856             this.metaFromRemote = true;
1857             this.meta = o.metaData;
1858             this.recordType = Roo.data.Record.create(o.metaData.fields);
1859             this.onMetaChange(this.meta, this.recordType, o);
1860         }
1861         return this.readRecords(o);
1862     },
1863
1864     // private function a store will implement
1865     onMetaChange : function(meta, recordType, o){
1866
1867     },
1868
1869     /**
1870          * @ignore
1871          */
1872     simpleAccess: function(obj, subsc) {
1873         return obj[subsc];
1874     },
1875
1876         /**
1877          * @ignore
1878          */
1879     getJsonAccessor: function(){
1880         var re = /[\[\.]/;
1881         return function(expr) {
1882             try {
1883                 return(re.test(expr))
1884                     ? new Function("obj", "return obj." + expr)
1885                     : function(obj){
1886                         return obj[expr];
1887                     };
1888             } catch(e){}
1889             return Roo.emptyFn;
1890         };
1891     }(),
1892
1893     /**
1894      * Create a data block containing Roo.data.Records from an XML document.
1895      * @param {Object} o An object which contains an Array of row objects in the property specified
1896      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1897      * which contains the total size of the dataset.
1898      * @return {Object} data A data block which is used by an Roo.data.Store object as
1899      * a cache of Roo.data.Records.
1900      */
1901     readRecords : function(o){
1902         /**
1903          * After any data loads, the raw JSON data is available for further custom processing.
1904          * @type Object
1905          */
1906         this.o = o;
1907         var s = this.meta, Record = this.recordType,
1908             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1909
1910 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1911         if (!this.ef) {
1912             if(s.totalProperty) {
1913                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1914                 }
1915                 if(s.successProperty) {
1916                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1917                 }
1918                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1919                 if (s.id) {
1920                         var g = this.getJsonAccessor(s.id);
1921                         this.getId = function(rec) {
1922                                 var r = g(rec);  
1923                                 return (r === undefined || r === "") ? null : r;
1924                         };
1925                 } else {
1926                         this.getId = function(){return null;};
1927                 }
1928             this.ef = [];
1929             for(var jj = 0; jj < fl; jj++){
1930                 f = fi[jj];
1931                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1932                 this.ef[jj] = this.getJsonAccessor(map);
1933             }
1934         }
1935
1936         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1937         if(s.totalProperty){
1938             var vt = parseInt(this.getTotal(o), 10);
1939             if(!isNaN(vt)){
1940                 totalRecords = vt;
1941             }
1942         }
1943         if(s.successProperty){
1944             var vs = this.getSuccess(o);
1945             if(vs === false || vs === 'false'){
1946                 success = false;
1947             }
1948         }
1949         var records = [];
1950         for(var i = 0; i < c; i++){
1951                 var n = root[i];
1952             var values = {};
1953             var id = this.getId(n);
1954             for(var j = 0; j < fl; j++){
1955                 f = fi[j];
1956             var v = this.ef[j](n);
1957             if (!f.convert) {
1958                 Roo.log('missing convert for ' + f.name);
1959                 Roo.log(f);
1960                 continue;
1961             }
1962             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1963             }
1964             var record = new Record(values, id);
1965             record.json = n;
1966             records[i] = record;
1967         }
1968         return {
1969             raw : o,
1970             success : success,
1971             records : records,
1972             totalRecords : totalRecords
1973         };
1974     }
1975 });/*
1976  * Based on:
1977  * Ext JS Library 1.1.1
1978  * Copyright(c) 2006-2007, Ext JS, LLC.
1979  *
1980  * Originally Released Under LGPL - original licence link has changed is not relivant.
1981  *
1982  * Fork - LGPL
1983  * <script type="text/javascript">
1984  */
1985
1986 /**
1987  * @class Roo.data.XmlReader
1988  * @extends Roo.data.DataReader
1989  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1990  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1991  * <p>
1992  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1993  * header in the HTTP response must be set to "text/xml".</em>
1994  * <p>
1995  * Example code:
1996  * <pre><code>
1997 var RecordDef = Roo.data.Record.create([
1998    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1999    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2000 ]);
2001 var myReader = new Roo.data.XmlReader({
2002    totalRecords: "results", // The element which contains the total dataset size (optional)
2003    record: "row",           // The repeated element which contains row information
2004    id: "id"                 // The element within the row that provides an ID for the record (optional)
2005 }, RecordDef);
2006 </code></pre>
2007  * <p>
2008  * This would consume an XML file like this:
2009  * <pre><code>
2010 &lt;?xml?>
2011 &lt;dataset>
2012  &lt;results>2&lt;/results>
2013  &lt;row>
2014    &lt;id>1&lt;/id>
2015    &lt;name>Bill&lt;/name>
2016    &lt;occupation>Gardener&lt;/occupation>
2017  &lt;/row>
2018  &lt;row>
2019    &lt;id>2&lt;/id>
2020    &lt;name>Ben&lt;/name>
2021    &lt;occupation>Horticulturalist&lt;/occupation>
2022  &lt;/row>
2023 &lt;/dataset>
2024 </code></pre>
2025  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2026  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2027  * paged from the remote server.
2028  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2029  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2030  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2031  * a record identifier value.
2032  * @constructor
2033  * Create a new XmlReader
2034  * @param {Object} meta Metadata configuration options
2035  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2036  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2037  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2038  */
2039 Roo.data.XmlReader = function(meta, recordType){
2040     meta = meta || {};
2041     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2042 };
2043 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2044     /**
2045      * This method is only used by a DataProxy which has retrieved data from a remote server.
2046          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2047          * to contain a method called 'responseXML' that returns an XML document object.
2048      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2049      * a cache of Roo.data.Records.
2050      */
2051     read : function(response){
2052         var doc = response.responseXML;
2053         if(!doc) {
2054             throw {message: "XmlReader.read: XML Document not available"};
2055         }
2056         return this.readRecords(doc);
2057     },
2058
2059     /**
2060      * Create a data block containing Roo.data.Records from an XML document.
2061          * @param {Object} doc A parsed XML document.
2062      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2063      * a cache of Roo.data.Records.
2064      */
2065     readRecords : function(doc){
2066         /**
2067          * After any data loads/reads, the raw XML Document is available for further custom processing.
2068          * @type XMLDocument
2069          */
2070         this.xmlData = doc;
2071         var root = doc.documentElement || doc;
2072         var q = Roo.DomQuery;
2073         var recordType = this.recordType, fields = recordType.prototype.fields;
2074         var sid = this.meta.id;
2075         var totalRecords = 0, success = true;
2076         if(this.meta.totalRecords){
2077             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2078         }
2079         
2080         if(this.meta.success){
2081             var sv = q.selectValue(this.meta.success, root, true);
2082             success = sv !== false && sv !== 'false';
2083         }
2084         var records = [];
2085         var ns = q.select(this.meta.record, root);
2086         for(var i = 0, len = ns.length; i < len; i++) {
2087                 var n = ns[i];
2088                 var values = {};
2089                 var id = sid ? q.selectValue(sid, n) : undefined;
2090                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2091                     var f = fields.items[j];
2092                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2093                     v = f.convert(v);
2094                     values[f.name] = v;
2095                 }
2096                 var record = new recordType(values, id);
2097                 record.node = n;
2098                 records[records.length] = record;
2099             }
2100
2101             return {
2102                 success : success,
2103                 records : records,
2104                 totalRecords : totalRecords || records.length
2105             };
2106     }
2107 });/*
2108  * Based on:
2109  * Ext JS Library 1.1.1
2110  * Copyright(c) 2006-2007, Ext JS, LLC.
2111  *
2112  * Originally Released Under LGPL - original licence link has changed is not relivant.
2113  *
2114  * Fork - LGPL
2115  * <script type="text/javascript">
2116  */
2117
2118 /**
2119  * @class Roo.data.ArrayReader
2120  * @extends Roo.data.DataReader
2121  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2122  * Each element of that Array represents a row of data fields. The
2123  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2124  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2125  * <p>
2126  * Example code:.
2127  * <pre><code>
2128 var RecordDef = Roo.data.Record.create([
2129     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2130     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2131 ]);
2132 var myReader = new Roo.data.ArrayReader({
2133     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2134 }, RecordDef);
2135 </code></pre>
2136  * <p>
2137  * This would consume an Array like this:
2138  * <pre><code>
2139 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2140   </code></pre>
2141  
2142  * @constructor
2143  * Create a new JsonReader
2144  * @param {Object} meta Metadata configuration options.
2145  * @param {Object|Array} recordType Either an Array of field definition objects
2146  * 
2147  * @cfg {Array} fields Array of field definition objects
2148  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2149  * as specified to {@link Roo.data.Record#create},
2150  * or an {@link Roo.data.Record} object
2151  *
2152  * 
2153  * created using {@link Roo.data.Record#create}.
2154  */
2155 Roo.data.ArrayReader = function(meta, recordType){
2156     
2157      
2158     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2159 };
2160
2161 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2162     /**
2163      * Create a data block containing Roo.data.Records from an XML document.
2164      * @param {Object} o An Array of row objects which represents the dataset.
2165      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2166      * a cache of Roo.data.Records.
2167      */
2168     readRecords : function(o){
2169         var sid = this.meta ? this.meta.id : null;
2170         var recordType = this.recordType, fields = recordType.prototype.fields;
2171         var records = [];
2172         var root = o;
2173             for(var i = 0; i < root.length; i++){
2174                     var n = root[i];
2175                 var values = {};
2176                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2177                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2178                 var f = fields.items[j];
2179                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2180                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2181                 v = f.convert(v);
2182                 values[f.name] = v;
2183             }
2184                 var record = new recordType(values, id);
2185                 record.json = n;
2186                 records[records.length] = record;
2187             }
2188             return {
2189                 records : records,
2190                 totalRecords : records.length
2191             };
2192     }
2193 });/*
2194  * Based on:
2195  * Ext JS Library 1.1.1
2196  * Copyright(c) 2006-2007, Ext JS, LLC.
2197  *
2198  * Originally Released Under LGPL - original licence link has changed is not relivant.
2199  *
2200  * Fork - LGPL
2201  * <script type="text/javascript">
2202  */
2203
2204
2205 /**
2206  * @class Roo.data.Tree
2207  * @extends Roo.util.Observable
2208  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2209  * in the tree have most standard DOM functionality.
2210  * @constructor
2211  * @param {Node} root (optional) The root node
2212  */
2213 Roo.data.Tree = function(root){
2214    this.nodeHash = {};
2215    /**
2216     * The root node for this tree
2217     * @type Node
2218     */
2219    this.root = null;
2220    if(root){
2221        this.setRootNode(root);
2222    }
2223    this.addEvents({
2224        /**
2225         * @event append
2226         * Fires when a new child node is appended to a node in this tree.
2227         * @param {Tree} tree The owner tree
2228         * @param {Node} parent The parent node
2229         * @param {Node} node The newly appended node
2230         * @param {Number} index The index of the newly appended node
2231         */
2232        "append" : true,
2233        /**
2234         * @event remove
2235         * Fires when a child node is removed from a node in this tree.
2236         * @param {Tree} tree The owner tree
2237         * @param {Node} parent The parent node
2238         * @param {Node} node The child node removed
2239         */
2240        "remove" : true,
2241        /**
2242         * @event move
2243         * Fires when a node is moved to a new location in the tree
2244         * @param {Tree} tree The owner tree
2245         * @param {Node} node The node moved
2246         * @param {Node} oldParent The old parent of this node
2247         * @param {Node} newParent The new parent of this node
2248         * @param {Number} index The index it was moved to
2249         */
2250        "move" : true,
2251        /**
2252         * @event insert
2253         * Fires when a new child node is inserted in a node in this tree.
2254         * @param {Tree} tree The owner tree
2255         * @param {Node} parent The parent node
2256         * @param {Node} node The child node inserted
2257         * @param {Node} refNode The child node the node was inserted before
2258         */
2259        "insert" : true,
2260        /**
2261         * @event beforeappend
2262         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2263         * @param {Tree} tree The owner tree
2264         * @param {Node} parent The parent node
2265         * @param {Node} node The child node to be appended
2266         */
2267        "beforeappend" : true,
2268        /**
2269         * @event beforeremove
2270         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2271         * @param {Tree} tree The owner tree
2272         * @param {Node} parent The parent node
2273         * @param {Node} node The child node to be removed
2274         */
2275        "beforeremove" : true,
2276        /**
2277         * @event beforemove
2278         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} node The node being moved
2281         * @param {Node} oldParent The parent of the node
2282         * @param {Node} newParent The new parent the node is moving to
2283         * @param {Number} index The index it is being moved to
2284         */
2285        "beforemove" : true,
2286        /**
2287         * @event beforeinsert
2288         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2289         * @param {Tree} tree The owner tree
2290         * @param {Node} parent The parent node
2291         * @param {Node} node The child node to be inserted
2292         * @param {Node} refNode The child node the node is being inserted before
2293         */
2294        "beforeinsert" : true
2295    });
2296
2297     Roo.data.Tree.superclass.constructor.call(this);
2298 };
2299
2300 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2301     pathSeparator: "/",
2302
2303     proxyNodeEvent : function(){
2304         return this.fireEvent.apply(this, arguments);
2305     },
2306
2307     /**
2308      * Returns the root node for this tree.
2309      * @return {Node}
2310      */
2311     getRootNode : function(){
2312         return this.root;
2313     },
2314
2315     /**
2316      * Sets the root node for this tree.
2317      * @param {Node} node
2318      * @return {Node}
2319      */
2320     setRootNode : function(node){
2321         this.root = node;
2322         node.ownerTree = this;
2323         node.isRoot = true;
2324         this.registerNode(node);
2325         return node;
2326     },
2327
2328     /**
2329      * Gets a node in this tree by its id.
2330      * @param {String} id
2331      * @return {Node}
2332      */
2333     getNodeById : function(id){
2334         return this.nodeHash[id];
2335     },
2336
2337     registerNode : function(node){
2338         this.nodeHash[node.id] = node;
2339     },
2340
2341     unregisterNode : function(node){
2342         delete this.nodeHash[node.id];
2343     },
2344
2345     toString : function(){
2346         return "[Tree"+(this.id?" "+this.id:"")+"]";
2347     }
2348 });
2349
2350 /**
2351  * @class Roo.data.Node
2352  * @extends Roo.util.Observable
2353  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2354  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2355  * @constructor
2356  * @param {Object} attributes The attributes/config for the node
2357  */
2358 Roo.data.Node = function(attributes){
2359     /**
2360      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2361      * @type {Object}
2362      */
2363     this.attributes = attributes || {};
2364     this.leaf = this.attributes.leaf;
2365     /**
2366      * The node id. @type String
2367      */
2368     this.id = this.attributes.id;
2369     if(!this.id){
2370         this.id = Roo.id(null, "ynode-");
2371         this.attributes.id = this.id;
2372     }
2373      
2374     
2375     /**
2376      * All child nodes of this node. @type Array
2377      */
2378     this.childNodes = [];
2379     if(!this.childNodes.indexOf){ // indexOf is a must
2380         this.childNodes.indexOf = function(o){
2381             for(var i = 0, len = this.length; i < len; i++){
2382                 if(this[i] == o) {
2383                     return i;
2384                 }
2385             }
2386             return -1;
2387         };
2388     }
2389     /**
2390      * The parent node for this node. @type Node
2391      */
2392     this.parentNode = null;
2393     /**
2394      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2395      */
2396     this.firstChild = null;
2397     /**
2398      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2399      */
2400     this.lastChild = null;
2401     /**
2402      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2403      */
2404     this.previousSibling = null;
2405     /**
2406      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2407      */
2408     this.nextSibling = null;
2409
2410     this.addEvents({
2411        /**
2412         * @event append
2413         * Fires when a new child node is appended
2414         * @param {Tree} tree The owner tree
2415         * @param {Node} this This node
2416         * @param {Node} node The newly appended node
2417         * @param {Number} index The index of the newly appended node
2418         */
2419        "append" : true,
2420        /**
2421         * @event remove
2422         * Fires when a child node is removed
2423         * @param {Tree} tree The owner tree
2424         * @param {Node} this This node
2425         * @param {Node} node The removed node
2426         */
2427        "remove" : true,
2428        /**
2429         * @event move
2430         * Fires when this node is moved to a new location in the tree
2431         * @param {Tree} tree The owner tree
2432         * @param {Node} this This node
2433         * @param {Node} oldParent The old parent of this node
2434         * @param {Node} newParent The new parent of this node
2435         * @param {Number} index The index it was moved to
2436         */
2437        "move" : true,
2438        /**
2439         * @event insert
2440         * Fires when a new child node is inserted.
2441         * @param {Tree} tree The owner tree
2442         * @param {Node} this This node
2443         * @param {Node} node The child node inserted
2444         * @param {Node} refNode The child node the node was inserted before
2445         */
2446        "insert" : true,
2447        /**
2448         * @event beforeappend
2449         * Fires before a new child is appended, return false to cancel the append.
2450         * @param {Tree} tree The owner tree
2451         * @param {Node} this This node
2452         * @param {Node} node The child node to be appended
2453         */
2454        "beforeappend" : true,
2455        /**
2456         * @event beforeremove
2457         * Fires before a child is removed, return false to cancel the remove.
2458         * @param {Tree} tree The owner tree
2459         * @param {Node} this This node
2460         * @param {Node} node The child node to be removed
2461         */
2462        "beforeremove" : true,
2463        /**
2464         * @event beforemove
2465         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} oldParent The parent of this node
2469         * @param {Node} newParent The new parent this node is moving to
2470         * @param {Number} index The index it is being moved to
2471         */
2472        "beforemove" : true,
2473        /**
2474         * @event beforeinsert
2475         * Fires before a new child is inserted, return false to cancel the insert.
2476         * @param {Tree} tree The owner tree
2477         * @param {Node} this This node
2478         * @param {Node} node The child node to be inserted
2479         * @param {Node} refNode The child node the node is being inserted before
2480         */
2481        "beforeinsert" : true
2482    });
2483     this.listeners = this.attributes.listeners;
2484     Roo.data.Node.superclass.constructor.call(this);
2485 };
2486
2487 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2488     fireEvent : function(evtName){
2489         // first do standard event for this node
2490         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2491             return false;
2492         }
2493         // then bubble it up to the tree if the event wasn't cancelled
2494         var ot = this.getOwnerTree();
2495         if(ot){
2496             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2497                 return false;
2498             }
2499         }
2500         return true;
2501     },
2502
2503     /**
2504      * Returns true if this node is a leaf
2505      * @return {Boolean}
2506      */
2507     isLeaf : function(){
2508         return this.leaf === true;
2509     },
2510
2511     // private
2512     setFirstChild : function(node){
2513         this.firstChild = node;
2514     },
2515
2516     //private
2517     setLastChild : function(node){
2518         this.lastChild = node;
2519     },
2520
2521
2522     /**
2523      * Returns true if this node is the last child of its parent
2524      * @return {Boolean}
2525      */
2526     isLast : function(){
2527        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2528     },
2529
2530     /**
2531      * Returns true if this node is the first child of its parent
2532      * @return {Boolean}
2533      */
2534     isFirst : function(){
2535        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2536     },
2537
2538     hasChildNodes : function(){
2539         return !this.isLeaf() && this.childNodes.length > 0;
2540     },
2541
2542     /**
2543      * Insert node(s) as the last child node of this node.
2544      * @param {Node/Array} node The node or Array of nodes to append
2545      * @return {Node} The appended node if single append, or null if an array was passed
2546      */
2547     appendChild : function(node){
2548         var multi = false;
2549         if(node instanceof Array){
2550             multi = node;
2551         }else if(arguments.length > 1){
2552             multi = arguments;
2553         }
2554         // if passed an array or multiple args do them one by one
2555         if(multi){
2556             for(var i = 0, len = multi.length; i < len; i++) {
2557                 this.appendChild(multi[i]);
2558             }
2559         }else{
2560             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2561                 return false;
2562             }
2563             var index = this.childNodes.length;
2564             var oldParent = node.parentNode;
2565             // it's a move, make sure we move it cleanly
2566             if(oldParent){
2567                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2568                     return false;
2569                 }
2570                 oldParent.removeChild(node);
2571             }
2572             index = this.childNodes.length;
2573             if(index == 0){
2574                 this.setFirstChild(node);
2575             }
2576             this.childNodes.push(node);
2577             node.parentNode = this;
2578             var ps = this.childNodes[index-1];
2579             if(ps){
2580                 node.previousSibling = ps;
2581                 ps.nextSibling = node;
2582             }else{
2583                 node.previousSibling = null;
2584             }
2585             node.nextSibling = null;
2586             this.setLastChild(node);
2587             node.setOwnerTree(this.getOwnerTree());
2588             this.fireEvent("append", this.ownerTree, this, node, index);
2589             if(oldParent){
2590                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2591             }
2592             return node;
2593         }
2594     },
2595
2596     /**
2597      * Removes a child node from this node.
2598      * @param {Node} node The node to remove
2599      * @return {Node} The removed node
2600      */
2601     removeChild : function(node){
2602         var index = this.childNodes.indexOf(node);
2603         if(index == -1){
2604             return false;
2605         }
2606         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2607             return false;
2608         }
2609
2610         // remove it from childNodes collection
2611         this.childNodes.splice(index, 1);
2612
2613         // update siblings
2614         if(node.previousSibling){
2615             node.previousSibling.nextSibling = node.nextSibling;
2616         }
2617         if(node.nextSibling){
2618             node.nextSibling.previousSibling = node.previousSibling;
2619         }
2620
2621         // update child refs
2622         if(this.firstChild == node){
2623             this.setFirstChild(node.nextSibling);
2624         }
2625         if(this.lastChild == node){
2626             this.setLastChild(node.previousSibling);
2627         }
2628
2629         node.setOwnerTree(null);
2630         // clear any references from the node
2631         node.parentNode = null;
2632         node.previousSibling = null;
2633         node.nextSibling = null;
2634         this.fireEvent("remove", this.ownerTree, this, node);
2635         return node;
2636     },
2637
2638     /**
2639      * Inserts the first node before the second node in this nodes childNodes collection.
2640      * @param {Node} node The node to insert
2641      * @param {Node} refNode The node to insert before (if null the node is appended)
2642      * @return {Node} The inserted node
2643      */
2644     insertBefore : function(node, refNode){
2645         if(!refNode){ // like standard Dom, refNode can be null for append
2646             return this.appendChild(node);
2647         }
2648         // nothing to do
2649         if(node == refNode){
2650             return false;
2651         }
2652
2653         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2654             return false;
2655         }
2656         var index = this.childNodes.indexOf(refNode);
2657         var oldParent = node.parentNode;
2658         var refIndex = index;
2659
2660         // when moving internally, indexes will change after remove
2661         if(oldParent == this && this.childNodes.indexOf(node) < index){
2662             refIndex--;
2663         }
2664
2665         // it's a move, make sure we move it cleanly
2666         if(oldParent){
2667             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2668                 return false;
2669             }
2670             oldParent.removeChild(node);
2671         }
2672         if(refIndex == 0){
2673             this.setFirstChild(node);
2674         }
2675         this.childNodes.splice(refIndex, 0, node);
2676         node.parentNode = this;
2677         var ps = this.childNodes[refIndex-1];
2678         if(ps){
2679             node.previousSibling = ps;
2680             ps.nextSibling = node;
2681         }else{
2682             node.previousSibling = null;
2683         }
2684         node.nextSibling = refNode;
2685         refNode.previousSibling = node;
2686         node.setOwnerTree(this.getOwnerTree());
2687         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2688         if(oldParent){
2689             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2690         }
2691         return node;
2692     },
2693
2694     /**
2695      * Returns the child node at the specified index.
2696      * @param {Number} index
2697      * @return {Node}
2698      */
2699     item : function(index){
2700         return this.childNodes[index];
2701     },
2702
2703     /**
2704      * Replaces one child node in this node with another.
2705      * @param {Node} newChild The replacement node
2706      * @param {Node} oldChild The node to replace
2707      * @return {Node} The replaced node
2708      */
2709     replaceChild : function(newChild, oldChild){
2710         this.insertBefore(newChild, oldChild);
2711         this.removeChild(oldChild);
2712         return oldChild;
2713     },
2714
2715     /**
2716      * Returns the index of a child node
2717      * @param {Node} node
2718      * @return {Number} The index of the node or -1 if it was not found
2719      */
2720     indexOf : function(child){
2721         return this.childNodes.indexOf(child);
2722     },
2723
2724     /**
2725      * Returns the tree this node is in.
2726      * @return {Tree}
2727      */
2728     getOwnerTree : function(){
2729         // if it doesn't have one, look for one
2730         if(!this.ownerTree){
2731             var p = this;
2732             while(p){
2733                 if(p.ownerTree){
2734                     this.ownerTree = p.ownerTree;
2735                     break;
2736                 }
2737                 p = p.parentNode;
2738             }
2739         }
2740         return this.ownerTree;
2741     },
2742
2743     /**
2744      * Returns depth of this node (the root node has a depth of 0)
2745      * @return {Number}
2746      */
2747     getDepth : function(){
2748         var depth = 0;
2749         var p = this;
2750         while(p.parentNode){
2751             ++depth;
2752             p = p.parentNode;
2753         }
2754         return depth;
2755     },
2756
2757     // private
2758     setOwnerTree : function(tree){
2759         // if it's move, we need to update everyone
2760         if(tree != this.ownerTree){
2761             if(this.ownerTree){
2762                 this.ownerTree.unregisterNode(this);
2763             }
2764             this.ownerTree = tree;
2765             var cs = this.childNodes;
2766             for(var i = 0, len = cs.length; i < len; i++) {
2767                 cs[i].setOwnerTree(tree);
2768             }
2769             if(tree){
2770                 tree.registerNode(this);
2771             }
2772         }
2773     },
2774
2775     /**
2776      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2777      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2778      * @return {String} The path
2779      */
2780     getPath : function(attr){
2781         attr = attr || "id";
2782         var p = this.parentNode;
2783         var b = [this.attributes[attr]];
2784         while(p){
2785             b.unshift(p.attributes[attr]);
2786             p = p.parentNode;
2787         }
2788         var sep = this.getOwnerTree().pathSeparator;
2789         return sep + b.join(sep);
2790     },
2791
2792     /**
2793      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2794      * function call will be the scope provided or the current node. The arguments to the function
2795      * will be the args provided or the current node. If the function returns false at any point,
2796      * the bubble is stopped.
2797      * @param {Function} fn The function to call
2798      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2799      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2800      */
2801     bubble : function(fn, scope, args){
2802         var p = this;
2803         while(p){
2804             if(fn.call(scope || p, args || p) === false){
2805                 break;
2806             }
2807             p = p.parentNode;
2808         }
2809     },
2810
2811     /**
2812      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2813      * function call will be the scope provided or the current node. The arguments to the function
2814      * will be the args provided or the current node. If the function returns false at any point,
2815      * the cascade is stopped on that branch.
2816      * @param {Function} fn The function to call
2817      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2818      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2819      */
2820     cascade : function(fn, scope, args){
2821         if(fn.call(scope || this, args || this) !== false){
2822             var cs = this.childNodes;
2823             for(var i = 0, len = cs.length; i < len; i++) {
2824                 cs[i].cascade(fn, scope, args);
2825             }
2826         }
2827     },
2828
2829     /**
2830      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2831      * function call will be the scope provided or the current node. The arguments to the function
2832      * will be the args provided or the current node. If the function returns false at any point,
2833      * the iteration stops.
2834      * @param {Function} fn The function to call
2835      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2836      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2837      */
2838     eachChild : function(fn, scope, args){
2839         var cs = this.childNodes;
2840         for(var i = 0, len = cs.length; i < len; i++) {
2841                 if(fn.call(scope || this, args || cs[i]) === false){
2842                     break;
2843                 }
2844         }
2845     },
2846
2847     /**
2848      * Finds the first child that has the attribute with the specified value.
2849      * @param {String} attribute The attribute name
2850      * @param {Mixed} value The value to search for
2851      * @return {Node} The found child or null if none was found
2852      */
2853     findChild : function(attribute, value){
2854         var cs = this.childNodes;
2855         for(var i = 0, len = cs.length; i < len; i++) {
2856                 if(cs[i].attributes[attribute] == value){
2857                     return cs[i];
2858                 }
2859         }
2860         return null;
2861     },
2862
2863     /**
2864      * Finds the first child by a custom function. The child matches if the function passed
2865      * returns true.
2866      * @param {Function} fn
2867      * @param {Object} scope (optional)
2868      * @return {Node} The found child or null if none was found
2869      */
2870     findChildBy : function(fn, scope){
2871         var cs = this.childNodes;
2872         for(var i = 0, len = cs.length; i < len; i++) {
2873                 if(fn.call(scope||cs[i], cs[i]) === true){
2874                     return cs[i];
2875                 }
2876         }
2877         return null;
2878     },
2879
2880     /**
2881      * Sorts this nodes children using the supplied sort function
2882      * @param {Function} fn
2883      * @param {Object} scope (optional)
2884      */
2885     sort : function(fn, scope){
2886         var cs = this.childNodes;
2887         var len = cs.length;
2888         if(len > 0){
2889             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2890             cs.sort(sortFn);
2891             for(var i = 0; i < len; i++){
2892                 var n = cs[i];
2893                 n.previousSibling = cs[i-1];
2894                 n.nextSibling = cs[i+1];
2895                 if(i == 0){
2896                     this.setFirstChild(n);
2897                 }
2898                 if(i == len-1){
2899                     this.setLastChild(n);
2900                 }
2901             }
2902         }
2903     },
2904
2905     /**
2906      * Returns true if this node is an ancestor (at any point) of the passed node.
2907      * @param {Node} node
2908      * @return {Boolean}
2909      */
2910     contains : function(node){
2911         return node.isAncestor(this);
2912     },
2913
2914     /**
2915      * Returns true if the passed node is an ancestor (at any point) of this node.
2916      * @param {Node} node
2917      * @return {Boolean}
2918      */
2919     isAncestor : function(node){
2920         var p = this.parentNode;
2921         while(p){
2922             if(p == node){
2923                 return true;
2924             }
2925             p = p.parentNode;
2926         }
2927         return false;
2928     },
2929
2930     toString : function(){
2931         return "[Node"+(this.id?" "+this.id:"")+"]";
2932     }
2933 });/*
2934  * Based on:
2935  * Ext JS Library 1.1.1
2936  * Copyright(c) 2006-2007, Ext JS, LLC.
2937  *
2938  * Originally Released Under LGPL - original licence link has changed is not relivant.
2939  *
2940  * Fork - LGPL
2941  * <script type="text/javascript">
2942  */
2943  (function(){ 
2944 /**
2945  * @class Roo.Layer
2946  * @extends Roo.Element
2947  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2948  * automatic maintaining of shadow/shim positions.
2949  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2950  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2951  * you can pass a string with a CSS class name. False turns off the shadow.
2952  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2953  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2954  * @cfg {String} cls CSS class to add to the element
2955  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2956  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2957  * @constructor
2958  * @param {Object} config An object with config options.
2959  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2960  */
2961
2962 Roo.Layer = function(config, existingEl){
2963     config = config || {};
2964     var dh = Roo.DomHelper;
2965     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2966     if(existingEl){
2967         this.dom = Roo.getDom(existingEl);
2968     }
2969     if(!this.dom){
2970         var o = config.dh || {tag: "div", cls: "x-layer"};
2971         this.dom = dh.append(pel, o);
2972     }
2973     if(config.cls){
2974         this.addClass(config.cls);
2975     }
2976     this.constrain = config.constrain !== false;
2977     this.visibilityMode = Roo.Element.VISIBILITY;
2978     if(config.id){
2979         this.id = this.dom.id = config.id;
2980     }else{
2981         this.id = Roo.id(this.dom);
2982     }
2983     this.zindex = config.zindex || this.getZIndex();
2984     this.position("absolute", this.zindex);
2985     if(config.shadow){
2986         this.shadowOffset = config.shadowOffset || 4;
2987         this.shadow = new Roo.Shadow({
2988             offset : this.shadowOffset,
2989             mode : config.shadow
2990         });
2991     }else{
2992         this.shadowOffset = 0;
2993     }
2994     this.useShim = config.shim !== false && Roo.useShims;
2995     this.useDisplay = config.useDisplay;
2996     this.hide();
2997 };
2998
2999 var supr = Roo.Element.prototype;
3000
3001 // shims are shared among layer to keep from having 100 iframes
3002 var shims = [];
3003
3004 Roo.extend(Roo.Layer, Roo.Element, {
3005
3006     getZIndex : function(){
3007         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3008     },
3009
3010     getShim : function(){
3011         if(!this.useShim){
3012             return null;
3013         }
3014         if(this.shim){
3015             return this.shim;
3016         }
3017         var shim = shims.shift();
3018         if(!shim){
3019             shim = this.createShim();
3020             shim.enableDisplayMode('block');
3021             shim.dom.style.display = 'none';
3022             shim.dom.style.visibility = 'visible';
3023         }
3024         var pn = this.dom.parentNode;
3025         if(shim.dom.parentNode != pn){
3026             pn.insertBefore(shim.dom, this.dom);
3027         }
3028         shim.setStyle('z-index', this.getZIndex()-2);
3029         this.shim = shim;
3030         return shim;
3031     },
3032
3033     hideShim : function(){
3034         if(this.shim){
3035             this.shim.setDisplayed(false);
3036             shims.push(this.shim);
3037             delete this.shim;
3038         }
3039     },
3040
3041     disableShadow : function(){
3042         if(this.shadow){
3043             this.shadowDisabled = true;
3044             this.shadow.hide();
3045             this.lastShadowOffset = this.shadowOffset;
3046             this.shadowOffset = 0;
3047         }
3048     },
3049
3050     enableShadow : function(show){
3051         if(this.shadow){
3052             this.shadowDisabled = false;
3053             this.shadowOffset = this.lastShadowOffset;
3054             delete this.lastShadowOffset;
3055             if(show){
3056                 this.sync(true);
3057             }
3058         }
3059     },
3060
3061     // private
3062     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3063     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3064     sync : function(doShow){
3065         var sw = this.shadow;
3066         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3067             var sh = this.getShim();
3068
3069             var w = this.getWidth(),
3070                 h = this.getHeight();
3071
3072             var l = this.getLeft(true),
3073                 t = this.getTop(true);
3074
3075             if(sw && !this.shadowDisabled){
3076                 if(doShow && !sw.isVisible()){
3077                     sw.show(this);
3078                 }else{
3079                     sw.realign(l, t, w, h);
3080                 }
3081                 if(sh){
3082                     if(doShow){
3083                        sh.show();
3084                     }
3085                     // fit the shim behind the shadow, so it is shimmed too
3086                     var a = sw.adjusts, s = sh.dom.style;
3087                     s.left = (Math.min(l, l+a.l))+"px";
3088                     s.top = (Math.min(t, t+a.t))+"px";
3089                     s.width = (w+a.w)+"px";
3090                     s.height = (h+a.h)+"px";
3091                 }
3092             }else if(sh){
3093                 if(doShow){
3094                    sh.show();
3095                 }
3096                 sh.setSize(w, h);
3097                 sh.setLeftTop(l, t);
3098             }
3099             
3100         }
3101     },
3102
3103     // private
3104     destroy : function(){
3105         this.hideShim();
3106         if(this.shadow){
3107             this.shadow.hide();
3108         }
3109         this.removeAllListeners();
3110         var pn = this.dom.parentNode;
3111         if(pn){
3112             pn.removeChild(this.dom);
3113         }
3114         Roo.Element.uncache(this.id);
3115     },
3116
3117     remove : function(){
3118         this.destroy();
3119     },
3120
3121     // private
3122     beginUpdate : function(){
3123         this.updating = true;
3124     },
3125
3126     // private
3127     endUpdate : function(){
3128         this.updating = false;
3129         this.sync(true);
3130     },
3131
3132     // private
3133     hideUnders : function(negOffset){
3134         if(this.shadow){
3135             this.shadow.hide();
3136         }
3137         this.hideShim();
3138     },
3139
3140     // private
3141     constrainXY : function(){
3142         if(this.constrain){
3143             var vw = Roo.lib.Dom.getViewWidth(),
3144                 vh = Roo.lib.Dom.getViewHeight();
3145             var s = Roo.get(document).getScroll();
3146
3147             var xy = this.getXY();
3148             var x = xy[0], y = xy[1];   
3149             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3150             // only move it if it needs it
3151             var moved = false;
3152             // first validate right/bottom
3153             if((x + w) > vw+s.left){
3154                 x = vw - w - this.shadowOffset;
3155                 moved = true;
3156             }
3157             if((y + h) > vh+s.top){
3158                 y = vh - h - this.shadowOffset;
3159                 moved = true;
3160             }
3161             // then make sure top/left isn't negative
3162             if(x < s.left){
3163                 x = s.left;
3164                 moved = true;
3165             }
3166             if(y < s.top){
3167                 y = s.top;
3168                 moved = true;
3169             }
3170             if(moved){
3171                 if(this.avoidY){
3172                     var ay = this.avoidY;
3173                     if(y <= ay && (y+h) >= ay){
3174                         y = ay-h-5;   
3175                     }
3176                 }
3177                 xy = [x, y];
3178                 this.storeXY(xy);
3179                 supr.setXY.call(this, xy);
3180                 this.sync();
3181             }
3182         }
3183     },
3184
3185     isVisible : function(){
3186         return this.visible;    
3187     },
3188
3189     // private
3190     showAction : function(){
3191         this.visible = true; // track visibility to prevent getStyle calls
3192         if(this.useDisplay === true){
3193             this.setDisplayed("");
3194         }else if(this.lastXY){
3195             supr.setXY.call(this, this.lastXY);
3196         }else if(this.lastLT){
3197             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3198         }
3199     },
3200
3201     // private
3202     hideAction : function(){
3203         this.visible = false;
3204         if(this.useDisplay === true){
3205             this.setDisplayed(false);
3206         }else{
3207             this.setLeftTop(-10000,-10000);
3208         }
3209     },
3210
3211     // overridden Element method
3212     setVisible : function(v, a, d, c, e){
3213         if(v){
3214             this.showAction();
3215         }
3216         if(a && v){
3217             var cb = function(){
3218                 this.sync(true);
3219                 if(c){
3220                     c();
3221                 }
3222             }.createDelegate(this);
3223             supr.setVisible.call(this, true, true, d, cb, e);
3224         }else{
3225             if(!v){
3226                 this.hideUnders(true);
3227             }
3228             var cb = c;
3229             if(a){
3230                 cb = function(){
3231                     this.hideAction();
3232                     if(c){
3233                         c();
3234                     }
3235                 }.createDelegate(this);
3236             }
3237             supr.setVisible.call(this, v, a, d, cb, e);
3238             if(v){
3239                 this.sync(true);
3240             }else if(!a){
3241                 this.hideAction();
3242             }
3243         }
3244     },
3245
3246     storeXY : function(xy){
3247         delete this.lastLT;
3248         this.lastXY = xy;
3249     },
3250
3251     storeLeftTop : function(left, top){
3252         delete this.lastXY;
3253         this.lastLT = [left, top];
3254     },
3255
3256     // private
3257     beforeFx : function(){
3258         this.beforeAction();
3259         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3260     },
3261
3262     // private
3263     afterFx : function(){
3264         Roo.Layer.superclass.afterFx.apply(this, arguments);
3265         this.sync(this.isVisible());
3266     },
3267
3268     // private
3269     beforeAction : function(){
3270         if(!this.updating && this.shadow){
3271             this.shadow.hide();
3272         }
3273     },
3274
3275     // overridden Element method
3276     setLeft : function(left){
3277         this.storeLeftTop(left, this.getTop(true));
3278         supr.setLeft.apply(this, arguments);
3279         this.sync();
3280     },
3281
3282     setTop : function(top){
3283         this.storeLeftTop(this.getLeft(true), top);
3284         supr.setTop.apply(this, arguments);
3285         this.sync();
3286     },
3287
3288     setLeftTop : function(left, top){
3289         this.storeLeftTop(left, top);
3290         supr.setLeftTop.apply(this, arguments);
3291         this.sync();
3292     },
3293
3294     setXY : function(xy, a, d, c, e){
3295         this.fixDisplay();
3296         this.beforeAction();
3297         this.storeXY(xy);
3298         var cb = this.createCB(c);
3299         supr.setXY.call(this, xy, a, d, cb, e);
3300         if(!a){
3301             cb();
3302         }
3303     },
3304
3305     // private
3306     createCB : function(c){
3307         var el = this;
3308         return function(){
3309             el.constrainXY();
3310             el.sync(true);
3311             if(c){
3312                 c();
3313             }
3314         };
3315     },
3316
3317     // overridden Element method
3318     setX : function(x, a, d, c, e){
3319         this.setXY([x, this.getY()], a, d, c, e);
3320     },
3321
3322     // overridden Element method
3323     setY : function(y, a, d, c, e){
3324         this.setXY([this.getX(), y], a, d, c, e);
3325     },
3326
3327     // overridden Element method
3328     setSize : function(w, h, a, d, c, e){
3329         this.beforeAction();
3330         var cb = this.createCB(c);
3331         supr.setSize.call(this, w, h, a, d, cb, e);
3332         if(!a){
3333             cb();
3334         }
3335     },
3336
3337     // overridden Element method
3338     setWidth : function(w, a, d, c, e){
3339         this.beforeAction();
3340         var cb = this.createCB(c);
3341         supr.setWidth.call(this, w, a, d, cb, e);
3342         if(!a){
3343             cb();
3344         }
3345     },
3346
3347     // overridden Element method
3348     setHeight : function(h, a, d, c, e){
3349         this.beforeAction();
3350         var cb = this.createCB(c);
3351         supr.setHeight.call(this, h, a, d, cb, e);
3352         if(!a){
3353             cb();
3354         }
3355     },
3356
3357     // overridden Element method
3358     setBounds : function(x, y, w, h, a, d, c, e){
3359         this.beforeAction();
3360         var cb = this.createCB(c);
3361         if(!a){
3362             this.storeXY([x, y]);
3363             supr.setXY.call(this, [x, y]);
3364             supr.setSize.call(this, w, h, a, d, cb, e);
3365             cb();
3366         }else{
3367             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3368         }
3369         return this;
3370     },
3371     
3372     /**
3373      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3374      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3375      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3376      * @param {Number} zindex The new z-index to set
3377      * @return {this} The Layer
3378      */
3379     setZIndex : function(zindex){
3380         this.zindex = zindex;
3381         this.setStyle("z-index", zindex + 2);
3382         if(this.shadow){
3383             this.shadow.setZIndex(zindex + 1);
3384         }
3385         if(this.shim){
3386             this.shim.setStyle("z-index", zindex);
3387         }
3388     }
3389 });
3390 })();/*
3391  * Based on:
3392  * Ext JS Library 1.1.1
3393  * Copyright(c) 2006-2007, Ext JS, LLC.
3394  *
3395  * Originally Released Under LGPL - original licence link has changed is not relivant.
3396  *
3397  * Fork - LGPL
3398  * <script type="text/javascript">
3399  */
3400
3401
3402 /**
3403  * @class Roo.Shadow
3404  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3405  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3406  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3407  * @constructor
3408  * Create a new Shadow
3409  * @param {Object} config The config object
3410  */
3411 Roo.Shadow = function(config){
3412     Roo.apply(this, config);
3413     if(typeof this.mode != "string"){
3414         this.mode = this.defaultMode;
3415     }
3416     var o = this.offset, a = {h: 0};
3417     var rad = Math.floor(this.offset/2);
3418     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3419         case "drop":
3420             a.w = 0;
3421             a.l = a.t = o;
3422             a.t -= 1;
3423             if(Roo.isIE){
3424                 a.l -= this.offset + rad;
3425                 a.t -= this.offset + rad;
3426                 a.w -= rad;
3427                 a.h -= rad;
3428                 a.t += 1;
3429             }
3430         break;
3431         case "sides":
3432             a.w = (o*2);
3433             a.l = -o;
3434             a.t = o-1;
3435             if(Roo.isIE){
3436                 a.l -= (this.offset - rad);
3437                 a.t -= this.offset + rad;
3438                 a.l += 1;
3439                 a.w -= (this.offset - rad)*2;
3440                 a.w -= rad + 1;
3441                 a.h -= 1;
3442             }
3443         break;
3444         case "frame":
3445             a.w = a.h = (o*2);
3446             a.l = a.t = -o;
3447             a.t += 1;
3448             a.h -= 2;
3449             if(Roo.isIE){
3450                 a.l -= (this.offset - rad);
3451                 a.t -= (this.offset - rad);
3452                 a.l += 1;
3453                 a.w -= (this.offset + rad + 1);
3454                 a.h -= (this.offset + rad);
3455                 a.h += 1;
3456             }
3457         break;
3458     };
3459
3460     this.adjusts = a;
3461 };
3462
3463 Roo.Shadow.prototype = {
3464     /**
3465      * @cfg {String} mode
3466      * The shadow display mode.  Supports the following options:<br />
3467      * sides: Shadow displays on both sides and bottom only<br />
3468      * frame: Shadow displays equally on all four sides<br />
3469      * drop: Traditional bottom-right drop shadow (default)
3470      */
3471     /**
3472      * @cfg {String} offset
3473      * The number of pixels to offset the shadow from the element (defaults to 4)
3474      */
3475     offset: 4,
3476
3477     // private
3478     defaultMode: "drop",
3479
3480     /**
3481      * Displays the shadow under the target element
3482      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3483      */
3484     show : function(target){
3485         target = Roo.get(target);
3486         if(!this.el){
3487             this.el = Roo.Shadow.Pool.pull();
3488             if(this.el.dom.nextSibling != target.dom){
3489                 this.el.insertBefore(target);
3490             }
3491         }
3492         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3493         if(Roo.isIE){
3494             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3495         }
3496         this.realign(
3497             target.getLeft(true),
3498             target.getTop(true),
3499             target.getWidth(),
3500             target.getHeight()
3501         );
3502         this.el.dom.style.display = "block";
3503     },
3504
3505     /**
3506      * Returns true if the shadow is visible, else false
3507      */
3508     isVisible : function(){
3509         return this.el ? true : false;  
3510     },
3511
3512     /**
3513      * Direct alignment when values are already available. Show must be called at least once before
3514      * calling this method to ensure it is initialized.
3515      * @param {Number} left The target element left position
3516      * @param {Number} top The target element top position
3517      * @param {Number} width The target element width
3518      * @param {Number} height The target element height
3519      */
3520     realign : function(l, t, w, h){
3521         if(!this.el){
3522             return;
3523         }
3524         var a = this.adjusts, d = this.el.dom, s = d.style;
3525         var iea = 0;
3526         s.left = (l+a.l)+"px";
3527         s.top = (t+a.t)+"px";
3528         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3529  
3530         if(s.width != sws || s.height != shs){
3531             s.width = sws;
3532             s.height = shs;
3533             if(!Roo.isIE){
3534                 var cn = d.childNodes;
3535                 var sww = Math.max(0, (sw-12))+"px";
3536                 cn[0].childNodes[1].style.width = sww;
3537                 cn[1].childNodes[1].style.width = sww;
3538                 cn[2].childNodes[1].style.width = sww;
3539                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3540             }
3541         }
3542     },
3543
3544     /**
3545      * Hides this shadow
3546      */
3547     hide : function(){
3548         if(this.el){
3549             this.el.dom.style.display = "none";
3550             Roo.Shadow.Pool.push(this.el);
3551             delete this.el;
3552         }
3553     },
3554
3555     /**
3556      * Adjust the z-index of this shadow
3557      * @param {Number} zindex The new z-index
3558      */
3559     setZIndex : function(z){
3560         this.zIndex = z;
3561         if(this.el){
3562             this.el.setStyle("z-index", z);
3563         }
3564     }
3565 };
3566
3567 // Private utility class that manages the internal Shadow cache
3568 Roo.Shadow.Pool = function(){
3569     var p = [];
3570     var markup = Roo.isIE ?
3571                  '<div class="x-ie-shadow"></div>' :
3572                  '<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>';
3573     return {
3574         pull : function(){
3575             var sh = p.shift();
3576             if(!sh){
3577                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3578                 sh.autoBoxAdjust = false;
3579             }
3580             return sh;
3581         },
3582
3583         push : function(sh){
3584             p.push(sh);
3585         }
3586     };
3587 }();/*
3588  * Based on:
3589  * Ext JS Library 1.1.1
3590  * Copyright(c) 2006-2007, Ext JS, LLC.
3591  *
3592  * Originally Released Under LGPL - original licence link has changed is not relivant.
3593  *
3594  * Fork - LGPL
3595  * <script type="text/javascript">
3596  */
3597
3598
3599 /**
3600  * @class Roo.SplitBar
3601  * @extends Roo.util.Observable
3602  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3603  * <br><br>
3604  * Usage:
3605  * <pre><code>
3606 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3607                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3608 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3609 split.minSize = 100;
3610 split.maxSize = 600;
3611 split.animate = true;
3612 split.on('moved', splitterMoved);
3613 </code></pre>
3614  * @constructor
3615  * Create a new SplitBar
3616  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3617  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3618  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3619  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3620                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3621                         position of the SplitBar).
3622  */
3623 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3624     
3625     /** @private */
3626     this.el = Roo.get(dragElement, true);
3627     this.el.dom.unselectable = "on";
3628     /** @private */
3629     this.resizingEl = Roo.get(resizingElement, true);
3630
3631     /**
3632      * @private
3633      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3634      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3635      * @type Number
3636      */
3637     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3638     
3639     /**
3640      * The minimum size of the resizing element. (Defaults to 0)
3641      * @type Number
3642      */
3643     this.minSize = 0;
3644     
3645     /**
3646      * The maximum size of the resizing element. (Defaults to 2000)
3647      * @type Number
3648      */
3649     this.maxSize = 2000;
3650     
3651     /**
3652      * Whether to animate the transition to the new size
3653      * @type Boolean
3654      */
3655     this.animate = false;
3656     
3657     /**
3658      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3659      * @type Boolean
3660      */
3661     this.useShim = false;
3662     
3663     /** @private */
3664     this.shim = null;
3665     
3666     if(!existingProxy){
3667         /** @private */
3668         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3669     }else{
3670         this.proxy = Roo.get(existingProxy).dom;
3671     }
3672     /** @private */
3673     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3674     
3675     /** @private */
3676     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3677     
3678     /** @private */
3679     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3680     
3681     /** @private */
3682     this.dragSpecs = {};
3683     
3684     /**
3685      * @private The adapter to use to positon and resize elements
3686      */
3687     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3688     this.adapter.init(this);
3689     
3690     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3691         /** @private */
3692         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3693         this.el.addClass("x-splitbar-h");
3694     }else{
3695         /** @private */
3696         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3697         this.el.addClass("x-splitbar-v");
3698     }
3699     
3700     this.addEvents({
3701         /**
3702          * @event resize
3703          * Fires when the splitter is moved (alias for {@link #event-moved})
3704          * @param {Roo.SplitBar} this
3705          * @param {Number} newSize the new width or height
3706          */
3707         "resize" : true,
3708         /**
3709          * @event moved
3710          * Fires when the splitter is moved
3711          * @param {Roo.SplitBar} this
3712          * @param {Number} newSize the new width or height
3713          */
3714         "moved" : true,
3715         /**
3716          * @event beforeresize
3717          * Fires before the splitter is dragged
3718          * @param {Roo.SplitBar} this
3719          */
3720         "beforeresize" : true,
3721
3722         "beforeapply" : true
3723     });
3724
3725     Roo.util.Observable.call(this);
3726 };
3727
3728 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3729     onStartProxyDrag : function(x, y){
3730         this.fireEvent("beforeresize", this);
3731         if(!this.overlay){
3732             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3733             o.unselectable();
3734             o.enableDisplayMode("block");
3735             // all splitbars share the same overlay
3736             Roo.SplitBar.prototype.overlay = o;
3737         }
3738         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3739         this.overlay.show();
3740         Roo.get(this.proxy).setDisplayed("block");
3741         var size = this.adapter.getElementSize(this);
3742         this.activeMinSize = this.getMinimumSize();;
3743         this.activeMaxSize = this.getMaximumSize();;
3744         var c1 = size - this.activeMinSize;
3745         var c2 = Math.max(this.activeMaxSize - size, 0);
3746         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3747             this.dd.resetConstraints();
3748             this.dd.setXConstraint(
3749                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3750                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3751             );
3752             this.dd.setYConstraint(0, 0);
3753         }else{
3754             this.dd.resetConstraints();
3755             this.dd.setXConstraint(0, 0);
3756             this.dd.setYConstraint(
3757                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3758                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3759             );
3760          }
3761         this.dragSpecs.startSize = size;
3762         this.dragSpecs.startPoint = [x, y];
3763         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3764     },
3765     
3766     /** 
3767      * @private Called after the drag operation by the DDProxy
3768      */
3769     onEndProxyDrag : function(e){
3770         Roo.get(this.proxy).setDisplayed(false);
3771         var endPoint = Roo.lib.Event.getXY(e);
3772         if(this.overlay){
3773             this.overlay.hide();
3774         }
3775         var newSize;
3776         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3777             newSize = this.dragSpecs.startSize + 
3778                 (this.placement == Roo.SplitBar.LEFT ?
3779                     endPoint[0] - this.dragSpecs.startPoint[0] :
3780                     this.dragSpecs.startPoint[0] - endPoint[0]
3781                 );
3782         }else{
3783             newSize = this.dragSpecs.startSize + 
3784                 (this.placement == Roo.SplitBar.TOP ?
3785                     endPoint[1] - this.dragSpecs.startPoint[1] :
3786                     this.dragSpecs.startPoint[1] - endPoint[1]
3787                 );
3788         }
3789         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3790         if(newSize != this.dragSpecs.startSize){
3791             if(this.fireEvent('beforeapply', this, newSize) !== false){
3792                 this.adapter.setElementSize(this, newSize);
3793                 this.fireEvent("moved", this, newSize);
3794                 this.fireEvent("resize", this, newSize);
3795             }
3796         }
3797     },
3798     
3799     /**
3800      * Get the adapter this SplitBar uses
3801      * @return The adapter object
3802      */
3803     getAdapter : function(){
3804         return this.adapter;
3805     },
3806     
3807     /**
3808      * Set the adapter this SplitBar uses
3809      * @param {Object} adapter A SplitBar adapter object
3810      */
3811     setAdapter : function(adapter){
3812         this.adapter = adapter;
3813         this.adapter.init(this);
3814     },
3815     
3816     /**
3817      * Gets the minimum size for the resizing element
3818      * @return {Number} The minimum size
3819      */
3820     getMinimumSize : function(){
3821         return this.minSize;
3822     },
3823     
3824     /**
3825      * Sets the minimum size for the resizing element
3826      * @param {Number} minSize The minimum size
3827      */
3828     setMinimumSize : function(minSize){
3829         this.minSize = minSize;
3830     },
3831     
3832     /**
3833      * Gets the maximum size for the resizing element
3834      * @return {Number} The maximum size
3835      */
3836     getMaximumSize : function(){
3837         return this.maxSize;
3838     },
3839     
3840     /**
3841      * Sets the maximum size for the resizing element
3842      * @param {Number} maxSize The maximum size
3843      */
3844     setMaximumSize : function(maxSize){
3845         this.maxSize = maxSize;
3846     },
3847     
3848     /**
3849      * Sets the initialize size for the resizing element
3850      * @param {Number} size The initial size
3851      */
3852     setCurrentSize : function(size){
3853         var oldAnimate = this.animate;
3854         this.animate = false;
3855         this.adapter.setElementSize(this, size);
3856         this.animate = oldAnimate;
3857     },
3858     
3859     /**
3860      * Destroy this splitbar. 
3861      * @param {Boolean} removeEl True to remove the element
3862      */
3863     destroy : function(removeEl){
3864         if(this.shim){
3865             this.shim.remove();
3866         }
3867         this.dd.unreg();
3868         this.proxy.parentNode.removeChild(this.proxy);
3869         if(removeEl){
3870             this.el.remove();
3871         }
3872     }
3873 });
3874
3875 /**
3876  * @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.
3877  */
3878 Roo.SplitBar.createProxy = function(dir){
3879     var proxy = new Roo.Element(document.createElement("div"));
3880     proxy.unselectable();
3881     var cls = 'x-splitbar-proxy';
3882     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3883     document.body.appendChild(proxy.dom);
3884     return proxy.dom;
3885 };
3886
3887 /** 
3888  * @class Roo.SplitBar.BasicLayoutAdapter
3889  * Default Adapter. It assumes the splitter and resizing element are not positioned
3890  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3891  */
3892 Roo.SplitBar.BasicLayoutAdapter = function(){
3893 };
3894
3895 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3896     // do nothing for now
3897     init : function(s){
3898     
3899     },
3900     /**
3901      * Called before drag operations to get the current size of the resizing element. 
3902      * @param {Roo.SplitBar} s The SplitBar using this adapter
3903      */
3904      getElementSize : function(s){
3905         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3906             return s.resizingEl.getWidth();
3907         }else{
3908             return s.resizingEl.getHeight();
3909         }
3910     },
3911     
3912     /**
3913      * Called after drag operations to set the size of the resizing element.
3914      * @param {Roo.SplitBar} s The SplitBar using this adapter
3915      * @param {Number} newSize The new size to set
3916      * @param {Function} onComplete A function to be invoked when resizing is complete
3917      */
3918     setElementSize : function(s, newSize, onComplete){
3919         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3920             if(!s.animate){
3921                 s.resizingEl.setWidth(newSize);
3922                 if(onComplete){
3923                     onComplete(s, newSize);
3924                 }
3925             }else{
3926                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3927             }
3928         }else{
3929             
3930             if(!s.animate){
3931                 s.resizingEl.setHeight(newSize);
3932                 if(onComplete){
3933                     onComplete(s, newSize);
3934                 }
3935             }else{
3936                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3937             }
3938         }
3939     }
3940 };
3941
3942 /** 
3943  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3944  * @extends Roo.SplitBar.BasicLayoutAdapter
3945  * Adapter that  moves the splitter element to align with the resized sizing element. 
3946  * Used with an absolute positioned SplitBar.
3947  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3948  * document.body, make sure you assign an id to the body element.
3949  */
3950 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3951     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3952     this.container = Roo.get(container);
3953 };
3954
3955 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3956     init : function(s){
3957         this.basic.init(s);
3958     },
3959     
3960     getElementSize : function(s){
3961         return this.basic.getElementSize(s);
3962     },
3963     
3964     setElementSize : function(s, newSize, onComplete){
3965         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3966     },
3967     
3968     moveSplitter : function(s){
3969         var yes = Roo.SplitBar;
3970         switch(s.placement){
3971             case yes.LEFT:
3972                 s.el.setX(s.resizingEl.getRight());
3973                 break;
3974             case yes.RIGHT:
3975                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3976                 break;
3977             case yes.TOP:
3978                 s.el.setY(s.resizingEl.getBottom());
3979                 break;
3980             case yes.BOTTOM:
3981                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3982                 break;
3983         }
3984     }
3985 };
3986
3987 /**
3988  * Orientation constant - Create a vertical SplitBar
3989  * @static
3990  * @type Number
3991  */
3992 Roo.SplitBar.VERTICAL = 1;
3993
3994 /**
3995  * Orientation constant - Create a horizontal SplitBar
3996  * @static
3997  * @type Number
3998  */
3999 Roo.SplitBar.HORIZONTAL = 2;
4000
4001 /**
4002  * Placement constant - The resizing element is to the left of the splitter element
4003  * @static
4004  * @type Number
4005  */
4006 Roo.SplitBar.LEFT = 1;
4007
4008 /**
4009  * Placement constant - The resizing element is to the right of the splitter element
4010  * @static
4011  * @type Number
4012  */
4013 Roo.SplitBar.RIGHT = 2;
4014
4015 /**
4016  * Placement constant - The resizing element is positioned above the splitter element
4017  * @static
4018  * @type Number
4019  */
4020 Roo.SplitBar.TOP = 3;
4021
4022 /**
4023  * Placement constant - The resizing element is positioned under splitter element
4024  * @static
4025  * @type Number
4026  */
4027 Roo.SplitBar.BOTTOM = 4;
4028 /*
4029  * Based on:
4030  * Ext JS Library 1.1.1
4031  * Copyright(c) 2006-2007, Ext JS, LLC.
4032  *
4033  * Originally Released Under LGPL - original licence link has changed is not relivant.
4034  *
4035  * Fork - LGPL
4036  * <script type="text/javascript">
4037  */
4038
4039 /**
4040  * @class Roo.View
4041  * @extends Roo.util.Observable
4042  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4043  * This class also supports single and multi selection modes. <br>
4044  * Create a data model bound view:
4045  <pre><code>
4046  var store = new Roo.data.Store(...);
4047
4048  var view = new Roo.View({
4049     el : "my-element",
4050     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4051  
4052     singleSelect: true,
4053     selectedClass: "ydataview-selected",
4054     store: store
4055  });
4056
4057  // listen for node click?
4058  view.on("click", function(vw, index, node, e){
4059  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4060  });
4061
4062  // load XML data
4063  dataModel.load("foobar.xml");
4064  </code></pre>
4065  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4066  * <br><br>
4067  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4068  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4069  * 
4070  * Note: old style constructor is still suported (container, template, config)
4071  * 
4072  * @constructor
4073  * Create a new View
4074  * @param {Object} config The config object
4075  * 
4076  */
4077 Roo.View = function(config, depreciated_tpl, depreciated_config){
4078     
4079     this.parent = false;
4080     
4081     if (typeof(depreciated_tpl) == 'undefined') {
4082         // new way.. - universal constructor.
4083         Roo.apply(this, config);
4084         this.el  = Roo.get(this.el);
4085     } else {
4086         // old format..
4087         this.el  = Roo.get(config);
4088         this.tpl = depreciated_tpl;
4089         Roo.apply(this, depreciated_config);
4090     }
4091     this.wrapEl  = this.el.wrap().wrap();
4092     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4093     
4094     
4095     if(typeof(this.tpl) == "string"){
4096         this.tpl = new Roo.Template(this.tpl);
4097     } else {
4098         // support xtype ctors..
4099         this.tpl = new Roo.factory(this.tpl, Roo);
4100     }
4101     
4102     
4103     this.tpl.compile();
4104     
4105     /** @private */
4106     this.addEvents({
4107         /**
4108          * @event beforeclick
4109          * Fires before a click is processed. Returns false to cancel the default action.
4110          * @param {Roo.View} this
4111          * @param {Number} index The index of the target node
4112          * @param {HTMLElement} node The target node
4113          * @param {Roo.EventObject} e The raw event object
4114          */
4115             "beforeclick" : true,
4116         /**
4117          * @event click
4118          * Fires when a template node is clicked.
4119          * @param {Roo.View} this
4120          * @param {Number} index The index of the target node
4121          * @param {HTMLElement} node The target node
4122          * @param {Roo.EventObject} e The raw event object
4123          */
4124             "click" : true,
4125         /**
4126          * @event dblclick
4127          * Fires when a template node is double clicked.
4128          * @param {Roo.View} this
4129          * @param {Number} index The index of the target node
4130          * @param {HTMLElement} node The target node
4131          * @param {Roo.EventObject} e The raw event object
4132          */
4133             "dblclick" : true,
4134         /**
4135          * @event contextmenu
4136          * Fires when a template node is right clicked.
4137          * @param {Roo.View} this
4138          * @param {Number} index The index of the target node
4139          * @param {HTMLElement} node The target node
4140          * @param {Roo.EventObject} e The raw event object
4141          */
4142             "contextmenu" : true,
4143         /**
4144          * @event selectionchange
4145          * Fires when the selected nodes change.
4146          * @param {Roo.View} this
4147          * @param {Array} selections Array of the selected nodes
4148          */
4149             "selectionchange" : true,
4150     
4151         /**
4152          * @event beforeselect
4153          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4154          * @param {Roo.View} this
4155          * @param {HTMLElement} node The node to be selected
4156          * @param {Array} selections Array of currently selected nodes
4157          */
4158             "beforeselect" : true,
4159         /**
4160          * @event preparedata
4161          * Fires on every row to render, to allow you to change the data.
4162          * @param {Roo.View} this
4163          * @param {Object} data to be rendered (change this)
4164          */
4165           "preparedata" : true
4166           
4167           
4168         });
4169
4170
4171
4172     this.el.on({
4173         "click": this.onClick,
4174         "dblclick": this.onDblClick,
4175         "contextmenu": this.onContextMenu,
4176         scope:this
4177     });
4178
4179     this.selections = [];
4180     this.nodes = [];
4181     this.cmp = new Roo.CompositeElementLite([]);
4182     if(this.store){
4183         this.store = Roo.factory(this.store, Roo.data);
4184         this.setStore(this.store, true);
4185     }
4186     
4187     if ( this.footer && this.footer.xtype) {
4188            
4189          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4190         
4191         this.footer.dataSource = this.store;
4192         this.footer.container = fctr;
4193         this.footer = Roo.factory(this.footer, Roo);
4194         fctr.insertFirst(this.el);
4195         
4196         // this is a bit insane - as the paging toolbar seems to detach the el..
4197 //        dom.parentNode.parentNode.parentNode
4198          // they get detached?
4199     }
4200     
4201     
4202     Roo.View.superclass.constructor.call(this);
4203     
4204     
4205 };
4206
4207 Roo.extend(Roo.View, Roo.util.Observable, {
4208     
4209      /**
4210      * @cfg {Roo.data.Store} store Data store to load data from.
4211      */
4212     store : false,
4213     
4214     /**
4215      * @cfg {String|Roo.Element} el The container element.
4216      */
4217     el : '',
4218     
4219     /**
4220      * @cfg {String|Roo.Template} tpl The template used by this View 
4221      */
4222     tpl : false,
4223     /**
4224      * @cfg {String} dataName the named area of the template to use as the data area
4225      *                          Works with domtemplates roo-name="name"
4226      */
4227     dataName: false,
4228     /**
4229      * @cfg {String} selectedClass The css class to add to selected nodes
4230      */
4231     selectedClass : "x-view-selected",
4232      /**
4233      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4234      */
4235     emptyText : "",
4236     
4237     /**
4238      * @cfg {String} text to display on mask (default Loading)
4239      */
4240     mask : false,
4241     /**
4242      * @cfg {Boolean} multiSelect Allow multiple selection
4243      */
4244     multiSelect : false,
4245     /**
4246      * @cfg {Boolean} singleSelect Allow single selection
4247      */
4248     singleSelect:  false,
4249     
4250     /**
4251      * @cfg {Boolean} toggleSelect - selecting 
4252      */
4253     toggleSelect : false,
4254     
4255     /**
4256      * @cfg {Boolean} tickable - selecting 
4257      */
4258     tickable : false,
4259     
4260     /**
4261      * Returns the element this view is bound to.
4262      * @return {Roo.Element}
4263      */
4264     getEl : function(){
4265         return this.wrapEl;
4266     },
4267     
4268     
4269
4270     /**
4271      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4272      */
4273     refresh : function(){
4274         //Roo.log('refresh');
4275         var t = this.tpl;
4276         
4277         // if we are using something like 'domtemplate', then
4278         // the what gets used is:
4279         // t.applySubtemplate(NAME, data, wrapping data..)
4280         // the outer template then get' applied with
4281         //     the store 'extra data'
4282         // and the body get's added to the
4283         //      roo-name="data" node?
4284         //      <span class='roo-tpl-{name}'></span> ?????
4285         
4286         
4287         
4288         this.clearSelections();
4289         this.el.update("");
4290         var html = [];
4291         var records = this.store.getRange();
4292         if(records.length < 1) {
4293             
4294             // is this valid??  = should it render a template??
4295             
4296             this.el.update(this.emptyText);
4297             return;
4298         }
4299         var el = this.el;
4300         if (this.dataName) {
4301             this.el.update(t.apply(this.store.meta)); //????
4302             el = this.el.child('.roo-tpl-' + this.dataName);
4303         }
4304         
4305         for(var i = 0, len = records.length; i < len; i++){
4306             var data = this.prepareData(records[i].data, i, records[i]);
4307             this.fireEvent("preparedata", this, data, i, records[i]);
4308             
4309             var d = Roo.apply({}, data);
4310             
4311             if(this.tickable){
4312                 Roo.apply(d, {'roo-id' : Roo.id()});
4313                 
4314                 var _this = this;
4315             
4316                 Roo.each(this.parent.item, function(item){
4317                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4318                         return;
4319                     }
4320                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4321                 });
4322             }
4323             
4324             html[html.length] = Roo.util.Format.trim(
4325                 this.dataName ?
4326                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4327                     t.apply(d)
4328             );
4329         }
4330         
4331         
4332         
4333         el.update(html.join(""));
4334         this.nodes = el.dom.childNodes;
4335         this.updateIndexes(0);
4336     },
4337     
4338
4339     /**
4340      * Function to override to reformat the data that is sent to
4341      * the template for each node.
4342      * DEPRICATED - use the preparedata event handler.
4343      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4344      * a JSON object for an UpdateManager bound view).
4345      */
4346     prepareData : function(data, index, record)
4347     {
4348         this.fireEvent("preparedata", this, data, index, record);
4349         return data;
4350     },
4351
4352     onUpdate : function(ds, record){
4353         // Roo.log('on update');   
4354         this.clearSelections();
4355         var index = this.store.indexOf(record);
4356         var n = this.nodes[index];
4357         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4358         n.parentNode.removeChild(n);
4359         this.updateIndexes(index, index);
4360     },
4361
4362     
4363     
4364 // --------- FIXME     
4365     onAdd : function(ds, records, index)
4366     {
4367         //Roo.log(['on Add', ds, records, index] );        
4368         this.clearSelections();
4369         if(this.nodes.length == 0){
4370             this.refresh();
4371             return;
4372         }
4373         var n = this.nodes[index];
4374         for(var i = 0, len = records.length; i < len; i++){
4375             var d = this.prepareData(records[i].data, i, records[i]);
4376             if(n){
4377                 this.tpl.insertBefore(n, d);
4378             }else{
4379                 
4380                 this.tpl.append(this.el, d);
4381             }
4382         }
4383         this.updateIndexes(index);
4384     },
4385
4386     onRemove : function(ds, record, index){
4387        // Roo.log('onRemove');
4388         this.clearSelections();
4389         var el = this.dataName  ?
4390             this.el.child('.roo-tpl-' + this.dataName) :
4391             this.el; 
4392         
4393         el.dom.removeChild(this.nodes[index]);
4394         this.updateIndexes(index);
4395     },
4396
4397     /**
4398      * Refresh an individual node.
4399      * @param {Number} index
4400      */
4401     refreshNode : function(index){
4402         this.onUpdate(this.store, this.store.getAt(index));
4403     },
4404
4405     updateIndexes : function(startIndex, endIndex){
4406         var ns = this.nodes;
4407         startIndex = startIndex || 0;
4408         endIndex = endIndex || ns.length - 1;
4409         for(var i = startIndex; i <= endIndex; i++){
4410             ns[i].nodeIndex = i;
4411         }
4412     },
4413
4414     /**
4415      * Changes the data store this view uses and refresh the view.
4416      * @param {Store} store
4417      */
4418     setStore : function(store, initial){
4419         if(!initial && this.store){
4420             this.store.un("datachanged", this.refresh);
4421             this.store.un("add", this.onAdd);
4422             this.store.un("remove", this.onRemove);
4423             this.store.un("update", this.onUpdate);
4424             this.store.un("clear", this.refresh);
4425             this.store.un("beforeload", this.onBeforeLoad);
4426             this.store.un("load", this.onLoad);
4427             this.store.un("loadexception", this.onLoad);
4428         }
4429         if(store){
4430           
4431             store.on("datachanged", this.refresh, this);
4432             store.on("add", this.onAdd, this);
4433             store.on("remove", this.onRemove, this);
4434             store.on("update", this.onUpdate, this);
4435             store.on("clear", this.refresh, this);
4436             store.on("beforeload", this.onBeforeLoad, this);
4437             store.on("load", this.onLoad, this);
4438             store.on("loadexception", this.onLoad, this);
4439         }
4440         
4441         if(store){
4442             this.refresh();
4443         }
4444     },
4445     /**
4446      * onbeforeLoad - masks the loading area.
4447      *
4448      */
4449     onBeforeLoad : function(store,opts)
4450     {
4451          //Roo.log('onBeforeLoad');   
4452         if (!opts.add) {
4453             this.el.update("");
4454         }
4455         this.el.mask(this.mask ? this.mask : "Loading" ); 
4456     },
4457     onLoad : function ()
4458     {
4459         this.el.unmask();
4460     },
4461     
4462
4463     /**
4464      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4465      * @param {HTMLElement} node
4466      * @return {HTMLElement} The template node
4467      */
4468     findItemFromChild : function(node){
4469         var el = this.dataName  ?
4470             this.el.child('.roo-tpl-' + this.dataName,true) :
4471             this.el.dom; 
4472         
4473         if(!node || node.parentNode == el){
4474                     return node;
4475             }
4476             var p = node.parentNode;
4477             while(p && p != el){
4478             if(p.parentNode == el){
4479                 return p;
4480             }
4481             p = p.parentNode;
4482         }
4483             return null;
4484     },
4485
4486     /** @ignore */
4487     onClick : function(e){
4488         var item = this.findItemFromChild(e.getTarget());
4489         if(item){
4490             var index = this.indexOf(item);
4491             if(this.onItemClick(item, index, e) !== false){
4492                 this.fireEvent("click", this, index, item, e);
4493             }
4494         }else{
4495             this.clearSelections();
4496         }
4497     },
4498
4499     /** @ignore */
4500     onContextMenu : function(e){
4501         var item = this.findItemFromChild(e.getTarget());
4502         if(item){
4503             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4504         }
4505     },
4506
4507     /** @ignore */
4508     onDblClick : function(e){
4509         var item = this.findItemFromChild(e.getTarget());
4510         if(item){
4511             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4512         }
4513     },
4514
4515     onItemClick : function(item, index, e)
4516     {
4517         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4518             return false;
4519         }
4520         if (this.toggleSelect) {
4521             var m = this.isSelected(item) ? 'unselect' : 'select';
4522             //Roo.log(m);
4523             var _t = this;
4524             _t[m](item, true, false);
4525             return true;
4526         }
4527         if(this.multiSelect || this.singleSelect){
4528             if(this.multiSelect && e.shiftKey && this.lastSelection){
4529                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4530             }else{
4531                 this.select(item, this.multiSelect && e.ctrlKey);
4532                 this.lastSelection = item;
4533             }
4534             
4535             if(!this.tickable){
4536                 e.preventDefault();
4537             }
4538             
4539         }
4540         return true;
4541     },
4542
4543     /**
4544      * Get the number of selected nodes.
4545      * @return {Number}
4546      */
4547     getSelectionCount : function(){
4548         return this.selections.length;
4549     },
4550
4551     /**
4552      * Get the currently selected nodes.
4553      * @return {Array} An array of HTMLElements
4554      */
4555     getSelectedNodes : function(){
4556         return this.selections;
4557     },
4558
4559     /**
4560      * Get the indexes of the selected nodes.
4561      * @return {Array}
4562      */
4563     getSelectedIndexes : function(){
4564         var indexes = [], s = this.selections;
4565         for(var i = 0, len = s.length; i < len; i++){
4566             indexes.push(s[i].nodeIndex);
4567         }
4568         return indexes;
4569     },
4570
4571     /**
4572      * Clear all selections
4573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4574      */
4575     clearSelections : function(suppressEvent){
4576         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4577             this.cmp.elements = this.selections;
4578             this.cmp.removeClass(this.selectedClass);
4579             this.selections = [];
4580             if(!suppressEvent){
4581                 this.fireEvent("selectionchange", this, this.selections);
4582             }
4583         }
4584     },
4585
4586     /**
4587      * Returns true if the passed node is selected
4588      * @param {HTMLElement/Number} node The node or node index
4589      * @return {Boolean}
4590      */
4591     isSelected : function(node){
4592         var s = this.selections;
4593         if(s.length < 1){
4594             return false;
4595         }
4596         node = this.getNode(node);
4597         return s.indexOf(node) !== -1;
4598     },
4599
4600     /**
4601      * Selects nodes.
4602      * @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
4603      * @param {Boolean} keepExisting (optional) true to keep existing selections
4604      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4605      */
4606     select : function(nodeInfo, keepExisting, suppressEvent){
4607         if(nodeInfo instanceof Array){
4608             if(!keepExisting){
4609                 this.clearSelections(true);
4610             }
4611             for(var i = 0, len = nodeInfo.length; i < len; i++){
4612                 this.select(nodeInfo[i], true, true);
4613             }
4614             return;
4615         } 
4616         var node = this.getNode(nodeInfo);
4617         if(!node || this.isSelected(node)){
4618             return; // already selected.
4619         }
4620         if(!keepExisting){
4621             this.clearSelections(true);
4622         }
4623         
4624         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4625             Roo.fly(node).addClass(this.selectedClass);
4626             this.selections.push(node);
4627             if(!suppressEvent){
4628                 this.fireEvent("selectionchange", this, this.selections);
4629             }
4630         }
4631         
4632         
4633     },
4634       /**
4635      * Unselects nodes.
4636      * @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
4637      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4638      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4639      */
4640     unselect : function(nodeInfo, keepExisting, suppressEvent)
4641     {
4642         if(nodeInfo instanceof Array){
4643             Roo.each(this.selections, function(s) {
4644                 this.unselect(s, nodeInfo);
4645             }, this);
4646             return;
4647         }
4648         var node = this.getNode(nodeInfo);
4649         if(!node || !this.isSelected(node)){
4650             //Roo.log("not selected");
4651             return; // not selected.
4652         }
4653         // fireevent???
4654         var ns = [];
4655         Roo.each(this.selections, function(s) {
4656             if (s == node ) {
4657                 Roo.fly(node).removeClass(this.selectedClass);
4658
4659                 return;
4660             }
4661             ns.push(s);
4662         },this);
4663         
4664         this.selections= ns;
4665         this.fireEvent("selectionchange", this, this.selections);
4666     },
4667
4668     /**
4669      * Gets a template node.
4670      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4671      * @return {HTMLElement} The node or null if it wasn't found
4672      */
4673     getNode : function(nodeInfo){
4674         if(typeof nodeInfo == "string"){
4675             return document.getElementById(nodeInfo);
4676         }else if(typeof nodeInfo == "number"){
4677             return this.nodes[nodeInfo];
4678         }
4679         return nodeInfo;
4680     },
4681
4682     /**
4683      * Gets a range template nodes.
4684      * @param {Number} startIndex
4685      * @param {Number} endIndex
4686      * @return {Array} An array of nodes
4687      */
4688     getNodes : function(start, end){
4689         var ns = this.nodes;
4690         start = start || 0;
4691         end = typeof end == "undefined" ? ns.length - 1 : end;
4692         var nodes = [];
4693         if(start <= end){
4694             for(var i = start; i <= end; i++){
4695                 nodes.push(ns[i]);
4696             }
4697         } else{
4698             for(var i = start; i >= end; i--){
4699                 nodes.push(ns[i]);
4700             }
4701         }
4702         return nodes;
4703     },
4704
4705     /**
4706      * Finds the index of the passed node
4707      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4708      * @return {Number} The index of the node or -1
4709      */
4710     indexOf : function(node){
4711         node = this.getNode(node);
4712         if(typeof node.nodeIndex == "number"){
4713             return node.nodeIndex;
4714         }
4715         var ns = this.nodes;
4716         for(var i = 0, len = ns.length; i < len; i++){
4717             if(ns[i] == node){
4718                 return i;
4719             }
4720         }
4721         return -1;
4722     }
4723 });
4724 /*
4725  * Based on:
4726  * Ext JS Library 1.1.1
4727  * Copyright(c) 2006-2007, Ext JS, LLC.
4728  *
4729  * Originally Released Under LGPL - original licence link has changed is not relivant.
4730  *
4731  * Fork - LGPL
4732  * <script type="text/javascript">
4733  */
4734
4735 /**
4736  * @class Roo.JsonView
4737  * @extends Roo.View
4738  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4739 <pre><code>
4740 var view = new Roo.JsonView({
4741     container: "my-element",
4742     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4743     multiSelect: true, 
4744     jsonRoot: "data" 
4745 });
4746
4747 // listen for node click?
4748 view.on("click", function(vw, index, node, e){
4749     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4750 });
4751
4752 // direct load of JSON data
4753 view.load("foobar.php");
4754
4755 // Example from my blog list
4756 var tpl = new Roo.Template(
4757     '&lt;div class="entry"&gt;' +
4758     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4759     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4760     "&lt;/div&gt;&lt;hr /&gt;"
4761 );
4762
4763 var moreView = new Roo.JsonView({
4764     container :  "entry-list", 
4765     template : tpl,
4766     jsonRoot: "posts"
4767 });
4768 moreView.on("beforerender", this.sortEntries, this);
4769 moreView.load({
4770     url: "/blog/get-posts.php",
4771     params: "allposts=true",
4772     text: "Loading Blog Entries..."
4773 });
4774 </code></pre>
4775
4776 * Note: old code is supported with arguments : (container, template, config)
4777
4778
4779  * @constructor
4780  * Create a new JsonView
4781  * 
4782  * @param {Object} config The config object
4783  * 
4784  */
4785 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4786     
4787     
4788     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4789
4790     var um = this.el.getUpdateManager();
4791     um.setRenderer(this);
4792     um.on("update", this.onLoad, this);
4793     um.on("failure", this.onLoadException, this);
4794
4795     /**
4796      * @event beforerender
4797      * Fires before rendering of the downloaded JSON data.
4798      * @param {Roo.JsonView} this
4799      * @param {Object} data The JSON data loaded
4800      */
4801     /**
4802      * @event load
4803      * Fires when data is loaded.
4804      * @param {Roo.JsonView} this
4805      * @param {Object} data The JSON data loaded
4806      * @param {Object} response The raw Connect response object
4807      */
4808     /**
4809      * @event loadexception
4810      * Fires when loading fails.
4811      * @param {Roo.JsonView} this
4812      * @param {Object} response The raw Connect response object
4813      */
4814     this.addEvents({
4815         'beforerender' : true,
4816         'load' : true,
4817         'loadexception' : true
4818     });
4819 };
4820 Roo.extend(Roo.JsonView, Roo.View, {
4821     /**
4822      * @type {String} The root property in the loaded JSON object that contains the data
4823      */
4824     jsonRoot : "",
4825
4826     /**
4827      * Refreshes the view.
4828      */
4829     refresh : function(){
4830         this.clearSelections();
4831         this.el.update("");
4832         var html = [];
4833         var o = this.jsonData;
4834         if(o && o.length > 0){
4835             for(var i = 0, len = o.length; i < len; i++){
4836                 var data = this.prepareData(o[i], i, o);
4837                 html[html.length] = this.tpl.apply(data);
4838             }
4839         }else{
4840             html.push(this.emptyText);
4841         }
4842         this.el.update(html.join(""));
4843         this.nodes = this.el.dom.childNodes;
4844         this.updateIndexes(0);
4845     },
4846
4847     /**
4848      * 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.
4849      * @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:
4850      <pre><code>
4851      view.load({
4852          url: "your-url.php",
4853          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4854          callback: yourFunction,
4855          scope: yourObject, //(optional scope)
4856          discardUrl: false,
4857          nocache: false,
4858          text: "Loading...",
4859          timeout: 30,
4860          scripts: false
4861      });
4862      </code></pre>
4863      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4864      * 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.
4865      * @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}
4866      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4867      * @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.
4868      */
4869     load : function(){
4870         var um = this.el.getUpdateManager();
4871         um.update.apply(um, arguments);
4872     },
4873
4874     // note - render is a standard framework call...
4875     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4876     render : function(el, response){
4877         
4878         this.clearSelections();
4879         this.el.update("");
4880         var o;
4881         try{
4882             if (response != '') {
4883                 o = Roo.util.JSON.decode(response.responseText);
4884                 if(this.jsonRoot){
4885                     
4886                     o = o[this.jsonRoot];
4887                 }
4888             }
4889         } catch(e){
4890         }
4891         /**
4892          * The current JSON data or null
4893          */
4894         this.jsonData = o;
4895         this.beforeRender();
4896         this.refresh();
4897     },
4898
4899 /**
4900  * Get the number of records in the current JSON dataset
4901  * @return {Number}
4902  */
4903     getCount : function(){
4904         return this.jsonData ? this.jsonData.length : 0;
4905     },
4906
4907 /**
4908  * Returns the JSON object for the specified node(s)
4909  * @param {HTMLElement/Array} node The node or an array of nodes
4910  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4911  * you get the JSON object for the node
4912  */
4913     getNodeData : function(node){
4914         if(node instanceof Array){
4915             var data = [];
4916             for(var i = 0, len = node.length; i < len; i++){
4917                 data.push(this.getNodeData(node[i]));
4918             }
4919             return data;
4920         }
4921         return this.jsonData[this.indexOf(node)] || null;
4922     },
4923
4924     beforeRender : function(){
4925         this.snapshot = this.jsonData;
4926         if(this.sortInfo){
4927             this.sort.apply(this, this.sortInfo);
4928         }
4929         this.fireEvent("beforerender", this, this.jsonData);
4930     },
4931
4932     onLoad : function(el, o){
4933         this.fireEvent("load", this, this.jsonData, o);
4934     },
4935
4936     onLoadException : function(el, o){
4937         this.fireEvent("loadexception", this, o);
4938     },
4939
4940 /**
4941  * Filter the data by a specific property.
4942  * @param {String} property A property on your JSON objects
4943  * @param {String/RegExp} value Either string that the property values
4944  * should start with, or a RegExp to test against the property
4945  */
4946     filter : function(property, value){
4947         if(this.jsonData){
4948             var data = [];
4949             var ss = this.snapshot;
4950             if(typeof value == "string"){
4951                 var vlen = value.length;
4952                 if(vlen == 0){
4953                     this.clearFilter();
4954                     return;
4955                 }
4956                 value = value.toLowerCase();
4957                 for(var i = 0, len = ss.length; i < len; i++){
4958                     var o = ss[i];
4959                     if(o[property].substr(0, vlen).toLowerCase() == value){
4960                         data.push(o);
4961                     }
4962                 }
4963             } else if(value.exec){ // regex?
4964                 for(var i = 0, len = ss.length; i < len; i++){
4965                     var o = ss[i];
4966                     if(value.test(o[property])){
4967                         data.push(o);
4968                     }
4969                 }
4970             } else{
4971                 return;
4972             }
4973             this.jsonData = data;
4974             this.refresh();
4975         }
4976     },
4977
4978 /**
4979  * Filter by a function. The passed function will be called with each
4980  * object in the current dataset. If the function returns true the value is kept,
4981  * otherwise it is filtered.
4982  * @param {Function} fn
4983  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4984  */
4985     filterBy : function(fn, scope){
4986         if(this.jsonData){
4987             var data = [];
4988             var ss = this.snapshot;
4989             for(var i = 0, len = ss.length; i < len; i++){
4990                 var o = ss[i];
4991                 if(fn.call(scope || this, o)){
4992                     data.push(o);
4993                 }
4994             }
4995             this.jsonData = data;
4996             this.refresh();
4997         }
4998     },
4999
5000 /**
5001  * Clears the current filter.
5002  */
5003     clearFilter : function(){
5004         if(this.snapshot && this.jsonData != this.snapshot){
5005             this.jsonData = this.snapshot;
5006             this.refresh();
5007         }
5008     },
5009
5010
5011 /**
5012  * Sorts the data for this view and refreshes it.
5013  * @param {String} property A property on your JSON objects to sort on
5014  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5015  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5016  */
5017     sort : function(property, dir, sortType){
5018         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5019         if(this.jsonData){
5020             var p = property;
5021             var dsc = dir && dir.toLowerCase() == "desc";
5022             var f = function(o1, o2){
5023                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5024                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5025                 ;
5026                 if(v1 < v2){
5027                     return dsc ? +1 : -1;
5028                 } else if(v1 > v2){
5029                     return dsc ? -1 : +1;
5030                 } else{
5031                     return 0;
5032                 }
5033             };
5034             this.jsonData.sort(f);
5035             this.refresh();
5036             if(this.jsonData != this.snapshot){
5037                 this.snapshot.sort(f);
5038             }
5039         }
5040     }
5041 });/*
5042  * Based on:
5043  * Ext JS Library 1.1.1
5044  * Copyright(c) 2006-2007, Ext JS, LLC.
5045  *
5046  * Originally Released Under LGPL - original licence link has changed is not relivant.
5047  *
5048  * Fork - LGPL
5049  * <script type="text/javascript">
5050  */
5051  
5052
5053 /**
5054  * @class Roo.ColorPalette
5055  * @extends Roo.Component
5056  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5057  * Here's an example of typical usage:
5058  * <pre><code>
5059 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5060 cp.render('my-div');
5061
5062 cp.on('select', function(palette, selColor){
5063     // do something with selColor
5064 });
5065 </code></pre>
5066  * @constructor
5067  * Create a new ColorPalette
5068  * @param {Object} config The config object
5069  */
5070 Roo.ColorPalette = function(config){
5071     Roo.ColorPalette.superclass.constructor.call(this, config);
5072     this.addEvents({
5073         /**
5074              * @event select
5075              * Fires when a color is selected
5076              * @param {ColorPalette} this
5077              * @param {String} color The 6-digit color hex code (without the # symbol)
5078              */
5079         select: true
5080     });
5081
5082     if(this.handler){
5083         this.on("select", this.handler, this.scope, true);
5084     }
5085 };
5086 Roo.extend(Roo.ColorPalette, Roo.Component, {
5087     /**
5088      * @cfg {String} itemCls
5089      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5090      */
5091     itemCls : "x-color-palette",
5092     /**
5093      * @cfg {String} value
5094      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5095      * the hex codes are case-sensitive.
5096      */
5097     value : null,
5098     clickEvent:'click',
5099     // private
5100     ctype: "Roo.ColorPalette",
5101
5102     /**
5103      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5104      */
5105     allowReselect : false,
5106
5107     /**
5108      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5109      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5110      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5111      * of colors with the width setting until the box is symmetrical.</p>
5112      * <p>You can override individual colors if needed:</p>
5113      * <pre><code>
5114 var cp = new Roo.ColorPalette();
5115 cp.colors[0] = "FF0000";  // change the first box to red
5116 </code></pre>
5117
5118 Or you can provide a custom array of your own for complete control:
5119 <pre><code>
5120 var cp = new Roo.ColorPalette();
5121 cp.colors = ["000000", "993300", "333300"];
5122 </code></pre>
5123      * @type Array
5124      */
5125     colors : [
5126         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5127         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5128         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5129         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5130         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5131     ],
5132
5133     // private
5134     onRender : function(container, position){
5135         var t = new Roo.MasterTemplate(
5136             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5137         );
5138         var c = this.colors;
5139         for(var i = 0, len = c.length; i < len; i++){
5140             t.add([c[i]]);
5141         }
5142         var el = document.createElement("div");
5143         el.className = this.itemCls;
5144         t.overwrite(el);
5145         container.dom.insertBefore(el, position);
5146         this.el = Roo.get(el);
5147         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5148         if(this.clickEvent != 'click'){
5149             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5150         }
5151     },
5152
5153     // private
5154     afterRender : function(){
5155         Roo.ColorPalette.superclass.afterRender.call(this);
5156         if(this.value){
5157             var s = this.value;
5158             this.value = null;
5159             this.select(s);
5160         }
5161     },
5162
5163     // private
5164     handleClick : function(e, t){
5165         e.preventDefault();
5166         if(!this.disabled){
5167             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5168             this.select(c.toUpperCase());
5169         }
5170     },
5171
5172     /**
5173      * Selects the specified color in the palette (fires the select event)
5174      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5175      */
5176     select : function(color){
5177         color = color.replace("#", "");
5178         if(color != this.value || this.allowReselect){
5179             var el = this.el;
5180             if(this.value){
5181                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5182             }
5183             el.child("a.color-"+color).addClass("x-color-palette-sel");
5184             this.value = color;
5185             this.fireEvent("select", this, color);
5186         }
5187     }
5188 });/*
5189  * Based on:
5190  * Ext JS Library 1.1.1
5191  * Copyright(c) 2006-2007, Ext JS, LLC.
5192  *
5193  * Originally Released Under LGPL - original licence link has changed is not relivant.
5194  *
5195  * Fork - LGPL
5196  * <script type="text/javascript">
5197  */
5198  
5199 /**
5200  * @class Roo.DatePicker
5201  * @extends Roo.Component
5202  * Simple date picker class.
5203  * @constructor
5204  * Create a new DatePicker
5205  * @param {Object} config The config object
5206  */
5207 Roo.DatePicker = function(config){
5208     Roo.DatePicker.superclass.constructor.call(this, config);
5209
5210     this.value = config && config.value ?
5211                  config.value.clearTime() : new Date().clearTime();
5212
5213     this.addEvents({
5214         /**
5215              * @event select
5216              * Fires when a date is selected
5217              * @param {DatePicker} this
5218              * @param {Date} date The selected date
5219              */
5220         'select': true,
5221         /**
5222              * @event monthchange
5223              * Fires when the displayed month changes 
5224              * @param {DatePicker} this
5225              * @param {Date} date The selected month
5226              */
5227         'monthchange': true
5228     });
5229
5230     if(this.handler){
5231         this.on("select", this.handler,  this.scope || this);
5232     }
5233     // build the disabledDatesRE
5234     if(!this.disabledDatesRE && this.disabledDates){
5235         var dd = this.disabledDates;
5236         var re = "(?:";
5237         for(var i = 0; i < dd.length; i++){
5238             re += dd[i];
5239             if(i != dd.length-1) {
5240                 re += "|";
5241             }
5242         }
5243         this.disabledDatesRE = new RegExp(re + ")");
5244     }
5245 };
5246
5247 Roo.extend(Roo.DatePicker, Roo.Component, {
5248     /**
5249      * @cfg {String} todayText
5250      * The text to display on the button that selects the current date (defaults to "Today")
5251      */
5252     todayText : "Today",
5253     /**
5254      * @cfg {String} okText
5255      * The text to display on the ok button
5256      */
5257     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5258     /**
5259      * @cfg {String} cancelText
5260      * The text to display on the cancel button
5261      */
5262     cancelText : "Cancel",
5263     /**
5264      * @cfg {String} todayTip
5265      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5266      */
5267     todayTip : "{0} (Spacebar)",
5268     /**
5269      * @cfg {Date} minDate
5270      * Minimum allowable date (JavaScript date object, defaults to null)
5271      */
5272     minDate : null,
5273     /**
5274      * @cfg {Date} maxDate
5275      * Maximum allowable date (JavaScript date object, defaults to null)
5276      */
5277     maxDate : null,
5278     /**
5279      * @cfg {String} minText
5280      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5281      */
5282     minText : "This date is before the minimum date",
5283     /**
5284      * @cfg {String} maxText
5285      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5286      */
5287     maxText : "This date is after the maximum date",
5288     /**
5289      * @cfg {String} format
5290      * The default date format string which can be overriden for localization support.  The format must be
5291      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5292      */
5293     format : "m/d/y",
5294     /**
5295      * @cfg {Array} disabledDays
5296      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5297      */
5298     disabledDays : null,
5299     /**
5300      * @cfg {String} disabledDaysText
5301      * The tooltip to display when the date falls on a disabled day (defaults to "")
5302      */
5303     disabledDaysText : "",
5304     /**
5305      * @cfg {RegExp} disabledDatesRE
5306      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5307      */
5308     disabledDatesRE : null,
5309     /**
5310      * @cfg {String} disabledDatesText
5311      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5312      */
5313     disabledDatesText : "",
5314     /**
5315      * @cfg {Boolean} constrainToViewport
5316      * True to constrain the date picker to the viewport (defaults to true)
5317      */
5318     constrainToViewport : true,
5319     /**
5320      * @cfg {Array} monthNames
5321      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5322      */
5323     monthNames : Date.monthNames,
5324     /**
5325      * @cfg {Array} dayNames
5326      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5327      */
5328     dayNames : Date.dayNames,
5329     /**
5330      * @cfg {String} nextText
5331      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5332      */
5333     nextText: 'Next Month (Control+Right)',
5334     /**
5335      * @cfg {String} prevText
5336      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5337      */
5338     prevText: 'Previous Month (Control+Left)',
5339     /**
5340      * @cfg {String} monthYearText
5341      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5342      */
5343     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5344     /**
5345      * @cfg {Number} startDay
5346      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5347      */
5348     startDay : 0,
5349     /**
5350      * @cfg {Bool} showClear
5351      * Show a clear button (usefull for date form elements that can be blank.)
5352      */
5353     
5354     showClear: false,
5355     
5356     /**
5357      * Sets the value of the date field
5358      * @param {Date} value The date to set
5359      */
5360     setValue : function(value){
5361         var old = this.value;
5362         
5363         if (typeof(value) == 'string') {
5364          
5365             value = Date.parseDate(value, this.format);
5366         }
5367         if (!value) {
5368             value = new Date();
5369         }
5370         
5371         this.value = value.clearTime(true);
5372         if(this.el){
5373             this.update(this.value);
5374         }
5375     },
5376
5377     /**
5378      * Gets the current selected value of the date field
5379      * @return {Date} The selected date
5380      */
5381     getValue : function(){
5382         return this.value;
5383     },
5384
5385     // private
5386     focus : function(){
5387         if(this.el){
5388             this.update(this.activeDate);
5389         }
5390     },
5391
5392     // privateval
5393     onRender : function(container, position){
5394         
5395         var m = [
5396              '<table cellspacing="0">',
5397                 '<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>',
5398                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5399         var dn = this.dayNames;
5400         for(var i = 0; i < 7; i++){
5401             var d = this.startDay+i;
5402             if(d > 6){
5403                 d = d-7;
5404             }
5405             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5406         }
5407         m[m.length] = "</tr></thead><tbody><tr>";
5408         for(var i = 0; i < 42; i++) {
5409             if(i % 7 == 0 && i != 0){
5410                 m[m.length] = "</tr><tr>";
5411             }
5412             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5413         }
5414         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5415             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5416
5417         var el = document.createElement("div");
5418         el.className = "x-date-picker";
5419         el.innerHTML = m.join("");
5420
5421         container.dom.insertBefore(el, position);
5422
5423         this.el = Roo.get(el);
5424         this.eventEl = Roo.get(el.firstChild);
5425
5426         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5427             handler: this.showPrevMonth,
5428             scope: this,
5429             preventDefault:true,
5430             stopDefault:true
5431         });
5432
5433         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5434             handler: this.showNextMonth,
5435             scope: this,
5436             preventDefault:true,
5437             stopDefault:true
5438         });
5439
5440         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5441
5442         this.monthPicker = this.el.down('div.x-date-mp');
5443         this.monthPicker.enableDisplayMode('block');
5444         
5445         var kn = new Roo.KeyNav(this.eventEl, {
5446             "left" : function(e){
5447                 e.ctrlKey ?
5448                     this.showPrevMonth() :
5449                     this.update(this.activeDate.add("d", -1));
5450             },
5451
5452             "right" : function(e){
5453                 e.ctrlKey ?
5454                     this.showNextMonth() :
5455                     this.update(this.activeDate.add("d", 1));
5456             },
5457
5458             "up" : function(e){
5459                 e.ctrlKey ?
5460                     this.showNextYear() :
5461                     this.update(this.activeDate.add("d", -7));
5462             },
5463
5464             "down" : function(e){
5465                 e.ctrlKey ?
5466                     this.showPrevYear() :
5467                     this.update(this.activeDate.add("d", 7));
5468             },
5469
5470             "pageUp" : function(e){
5471                 this.showNextMonth();
5472             },
5473
5474             "pageDown" : function(e){
5475                 this.showPrevMonth();
5476             },
5477
5478             "enter" : function(e){
5479                 e.stopPropagation();
5480                 return true;
5481             },
5482
5483             scope : this
5484         });
5485
5486         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5487
5488         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5489
5490         this.el.unselectable();
5491         
5492         this.cells = this.el.select("table.x-date-inner tbody td");
5493         this.textNodes = this.el.query("table.x-date-inner tbody span");
5494
5495         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5496             text: "&#160;",
5497             tooltip: this.monthYearText
5498         });
5499
5500         this.mbtn.on('click', this.showMonthPicker, this);
5501         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5502
5503
5504         var today = (new Date()).dateFormat(this.format);
5505         
5506         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5507         if (this.showClear) {
5508             baseTb.add( new Roo.Toolbar.Fill());
5509         }
5510         baseTb.add({
5511             text: String.format(this.todayText, today),
5512             tooltip: String.format(this.todayTip, today),
5513             handler: this.selectToday,
5514             scope: this
5515         });
5516         
5517         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5518             
5519         //});
5520         if (this.showClear) {
5521             
5522             baseTb.add( new Roo.Toolbar.Fill());
5523             baseTb.add({
5524                 text: '&#160;',
5525                 cls: 'x-btn-icon x-btn-clear',
5526                 handler: function() {
5527                     //this.value = '';
5528                     this.fireEvent("select", this, '');
5529                 },
5530                 scope: this
5531             });
5532         }
5533         
5534         
5535         if(Roo.isIE){
5536             this.el.repaint();
5537         }
5538         this.update(this.value);
5539     },
5540
5541     createMonthPicker : function(){
5542         if(!this.monthPicker.dom.firstChild){
5543             var buf = ['<table border="0" cellspacing="0">'];
5544             for(var i = 0; i < 6; i++){
5545                 buf.push(
5546                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5547                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5548                     i == 0 ?
5549                     '<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>' :
5550                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5551                 );
5552             }
5553             buf.push(
5554                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5555                     this.okText,
5556                     '</button><button type="button" class="x-date-mp-cancel">',
5557                     this.cancelText,
5558                     '</button></td></tr>',
5559                 '</table>'
5560             );
5561             this.monthPicker.update(buf.join(''));
5562             this.monthPicker.on('click', this.onMonthClick, this);
5563             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5564
5565             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5566             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5567
5568             this.mpMonths.each(function(m, a, i){
5569                 i += 1;
5570                 if((i%2) == 0){
5571                     m.dom.xmonth = 5 + Math.round(i * .5);
5572                 }else{
5573                     m.dom.xmonth = Math.round((i-1) * .5);
5574                 }
5575             });
5576         }
5577     },
5578
5579     showMonthPicker : function(){
5580         this.createMonthPicker();
5581         var size = this.el.getSize();
5582         this.monthPicker.setSize(size);
5583         this.monthPicker.child('table').setSize(size);
5584
5585         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5586         this.updateMPMonth(this.mpSelMonth);
5587         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5588         this.updateMPYear(this.mpSelYear);
5589
5590         this.monthPicker.slideIn('t', {duration:.2});
5591     },
5592
5593     updateMPYear : function(y){
5594         this.mpyear = y;
5595         var ys = this.mpYears.elements;
5596         for(var i = 1; i <= 10; i++){
5597             var td = ys[i-1], y2;
5598             if((i%2) == 0){
5599                 y2 = y + Math.round(i * .5);
5600                 td.firstChild.innerHTML = y2;
5601                 td.xyear = y2;
5602             }else{
5603                 y2 = y - (5-Math.round(i * .5));
5604                 td.firstChild.innerHTML = y2;
5605                 td.xyear = y2;
5606             }
5607             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5608         }
5609     },
5610
5611     updateMPMonth : function(sm){
5612         this.mpMonths.each(function(m, a, i){
5613             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5614         });
5615     },
5616
5617     selectMPMonth: function(m){
5618         
5619     },
5620
5621     onMonthClick : function(e, t){
5622         e.stopEvent();
5623         var el = new Roo.Element(t), pn;
5624         if(el.is('button.x-date-mp-cancel')){
5625             this.hideMonthPicker();
5626         }
5627         else if(el.is('button.x-date-mp-ok')){
5628             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5629             this.hideMonthPicker();
5630         }
5631         else if(pn = el.up('td.x-date-mp-month', 2)){
5632             this.mpMonths.removeClass('x-date-mp-sel');
5633             pn.addClass('x-date-mp-sel');
5634             this.mpSelMonth = pn.dom.xmonth;
5635         }
5636         else if(pn = el.up('td.x-date-mp-year', 2)){
5637             this.mpYears.removeClass('x-date-mp-sel');
5638             pn.addClass('x-date-mp-sel');
5639             this.mpSelYear = pn.dom.xyear;
5640         }
5641         else if(el.is('a.x-date-mp-prev')){
5642             this.updateMPYear(this.mpyear-10);
5643         }
5644         else if(el.is('a.x-date-mp-next')){
5645             this.updateMPYear(this.mpyear+10);
5646         }
5647     },
5648
5649     onMonthDblClick : function(e, t){
5650         e.stopEvent();
5651         var el = new Roo.Element(t), pn;
5652         if(pn = el.up('td.x-date-mp-month', 2)){
5653             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5654             this.hideMonthPicker();
5655         }
5656         else if(pn = el.up('td.x-date-mp-year', 2)){
5657             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5658             this.hideMonthPicker();
5659         }
5660     },
5661
5662     hideMonthPicker : function(disableAnim){
5663         if(this.monthPicker){
5664             if(disableAnim === true){
5665                 this.monthPicker.hide();
5666             }else{
5667                 this.monthPicker.slideOut('t', {duration:.2});
5668             }
5669         }
5670     },
5671
5672     // private
5673     showPrevMonth : function(e){
5674         this.update(this.activeDate.add("mo", -1));
5675     },
5676
5677     // private
5678     showNextMonth : function(e){
5679         this.update(this.activeDate.add("mo", 1));
5680     },
5681
5682     // private
5683     showPrevYear : function(){
5684         this.update(this.activeDate.add("y", -1));
5685     },
5686
5687     // private
5688     showNextYear : function(){
5689         this.update(this.activeDate.add("y", 1));
5690     },
5691
5692     // private
5693     handleMouseWheel : function(e){
5694         var delta = e.getWheelDelta();
5695         if(delta > 0){
5696             this.showPrevMonth();
5697             e.stopEvent();
5698         } else if(delta < 0){
5699             this.showNextMonth();
5700             e.stopEvent();
5701         }
5702     },
5703
5704     // private
5705     handleDateClick : function(e, t){
5706         e.stopEvent();
5707         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5708             this.setValue(new Date(t.dateValue));
5709             this.fireEvent("select", this, this.value);
5710         }
5711     },
5712
5713     // private
5714     selectToday : function(){
5715         this.setValue(new Date().clearTime());
5716         this.fireEvent("select", this, this.value);
5717     },
5718
5719     // private
5720     update : function(date)
5721     {
5722         var vd = this.activeDate;
5723         this.activeDate = date;
5724         if(vd && this.el){
5725             var t = date.getTime();
5726             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5727                 this.cells.removeClass("x-date-selected");
5728                 this.cells.each(function(c){
5729                    if(c.dom.firstChild.dateValue == t){
5730                        c.addClass("x-date-selected");
5731                        setTimeout(function(){
5732                             try{c.dom.firstChild.focus();}catch(e){}
5733                        }, 50);
5734                        return false;
5735                    }
5736                 });
5737                 return;
5738             }
5739         }
5740         
5741         var days = date.getDaysInMonth();
5742         var firstOfMonth = date.getFirstDateOfMonth();
5743         var startingPos = firstOfMonth.getDay()-this.startDay;
5744
5745         if(startingPos <= this.startDay){
5746             startingPos += 7;
5747         }
5748
5749         var pm = date.add("mo", -1);
5750         var prevStart = pm.getDaysInMonth()-startingPos;
5751
5752         var cells = this.cells.elements;
5753         var textEls = this.textNodes;
5754         days += startingPos;
5755
5756         // convert everything to numbers so it's fast
5757         var day = 86400000;
5758         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5759         var today = new Date().clearTime().getTime();
5760         var sel = date.clearTime().getTime();
5761         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5762         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5763         var ddMatch = this.disabledDatesRE;
5764         var ddText = this.disabledDatesText;
5765         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5766         var ddaysText = this.disabledDaysText;
5767         var format = this.format;
5768
5769         var setCellClass = function(cal, cell){
5770             cell.title = "";
5771             var t = d.getTime();
5772             cell.firstChild.dateValue = t;
5773             if(t == today){
5774                 cell.className += " x-date-today";
5775                 cell.title = cal.todayText;
5776             }
5777             if(t == sel){
5778                 cell.className += " x-date-selected";
5779                 setTimeout(function(){
5780                     try{cell.firstChild.focus();}catch(e){}
5781                 }, 50);
5782             }
5783             // disabling
5784             if(t < min) {
5785                 cell.className = " x-date-disabled";
5786                 cell.title = cal.minText;
5787                 return;
5788             }
5789             if(t > max) {
5790                 cell.className = " x-date-disabled";
5791                 cell.title = cal.maxText;
5792                 return;
5793             }
5794             if(ddays){
5795                 if(ddays.indexOf(d.getDay()) != -1){
5796                     cell.title = ddaysText;
5797                     cell.className = " x-date-disabled";
5798                 }
5799             }
5800             if(ddMatch && format){
5801                 var fvalue = d.dateFormat(format);
5802                 if(ddMatch.test(fvalue)){
5803                     cell.title = ddText.replace("%0", fvalue);
5804                     cell.className = " x-date-disabled";
5805                 }
5806             }
5807         };
5808
5809         var i = 0;
5810         for(; i < startingPos; i++) {
5811             textEls[i].innerHTML = (++prevStart);
5812             d.setDate(d.getDate()+1);
5813             cells[i].className = "x-date-prevday";
5814             setCellClass(this, cells[i]);
5815         }
5816         for(; i < days; i++){
5817             intDay = i - startingPos + 1;
5818             textEls[i].innerHTML = (intDay);
5819             d.setDate(d.getDate()+1);
5820             cells[i].className = "x-date-active";
5821             setCellClass(this, cells[i]);
5822         }
5823         var extraDays = 0;
5824         for(; i < 42; i++) {
5825              textEls[i].innerHTML = (++extraDays);
5826              d.setDate(d.getDate()+1);
5827              cells[i].className = "x-date-nextday";
5828              setCellClass(this, cells[i]);
5829         }
5830
5831         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5832         this.fireEvent('monthchange', this, date);
5833         
5834         if(!this.internalRender){
5835             var main = this.el.dom.firstChild;
5836             var w = main.offsetWidth;
5837             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5838             Roo.fly(main).setWidth(w);
5839             this.internalRender = true;
5840             // opera does not respect the auto grow header center column
5841             // then, after it gets a width opera refuses to recalculate
5842             // without a second pass
5843             if(Roo.isOpera && !this.secondPass){
5844                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5845                 this.secondPass = true;
5846                 this.update.defer(10, this, [date]);
5847             }
5848         }
5849         
5850         
5851     }
5852 });        /*
5853  * Based on:
5854  * Ext JS Library 1.1.1
5855  * Copyright(c) 2006-2007, Ext JS, LLC.
5856  *
5857  * Originally Released Under LGPL - original licence link has changed is not relivant.
5858  *
5859  * Fork - LGPL
5860  * <script type="text/javascript">
5861  */
5862 /**
5863  * @class Roo.TabPanel
5864  * @extends Roo.util.Observable
5865  * A lightweight tab container.
5866  * <br><br>
5867  * Usage:
5868  * <pre><code>
5869 // basic tabs 1, built from existing content
5870 var tabs = new Roo.TabPanel("tabs1");
5871 tabs.addTab("script", "View Script");
5872 tabs.addTab("markup", "View Markup");
5873 tabs.activate("script");
5874
5875 // more advanced tabs, built from javascript
5876 var jtabs = new Roo.TabPanel("jtabs");
5877 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5878
5879 // set up the UpdateManager
5880 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5881 var updater = tab2.getUpdateManager();
5882 updater.setDefaultUrl("ajax1.htm");
5883 tab2.on('activate', updater.refresh, updater, true);
5884
5885 // Use setUrl for Ajax loading
5886 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5887 tab3.setUrl("ajax2.htm", null, true);
5888
5889 // Disabled tab
5890 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5891 tab4.disable();
5892
5893 jtabs.activate("jtabs-1");
5894  * </code></pre>
5895  * @constructor
5896  * Create a new TabPanel.
5897  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5898  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5899  */
5900 Roo.TabPanel = function(container, config){
5901     /**
5902     * The container element for this TabPanel.
5903     * @type Roo.Element
5904     */
5905     this.el = Roo.get(container, true);
5906     if(config){
5907         if(typeof config == "boolean"){
5908             this.tabPosition = config ? "bottom" : "top";
5909         }else{
5910             Roo.apply(this, config);
5911         }
5912     }
5913     if(this.tabPosition == "bottom"){
5914         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5915         this.el.addClass("x-tabs-bottom");
5916     }
5917     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5918     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5919     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5920     if(Roo.isIE){
5921         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5922     }
5923     if(this.tabPosition != "bottom"){
5924         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5925          * @type Roo.Element
5926          */
5927         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5928         this.el.addClass("x-tabs-top");
5929     }
5930     this.items = [];
5931
5932     this.bodyEl.setStyle("position", "relative");
5933
5934     this.active = null;
5935     this.activateDelegate = this.activate.createDelegate(this);
5936
5937     this.addEvents({
5938         /**
5939          * @event tabchange
5940          * Fires when the active tab changes
5941          * @param {Roo.TabPanel} this
5942          * @param {Roo.TabPanelItem} activePanel The new active tab
5943          */
5944         "tabchange": true,
5945         /**
5946          * @event beforetabchange
5947          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5948          * @param {Roo.TabPanel} this
5949          * @param {Object} e Set cancel to true on this object to cancel the tab change
5950          * @param {Roo.TabPanelItem} tab The tab being changed to
5951          */
5952         "beforetabchange" : true
5953     });
5954
5955     Roo.EventManager.onWindowResize(this.onResize, this);
5956     this.cpad = this.el.getPadding("lr");
5957     this.hiddenCount = 0;
5958
5959
5960     // toolbar on the tabbar support...
5961     if (this.toolbar) {
5962         var tcfg = this.toolbar;
5963         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5964         this.toolbar = new Roo.Toolbar(tcfg);
5965         if (Roo.isSafari) {
5966             var tbl = tcfg.container.child('table', true);
5967             tbl.setAttribute('width', '100%');
5968         }
5969         
5970     }
5971    
5972
5973
5974     Roo.TabPanel.superclass.constructor.call(this);
5975 };
5976
5977 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5978     /*
5979      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5980      */
5981     tabPosition : "top",
5982     /*
5983      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5984      */
5985     currentTabWidth : 0,
5986     /*
5987      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5988      */
5989     minTabWidth : 40,
5990     /*
5991      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5992      */
5993     maxTabWidth : 250,
5994     /*
5995      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5996      */
5997     preferredTabWidth : 175,
5998     /*
5999      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6000      */
6001     resizeTabs : false,
6002     /*
6003      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6004      */
6005     monitorResize : true,
6006     /*
6007      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6008      */
6009     toolbar : false,
6010
6011     /**
6012      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6013      * @param {String} id The id of the div to use <b>or create</b>
6014      * @param {String} text The text for the tab
6015      * @param {String} content (optional) Content to put in the TabPanelItem body
6016      * @param {Boolean} closable (optional) True to create a close icon on the tab
6017      * @return {Roo.TabPanelItem} The created TabPanelItem
6018      */
6019     addTab : function(id, text, content, closable){
6020         var item = new Roo.TabPanelItem(this, id, text, closable);
6021         this.addTabItem(item);
6022         if(content){
6023             item.setContent(content);
6024         }
6025         return item;
6026     },
6027
6028     /**
6029      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6030      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6031      * @return {Roo.TabPanelItem}
6032      */
6033     getTab : function(id){
6034         return this.items[id];
6035     },
6036
6037     /**
6038      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6039      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6040      */
6041     hideTab : function(id){
6042         var t = this.items[id];
6043         if(!t.isHidden()){
6044            t.setHidden(true);
6045            this.hiddenCount++;
6046            this.autoSizeTabs();
6047         }
6048     },
6049
6050     /**
6051      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6052      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6053      */
6054     unhideTab : function(id){
6055         var t = this.items[id];
6056         if(t.isHidden()){
6057            t.setHidden(false);
6058            this.hiddenCount--;
6059            this.autoSizeTabs();
6060         }
6061     },
6062
6063     /**
6064      * Adds an existing {@link Roo.TabPanelItem}.
6065      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6066      */
6067     addTabItem : function(item){
6068         this.items[item.id] = item;
6069         this.items.push(item);
6070         if(this.resizeTabs){
6071            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6072            this.autoSizeTabs();
6073         }else{
6074             item.autoSize();
6075         }
6076     },
6077
6078     /**
6079      * Removes a {@link Roo.TabPanelItem}.
6080      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6081      */
6082     removeTab : function(id){
6083         var items = this.items;
6084         var tab = items[id];
6085         if(!tab) { return; }
6086         var index = items.indexOf(tab);
6087         if(this.active == tab && items.length > 1){
6088             var newTab = this.getNextAvailable(index);
6089             if(newTab) {
6090                 newTab.activate();
6091             }
6092         }
6093         this.stripEl.dom.removeChild(tab.pnode.dom);
6094         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6095             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6096         }
6097         items.splice(index, 1);
6098         delete this.items[tab.id];
6099         tab.fireEvent("close", tab);
6100         tab.purgeListeners();
6101         this.autoSizeTabs();
6102     },
6103
6104     getNextAvailable : function(start){
6105         var items = this.items;
6106         var index = start;
6107         // look for a next tab that will slide over to
6108         // replace the one being removed
6109         while(index < items.length){
6110             var item = items[++index];
6111             if(item && !item.isHidden()){
6112                 return item;
6113             }
6114         }
6115         // if one isn't found select the previous tab (on the left)
6116         index = start;
6117         while(index >= 0){
6118             var item = items[--index];
6119             if(item && !item.isHidden()){
6120                 return item;
6121             }
6122         }
6123         return null;
6124     },
6125
6126     /**
6127      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6128      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6129      */
6130     disableTab : function(id){
6131         var tab = this.items[id];
6132         if(tab && this.active != tab){
6133             tab.disable();
6134         }
6135     },
6136
6137     /**
6138      * Enables a {@link Roo.TabPanelItem} that is disabled.
6139      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6140      */
6141     enableTab : function(id){
6142         var tab = this.items[id];
6143         tab.enable();
6144     },
6145
6146     /**
6147      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6148      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6149      * @return {Roo.TabPanelItem} The TabPanelItem.
6150      */
6151     activate : function(id){
6152         var tab = this.items[id];
6153         if(!tab){
6154             return null;
6155         }
6156         if(tab == this.active || tab.disabled){
6157             return tab;
6158         }
6159         var e = {};
6160         this.fireEvent("beforetabchange", this, e, tab);
6161         if(e.cancel !== true && !tab.disabled){
6162             if(this.active){
6163                 this.active.hide();
6164             }
6165             this.active = this.items[id];
6166             this.active.show();
6167             this.fireEvent("tabchange", this, this.active);
6168         }
6169         return tab;
6170     },
6171
6172     /**
6173      * Gets the active {@link Roo.TabPanelItem}.
6174      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6175      */
6176     getActiveTab : function(){
6177         return this.active;
6178     },
6179
6180     /**
6181      * Updates the tab body element to fit the height of the container element
6182      * for overflow scrolling
6183      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6184      */
6185     syncHeight : function(targetHeight){
6186         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6187         var bm = this.bodyEl.getMargins();
6188         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6189         this.bodyEl.setHeight(newHeight);
6190         return newHeight;
6191     },
6192
6193     onResize : function(){
6194         if(this.monitorResize){
6195             this.autoSizeTabs();
6196         }
6197     },
6198
6199     /**
6200      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6201      */
6202     beginUpdate : function(){
6203         this.updating = true;
6204     },
6205
6206     /**
6207      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6208      */
6209     endUpdate : function(){
6210         this.updating = false;
6211         this.autoSizeTabs();
6212     },
6213
6214     /**
6215      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6216      */
6217     autoSizeTabs : function(){
6218         var count = this.items.length;
6219         var vcount = count - this.hiddenCount;
6220         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6221             return;
6222         }
6223         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6224         var availWidth = Math.floor(w / vcount);
6225         var b = this.stripBody;
6226         if(b.getWidth() > w){
6227             var tabs = this.items;
6228             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6229             if(availWidth < this.minTabWidth){
6230                 /*if(!this.sleft){    // incomplete scrolling code
6231                     this.createScrollButtons();
6232                 }
6233                 this.showScroll();
6234                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6235             }
6236         }else{
6237             if(this.currentTabWidth < this.preferredTabWidth){
6238                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6239             }
6240         }
6241     },
6242
6243     /**
6244      * Returns the number of tabs in this TabPanel.
6245      * @return {Number}
6246      */
6247      getCount : function(){
6248          return this.items.length;
6249      },
6250
6251     /**
6252      * Resizes all the tabs to the passed width
6253      * @param {Number} The new width
6254      */
6255     setTabWidth : function(width){
6256         this.currentTabWidth = width;
6257         for(var i = 0, len = this.items.length; i < len; i++) {
6258                 if(!this.items[i].isHidden()) {
6259                 this.items[i].setWidth(width);
6260             }
6261         }
6262     },
6263
6264     /**
6265      * Destroys this TabPanel
6266      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6267      */
6268     destroy : function(removeEl){
6269         Roo.EventManager.removeResizeListener(this.onResize, this);
6270         for(var i = 0, len = this.items.length; i < len; i++){
6271             this.items[i].purgeListeners();
6272         }
6273         if(removeEl === true){
6274             this.el.update("");
6275             this.el.remove();
6276         }
6277     }
6278 });
6279
6280 /**
6281  * @class Roo.TabPanelItem
6282  * @extends Roo.util.Observable
6283  * Represents an individual item (tab plus body) in a TabPanel.
6284  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6285  * @param {String} id The id of this TabPanelItem
6286  * @param {String} text The text for the tab of this TabPanelItem
6287  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6288  */
6289 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6290     /**
6291      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6292      * @type Roo.TabPanel
6293      */
6294     this.tabPanel = tabPanel;
6295     /**
6296      * The id for this TabPanelItem
6297      * @type String
6298      */
6299     this.id = id;
6300     /** @private */
6301     this.disabled = false;
6302     /** @private */
6303     this.text = text;
6304     /** @private */
6305     this.loaded = false;
6306     this.closable = closable;
6307
6308     /**
6309      * The body element for this TabPanelItem.
6310      * @type Roo.Element
6311      */
6312     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6313     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6314     this.bodyEl.setStyle("display", "block");
6315     this.bodyEl.setStyle("zoom", "1");
6316     this.hideAction();
6317
6318     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6319     /** @private */
6320     this.el = Roo.get(els.el, true);
6321     this.inner = Roo.get(els.inner, true);
6322     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6323     this.pnode = Roo.get(els.el.parentNode, true);
6324     this.el.on("mousedown", this.onTabMouseDown, this);
6325     this.el.on("click", this.onTabClick, this);
6326     /** @private */
6327     if(closable){
6328         var c = Roo.get(els.close, true);
6329         c.dom.title = this.closeText;
6330         c.addClassOnOver("close-over");
6331         c.on("click", this.closeClick, this);
6332      }
6333
6334     this.addEvents({
6335          /**
6336          * @event activate
6337          * Fires when this tab becomes the active tab.
6338          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6339          * @param {Roo.TabPanelItem} this
6340          */
6341         "activate": true,
6342         /**
6343          * @event beforeclose
6344          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6345          * @param {Roo.TabPanelItem} this
6346          * @param {Object} e Set cancel to true on this object to cancel the close.
6347          */
6348         "beforeclose": true,
6349         /**
6350          * @event close
6351          * Fires when this tab is closed.
6352          * @param {Roo.TabPanelItem} this
6353          */
6354          "close": true,
6355         /**
6356          * @event deactivate
6357          * Fires when this tab is no longer the active tab.
6358          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6359          * @param {Roo.TabPanelItem} this
6360          */
6361          "deactivate" : true
6362     });
6363     this.hidden = false;
6364
6365     Roo.TabPanelItem.superclass.constructor.call(this);
6366 };
6367
6368 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6369     purgeListeners : function(){
6370        Roo.util.Observable.prototype.purgeListeners.call(this);
6371        this.el.removeAllListeners();
6372     },
6373     /**
6374      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6375      */
6376     show : function(){
6377         this.pnode.addClass("on");
6378         this.showAction();
6379         if(Roo.isOpera){
6380             this.tabPanel.stripWrap.repaint();
6381         }
6382         this.fireEvent("activate", this.tabPanel, this);
6383     },
6384
6385     /**
6386      * Returns true if this tab is the active tab.
6387      * @return {Boolean}
6388      */
6389     isActive : function(){
6390         return this.tabPanel.getActiveTab() == this;
6391     },
6392
6393     /**
6394      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6395      */
6396     hide : function(){
6397         this.pnode.removeClass("on");
6398         this.hideAction();
6399         this.fireEvent("deactivate", this.tabPanel, this);
6400     },
6401
6402     hideAction : function(){
6403         this.bodyEl.hide();
6404         this.bodyEl.setStyle("position", "absolute");
6405         this.bodyEl.setLeft("-20000px");
6406         this.bodyEl.setTop("-20000px");
6407     },
6408
6409     showAction : function(){
6410         this.bodyEl.setStyle("position", "relative");
6411         this.bodyEl.setTop("");
6412         this.bodyEl.setLeft("");
6413         this.bodyEl.show();
6414     },
6415
6416     /**
6417      * Set the tooltip for the tab.
6418      * @param {String} tooltip The tab's tooltip
6419      */
6420     setTooltip : function(text){
6421         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6422             this.textEl.dom.qtip = text;
6423             this.textEl.dom.removeAttribute('title');
6424         }else{
6425             this.textEl.dom.title = text;
6426         }
6427     },
6428
6429     onTabClick : function(e){
6430         e.preventDefault();
6431         this.tabPanel.activate(this.id);
6432     },
6433
6434     onTabMouseDown : function(e){
6435         e.preventDefault();
6436         this.tabPanel.activate(this.id);
6437     },
6438
6439     getWidth : function(){
6440         return this.inner.getWidth();
6441     },
6442
6443     setWidth : function(width){
6444         var iwidth = width - this.pnode.getPadding("lr");
6445         this.inner.setWidth(iwidth);
6446         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6447         this.pnode.setWidth(width);
6448     },
6449
6450     /**
6451      * Show or hide the tab
6452      * @param {Boolean} hidden True to hide or false to show.
6453      */
6454     setHidden : function(hidden){
6455         this.hidden = hidden;
6456         this.pnode.setStyle("display", hidden ? "none" : "");
6457     },
6458
6459     /**
6460      * Returns true if this tab is "hidden"
6461      * @return {Boolean}
6462      */
6463     isHidden : function(){
6464         return this.hidden;
6465     },
6466
6467     /**
6468      * Returns the text for this tab
6469      * @return {String}
6470      */
6471     getText : function(){
6472         return this.text;
6473     },
6474
6475     autoSize : function(){
6476         //this.el.beginMeasure();
6477         this.textEl.setWidth(1);
6478         /*
6479          *  #2804 [new] Tabs in Roojs
6480          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6481          */
6482         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6483         //this.el.endMeasure();
6484     },
6485
6486     /**
6487      * Sets the text for the tab (Note: this also sets the tooltip text)
6488      * @param {String} text The tab's text and tooltip
6489      */
6490     setText : function(text){
6491         this.text = text;
6492         this.textEl.update(text);
6493         this.setTooltip(text);
6494         if(!this.tabPanel.resizeTabs){
6495             this.autoSize();
6496         }
6497     },
6498     /**
6499      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6500      */
6501     activate : function(){
6502         this.tabPanel.activate(this.id);
6503     },
6504
6505     /**
6506      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6507      */
6508     disable : function(){
6509         if(this.tabPanel.active != this){
6510             this.disabled = true;
6511             this.pnode.addClass("disabled");
6512         }
6513     },
6514
6515     /**
6516      * Enables this TabPanelItem if it was previously disabled.
6517      */
6518     enable : function(){
6519         this.disabled = false;
6520         this.pnode.removeClass("disabled");
6521     },
6522
6523     /**
6524      * Sets the content for this TabPanelItem.
6525      * @param {String} content The content
6526      * @param {Boolean} loadScripts true to look for and load scripts
6527      */
6528     setContent : function(content, loadScripts){
6529         this.bodyEl.update(content, loadScripts);
6530     },
6531
6532     /**
6533      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6534      * @return {Roo.UpdateManager} The UpdateManager
6535      */
6536     getUpdateManager : function(){
6537         return this.bodyEl.getUpdateManager();
6538     },
6539
6540     /**
6541      * Set a URL to be used to load the content for this TabPanelItem.
6542      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6543      * @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)
6544      * @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)
6545      * @return {Roo.UpdateManager} The UpdateManager
6546      */
6547     setUrl : function(url, params, loadOnce){
6548         if(this.refreshDelegate){
6549             this.un('activate', this.refreshDelegate);
6550         }
6551         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6552         this.on("activate", this.refreshDelegate);
6553         return this.bodyEl.getUpdateManager();
6554     },
6555
6556     /** @private */
6557     _handleRefresh : function(url, params, loadOnce){
6558         if(!loadOnce || !this.loaded){
6559             var updater = this.bodyEl.getUpdateManager();
6560             updater.update(url, params, this._setLoaded.createDelegate(this));
6561         }
6562     },
6563
6564     /**
6565      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6566      *   Will fail silently if the setUrl method has not been called.
6567      *   This does not activate the panel, just updates its content.
6568      */
6569     refresh : function(){
6570         if(this.refreshDelegate){
6571            this.loaded = false;
6572            this.refreshDelegate();
6573         }
6574     },
6575
6576     /** @private */
6577     _setLoaded : function(){
6578         this.loaded = true;
6579     },
6580
6581     /** @private */
6582     closeClick : function(e){
6583         var o = {};
6584         e.stopEvent();
6585         this.fireEvent("beforeclose", this, o);
6586         if(o.cancel !== true){
6587             this.tabPanel.removeTab(this.id);
6588         }
6589     },
6590     /**
6591      * The text displayed in the tooltip for the close icon.
6592      * @type String
6593      */
6594     closeText : "Close this tab"
6595 });
6596
6597 /** @private */
6598 Roo.TabPanel.prototype.createStrip = function(container){
6599     var strip = document.createElement("div");
6600     strip.className = "x-tabs-wrap";
6601     container.appendChild(strip);
6602     return strip;
6603 };
6604 /** @private */
6605 Roo.TabPanel.prototype.createStripList = function(strip){
6606     // div wrapper for retard IE
6607     // returns the "tr" element.
6608     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6609         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6610         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6611     return strip.firstChild.firstChild.firstChild.firstChild;
6612 };
6613 /** @private */
6614 Roo.TabPanel.prototype.createBody = function(container){
6615     var body = document.createElement("div");
6616     Roo.id(body, "tab-body");
6617     Roo.fly(body).addClass("x-tabs-body");
6618     container.appendChild(body);
6619     return body;
6620 };
6621 /** @private */
6622 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6623     var body = Roo.getDom(id);
6624     if(!body){
6625         body = document.createElement("div");
6626         body.id = id;
6627     }
6628     Roo.fly(body).addClass("x-tabs-item-body");
6629     bodyEl.insertBefore(body, bodyEl.firstChild);
6630     return body;
6631 };
6632 /** @private */
6633 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6634     var td = document.createElement("td");
6635     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6636     //stripEl.appendChild(td);
6637     if(closable){
6638         td.className = "x-tabs-closable";
6639         if(!this.closeTpl){
6640             this.closeTpl = new Roo.Template(
6641                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6642                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6643                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6644             );
6645         }
6646         var el = this.closeTpl.overwrite(td, {"text": text});
6647         var close = el.getElementsByTagName("div")[0];
6648         var inner = el.getElementsByTagName("em")[0];
6649         return {"el": el, "close": close, "inner": inner};
6650     } else {
6651         if(!this.tabTpl){
6652             this.tabTpl = new Roo.Template(
6653                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6654                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6655             );
6656         }
6657         var el = this.tabTpl.overwrite(td, {"text": text});
6658         var inner = el.getElementsByTagName("em")[0];
6659         return {"el": el, "inner": inner};
6660     }
6661 };/*
6662  * Based on:
6663  * Ext JS Library 1.1.1
6664  * Copyright(c) 2006-2007, Ext JS, LLC.
6665  *
6666  * Originally Released Under LGPL - original licence link has changed is not relivant.
6667  *
6668  * Fork - LGPL
6669  * <script type="text/javascript">
6670  */
6671
6672 /**
6673  * @class Roo.Button
6674  * @extends Roo.util.Observable
6675  * Simple Button class
6676  * @cfg {String} text The button text
6677  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6678  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6679  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6680  * @cfg {Object} scope The scope of the handler
6681  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6682  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6683  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6684  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6685  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6686  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6687    applies if enableToggle = true)
6688  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6689  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6690   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6691  * @constructor
6692  * Create a new button
6693  * @param {Object} config The config object
6694  */
6695 Roo.Button = function(renderTo, config)
6696 {
6697     if (!config) {
6698         config = renderTo;
6699         renderTo = config.renderTo || false;
6700     }
6701     
6702     Roo.apply(this, config);
6703     this.addEvents({
6704         /**
6705              * @event click
6706              * Fires when this button is clicked
6707              * @param {Button} this
6708              * @param {EventObject} e The click event
6709              */
6710             "click" : true,
6711         /**
6712              * @event toggle
6713              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6714              * @param {Button} this
6715              * @param {Boolean} pressed
6716              */
6717             "toggle" : true,
6718         /**
6719              * @event mouseover
6720              * Fires when the mouse hovers over the button
6721              * @param {Button} this
6722              * @param {Event} e The event object
6723              */
6724         'mouseover' : true,
6725         /**
6726              * @event mouseout
6727              * Fires when the mouse exits the button
6728              * @param {Button} this
6729              * @param {Event} e The event object
6730              */
6731         'mouseout': true,
6732          /**
6733              * @event render
6734              * Fires when the button is rendered
6735              * @param {Button} this
6736              */
6737         'render': true
6738     });
6739     if(this.menu){
6740         this.menu = Roo.menu.MenuMgr.get(this.menu);
6741     }
6742     // register listeners first!!  - so render can be captured..
6743     Roo.util.Observable.call(this);
6744     if(renderTo){
6745         this.render(renderTo);
6746     }
6747     
6748   
6749 };
6750
6751 Roo.extend(Roo.Button, Roo.util.Observable, {
6752     /**
6753      * 
6754      */
6755     
6756     /**
6757      * Read-only. True if this button is hidden
6758      * @type Boolean
6759      */
6760     hidden : false,
6761     /**
6762      * Read-only. True if this button is disabled
6763      * @type Boolean
6764      */
6765     disabled : false,
6766     /**
6767      * Read-only. True if this button is pressed (only if enableToggle = true)
6768      * @type Boolean
6769      */
6770     pressed : false,
6771
6772     /**
6773      * @cfg {Number} tabIndex 
6774      * The DOM tabIndex for this button (defaults to undefined)
6775      */
6776     tabIndex : undefined,
6777
6778     /**
6779      * @cfg {Boolean} enableToggle
6780      * True to enable pressed/not pressed toggling (defaults to false)
6781      */
6782     enableToggle: false,
6783     /**
6784      * @cfg {Mixed} menu
6785      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6786      */
6787     menu : undefined,
6788     /**
6789      * @cfg {String} menuAlign
6790      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6791      */
6792     menuAlign : "tl-bl?",
6793
6794     /**
6795      * @cfg {String} iconCls
6796      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6797      */
6798     iconCls : undefined,
6799     /**
6800      * @cfg {String} type
6801      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6802      */
6803     type : 'button',
6804
6805     // private
6806     menuClassTarget: 'tr',
6807
6808     /**
6809      * @cfg {String} clickEvent
6810      * The type of event to map to the button's event handler (defaults to 'click')
6811      */
6812     clickEvent : 'click',
6813
6814     /**
6815      * @cfg {Boolean} handleMouseEvents
6816      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6817      */
6818     handleMouseEvents : true,
6819
6820     /**
6821      * @cfg {String} tooltipType
6822      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6823      */
6824     tooltipType : 'qtip',
6825
6826     /**
6827      * @cfg {String} cls
6828      * A CSS class to apply to the button's main element.
6829      */
6830     
6831     /**
6832      * @cfg {Roo.Template} template (Optional)
6833      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6834      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6835      * require code modifications if required elements (e.g. a button) aren't present.
6836      */
6837
6838     // private
6839     render : function(renderTo){
6840         var btn;
6841         if(this.hideParent){
6842             this.parentEl = Roo.get(renderTo);
6843         }
6844         if(!this.dhconfig){
6845             if(!this.template){
6846                 if(!Roo.Button.buttonTemplate){
6847                     // hideous table template
6848                     Roo.Button.buttonTemplate = new Roo.Template(
6849                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6850                         '<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>',
6851                         "</tr></tbody></table>");
6852                 }
6853                 this.template = Roo.Button.buttonTemplate;
6854             }
6855             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6856             var btnEl = btn.child("button:first");
6857             btnEl.on('focus', this.onFocus, this);
6858             btnEl.on('blur', this.onBlur, this);
6859             if(this.cls){
6860                 btn.addClass(this.cls);
6861             }
6862             if(this.icon){
6863                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6864             }
6865             if(this.iconCls){
6866                 btnEl.addClass(this.iconCls);
6867                 if(!this.cls){
6868                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6869                 }
6870             }
6871             if(this.tabIndex !== undefined){
6872                 btnEl.dom.tabIndex = this.tabIndex;
6873             }
6874             if(this.tooltip){
6875                 if(typeof this.tooltip == 'object'){
6876                     Roo.QuickTips.tips(Roo.apply({
6877                           target: btnEl.id
6878                     }, this.tooltip));
6879                 } else {
6880                     btnEl.dom[this.tooltipType] = this.tooltip;
6881                 }
6882             }
6883         }else{
6884             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6885         }
6886         this.el = btn;
6887         if(this.id){
6888             this.el.dom.id = this.el.id = this.id;
6889         }
6890         if(this.menu){
6891             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6892             this.menu.on("show", this.onMenuShow, this);
6893             this.menu.on("hide", this.onMenuHide, this);
6894         }
6895         btn.addClass("x-btn");
6896         if(Roo.isIE && !Roo.isIE7){
6897             this.autoWidth.defer(1, this);
6898         }else{
6899             this.autoWidth();
6900         }
6901         if(this.handleMouseEvents){
6902             btn.on("mouseover", this.onMouseOver, this);
6903             btn.on("mouseout", this.onMouseOut, this);
6904             btn.on("mousedown", this.onMouseDown, this);
6905         }
6906         btn.on(this.clickEvent, this.onClick, this);
6907         //btn.on("mouseup", this.onMouseUp, this);
6908         if(this.hidden){
6909             this.hide();
6910         }
6911         if(this.disabled){
6912             this.disable();
6913         }
6914         Roo.ButtonToggleMgr.register(this);
6915         if(this.pressed){
6916             this.el.addClass("x-btn-pressed");
6917         }
6918         if(this.repeat){
6919             var repeater = new Roo.util.ClickRepeater(btn,
6920                 typeof this.repeat == "object" ? this.repeat : {}
6921             );
6922             repeater.on("click", this.onClick,  this);
6923         }
6924         
6925         this.fireEvent('render', this);
6926         
6927     },
6928     /**
6929      * Returns the button's underlying element
6930      * @return {Roo.Element} The element
6931      */
6932     getEl : function(){
6933         return this.el;  
6934     },
6935     
6936     /**
6937      * Destroys this Button and removes any listeners.
6938      */
6939     destroy : function(){
6940         Roo.ButtonToggleMgr.unregister(this);
6941         this.el.removeAllListeners();
6942         this.purgeListeners();
6943         this.el.remove();
6944     },
6945
6946     // private
6947     autoWidth : function(){
6948         if(this.el){
6949             this.el.setWidth("auto");
6950             if(Roo.isIE7 && Roo.isStrict){
6951                 var ib = this.el.child('button');
6952                 if(ib && ib.getWidth() > 20){
6953                     ib.clip();
6954                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6955                 }
6956             }
6957             if(this.minWidth){
6958                 if(this.hidden){
6959                     this.el.beginMeasure();
6960                 }
6961                 if(this.el.getWidth() < this.minWidth){
6962                     this.el.setWidth(this.minWidth);
6963                 }
6964                 if(this.hidden){
6965                     this.el.endMeasure();
6966                 }
6967             }
6968         }
6969     },
6970
6971     /**
6972      * Assigns this button's click handler
6973      * @param {Function} handler The function to call when the button is clicked
6974      * @param {Object} scope (optional) Scope for the function passed in
6975      */
6976     setHandler : function(handler, scope){
6977         this.handler = handler;
6978         this.scope = scope;  
6979     },
6980     
6981     /**
6982      * Sets this button's text
6983      * @param {String} text The button text
6984      */
6985     setText : function(text){
6986         this.text = text;
6987         if(this.el){
6988             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6989         }
6990         this.autoWidth();
6991     },
6992     
6993     /**
6994      * Gets the text for this button
6995      * @return {String} The button text
6996      */
6997     getText : function(){
6998         return this.text;  
6999     },
7000     
7001     /**
7002      * Show this button
7003      */
7004     show: function(){
7005         this.hidden = false;
7006         if(this.el){
7007             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7008         }
7009     },
7010     
7011     /**
7012      * Hide this button
7013      */
7014     hide: function(){
7015         this.hidden = true;
7016         if(this.el){
7017             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7018         }
7019     },
7020     
7021     /**
7022      * Convenience function for boolean show/hide
7023      * @param {Boolean} visible True to show, false to hide
7024      */
7025     setVisible: function(visible){
7026         if(visible) {
7027             this.show();
7028         }else{
7029             this.hide();
7030         }
7031     },
7032     
7033     /**
7034      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7035      * @param {Boolean} state (optional) Force a particular state
7036      */
7037     toggle : function(state){
7038         state = state === undefined ? !this.pressed : state;
7039         if(state != this.pressed){
7040             if(state){
7041                 this.el.addClass("x-btn-pressed");
7042                 this.pressed = true;
7043                 this.fireEvent("toggle", this, true);
7044             }else{
7045                 this.el.removeClass("x-btn-pressed");
7046                 this.pressed = false;
7047                 this.fireEvent("toggle", this, false);
7048             }
7049             if(this.toggleHandler){
7050                 this.toggleHandler.call(this.scope || this, this, state);
7051             }
7052         }
7053     },
7054     
7055     /**
7056      * Focus the button
7057      */
7058     focus : function(){
7059         this.el.child('button:first').focus();
7060     },
7061     
7062     /**
7063      * Disable this button
7064      */
7065     disable : function(){
7066         if(this.el){
7067             this.el.addClass("x-btn-disabled");
7068         }
7069         this.disabled = true;
7070     },
7071     
7072     /**
7073      * Enable this button
7074      */
7075     enable : function(){
7076         if(this.el){
7077             this.el.removeClass("x-btn-disabled");
7078         }
7079         this.disabled = false;
7080     },
7081
7082     /**
7083      * Convenience function for boolean enable/disable
7084      * @param {Boolean} enabled True to enable, false to disable
7085      */
7086     setDisabled : function(v){
7087         this[v !== true ? "enable" : "disable"]();
7088     },
7089
7090     // private
7091     onClick : function(e)
7092     {
7093         if(e){
7094             e.preventDefault();
7095         }
7096         if(e.button != 0){
7097             return;
7098         }
7099         if(!this.disabled){
7100             if(this.enableToggle){
7101                 this.toggle();
7102             }
7103             if(this.menu && !this.menu.isVisible()){
7104                 this.menu.show(this.el, this.menuAlign);
7105             }
7106             this.fireEvent("click", this, e);
7107             if(this.handler){
7108                 this.el.removeClass("x-btn-over");
7109                 this.handler.call(this.scope || this, this, e);
7110             }
7111         }
7112     },
7113     // private
7114     onMouseOver : function(e){
7115         if(!this.disabled){
7116             this.el.addClass("x-btn-over");
7117             this.fireEvent('mouseover', this, e);
7118         }
7119     },
7120     // private
7121     onMouseOut : function(e){
7122         if(!e.within(this.el,  true)){
7123             this.el.removeClass("x-btn-over");
7124             this.fireEvent('mouseout', this, e);
7125         }
7126     },
7127     // private
7128     onFocus : function(e){
7129         if(!this.disabled){
7130             this.el.addClass("x-btn-focus");
7131         }
7132     },
7133     // private
7134     onBlur : function(e){
7135         this.el.removeClass("x-btn-focus");
7136     },
7137     // private
7138     onMouseDown : function(e){
7139         if(!this.disabled && e.button == 0){
7140             this.el.addClass("x-btn-click");
7141             Roo.get(document).on('mouseup', this.onMouseUp, this);
7142         }
7143     },
7144     // private
7145     onMouseUp : function(e){
7146         if(e.button == 0){
7147             this.el.removeClass("x-btn-click");
7148             Roo.get(document).un('mouseup', this.onMouseUp, this);
7149         }
7150     },
7151     // private
7152     onMenuShow : function(e){
7153         this.el.addClass("x-btn-menu-active");
7154     },
7155     // private
7156     onMenuHide : function(e){
7157         this.el.removeClass("x-btn-menu-active");
7158     }   
7159 });
7160
7161 // Private utility class used by Button
7162 Roo.ButtonToggleMgr = function(){
7163    var groups = {};
7164    
7165    function toggleGroup(btn, state){
7166        if(state){
7167            var g = groups[btn.toggleGroup];
7168            for(var i = 0, l = g.length; i < l; i++){
7169                if(g[i] != btn){
7170                    g[i].toggle(false);
7171                }
7172            }
7173        }
7174    }
7175    
7176    return {
7177        register : function(btn){
7178            if(!btn.toggleGroup){
7179                return;
7180            }
7181            var g = groups[btn.toggleGroup];
7182            if(!g){
7183                g = groups[btn.toggleGroup] = [];
7184            }
7185            g.push(btn);
7186            btn.on("toggle", toggleGroup);
7187        },
7188        
7189        unregister : function(btn){
7190            if(!btn.toggleGroup){
7191                return;
7192            }
7193            var g = groups[btn.toggleGroup];
7194            if(g){
7195                g.remove(btn);
7196                btn.un("toggle", toggleGroup);
7197            }
7198        }
7199    };
7200 }();/*
7201  * Based on:
7202  * Ext JS Library 1.1.1
7203  * Copyright(c) 2006-2007, Ext JS, LLC.
7204  *
7205  * Originally Released Under LGPL - original licence link has changed is not relivant.
7206  *
7207  * Fork - LGPL
7208  * <script type="text/javascript">
7209  */
7210  
7211 /**
7212  * @class Roo.SplitButton
7213  * @extends Roo.Button
7214  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7215  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7216  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7217  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7218  * @cfg {String} arrowTooltip The title attribute of the arrow
7219  * @constructor
7220  * Create a new menu button
7221  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7222  * @param {Object} config The config object
7223  */
7224 Roo.SplitButton = function(renderTo, config){
7225     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7226     /**
7227      * @event arrowclick
7228      * Fires when this button's arrow is clicked
7229      * @param {SplitButton} this
7230      * @param {EventObject} e The click event
7231      */
7232     this.addEvents({"arrowclick":true});
7233 };
7234
7235 Roo.extend(Roo.SplitButton, Roo.Button, {
7236     render : function(renderTo){
7237         // this is one sweet looking template!
7238         var tpl = new Roo.Template(
7239             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7240             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7241             '<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>',
7242             "</tbody></table></td><td>",
7243             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7244             '<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>',
7245             "</tbody></table></td></tr></table>"
7246         );
7247         var btn = tpl.append(renderTo, [this.text, this.type], true);
7248         var btnEl = btn.child("button");
7249         if(this.cls){
7250             btn.addClass(this.cls);
7251         }
7252         if(this.icon){
7253             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7254         }
7255         if(this.iconCls){
7256             btnEl.addClass(this.iconCls);
7257             if(!this.cls){
7258                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7259             }
7260         }
7261         this.el = btn;
7262         if(this.handleMouseEvents){
7263             btn.on("mouseover", this.onMouseOver, this);
7264             btn.on("mouseout", this.onMouseOut, this);
7265             btn.on("mousedown", this.onMouseDown, this);
7266             btn.on("mouseup", this.onMouseUp, this);
7267         }
7268         btn.on(this.clickEvent, this.onClick, this);
7269         if(this.tooltip){
7270             if(typeof this.tooltip == 'object'){
7271                 Roo.QuickTips.tips(Roo.apply({
7272                       target: btnEl.id
7273                 }, this.tooltip));
7274             } else {
7275                 btnEl.dom[this.tooltipType] = this.tooltip;
7276             }
7277         }
7278         if(this.arrowTooltip){
7279             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7280         }
7281         if(this.hidden){
7282             this.hide();
7283         }
7284         if(this.disabled){
7285             this.disable();
7286         }
7287         if(this.pressed){
7288             this.el.addClass("x-btn-pressed");
7289         }
7290         if(Roo.isIE && !Roo.isIE7){
7291             this.autoWidth.defer(1, this);
7292         }else{
7293             this.autoWidth();
7294         }
7295         if(this.menu){
7296             this.menu.on("show", this.onMenuShow, this);
7297             this.menu.on("hide", this.onMenuHide, this);
7298         }
7299         this.fireEvent('render', this);
7300     },
7301
7302     // private
7303     autoWidth : function(){
7304         if(this.el){
7305             var tbl = this.el.child("table:first");
7306             var tbl2 = this.el.child("table:last");
7307             this.el.setWidth("auto");
7308             tbl.setWidth("auto");
7309             if(Roo.isIE7 && Roo.isStrict){
7310                 var ib = this.el.child('button:first');
7311                 if(ib && ib.getWidth() > 20){
7312                     ib.clip();
7313                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7314                 }
7315             }
7316             if(this.minWidth){
7317                 if(this.hidden){
7318                     this.el.beginMeasure();
7319                 }
7320                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7321                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7322                 }
7323                 if(this.hidden){
7324                     this.el.endMeasure();
7325                 }
7326             }
7327             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7328         } 
7329     },
7330     /**
7331      * Sets this button's click handler
7332      * @param {Function} handler The function to call when the button is clicked
7333      * @param {Object} scope (optional) Scope for the function passed above
7334      */
7335     setHandler : function(handler, scope){
7336         this.handler = handler;
7337         this.scope = scope;  
7338     },
7339     
7340     /**
7341      * Sets this button's arrow click handler
7342      * @param {Function} handler The function to call when the arrow is clicked
7343      * @param {Object} scope (optional) Scope for the function passed above
7344      */
7345     setArrowHandler : function(handler, scope){
7346         this.arrowHandler = handler;
7347         this.scope = scope;  
7348     },
7349     
7350     /**
7351      * Focus the button
7352      */
7353     focus : function(){
7354         if(this.el){
7355             this.el.child("button:first").focus();
7356         }
7357     },
7358
7359     // private
7360     onClick : function(e){
7361         e.preventDefault();
7362         if(!this.disabled){
7363             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7364                 if(this.menu && !this.menu.isVisible()){
7365                     this.menu.show(this.el, this.menuAlign);
7366                 }
7367                 this.fireEvent("arrowclick", this, e);
7368                 if(this.arrowHandler){
7369                     this.arrowHandler.call(this.scope || this, this, e);
7370                 }
7371             }else{
7372                 this.fireEvent("click", this, e);
7373                 if(this.handler){
7374                     this.handler.call(this.scope || this, this, e);
7375                 }
7376             }
7377         }
7378     },
7379     // private
7380     onMouseDown : function(e){
7381         if(!this.disabled){
7382             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7383         }
7384     },
7385     // private
7386     onMouseUp : function(e){
7387         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7388     }   
7389 });
7390
7391
7392 // backwards compat
7393 Roo.MenuButton = Roo.SplitButton;/*
7394  * Based on:
7395  * Ext JS Library 1.1.1
7396  * Copyright(c) 2006-2007, Ext JS, LLC.
7397  *
7398  * Originally Released Under LGPL - original licence link has changed is not relivant.
7399  *
7400  * Fork - LGPL
7401  * <script type="text/javascript">
7402  */
7403
7404 /**
7405  * @class Roo.Toolbar
7406  * Basic Toolbar class.
7407  * @constructor
7408  * Creates a new Toolbar
7409  * @param {Object} container The config object
7410  */ 
7411 Roo.Toolbar = function(container, buttons, config)
7412 {
7413     /// old consturctor format still supported..
7414     if(container instanceof Array){ // omit the container for later rendering
7415         buttons = container;
7416         config = buttons;
7417         container = null;
7418     }
7419     if (typeof(container) == 'object' && container.xtype) {
7420         config = container;
7421         container = config.container;
7422         buttons = config.buttons || []; // not really - use items!!
7423     }
7424     var xitems = [];
7425     if (config && config.items) {
7426         xitems = config.items;
7427         delete config.items;
7428     }
7429     Roo.apply(this, config);
7430     this.buttons = buttons;
7431     
7432     if(container){
7433         this.render(container);
7434     }
7435     this.xitems = xitems;
7436     Roo.each(xitems, function(b) {
7437         this.add(b);
7438     }, this);
7439     
7440 };
7441
7442 Roo.Toolbar.prototype = {
7443     /**
7444      * @cfg {Array} items
7445      * array of button configs or elements to add (will be converted to a MixedCollection)
7446      */
7447     
7448     /**
7449      * @cfg {String/HTMLElement/Element} container
7450      * The id or element that will contain the toolbar
7451      */
7452     // private
7453     render : function(ct){
7454         this.el = Roo.get(ct);
7455         if(this.cls){
7456             this.el.addClass(this.cls);
7457         }
7458         // using a table allows for vertical alignment
7459         // 100% width is needed by Safari...
7460         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7461         this.tr = this.el.child("tr", true);
7462         var autoId = 0;
7463         this.items = new Roo.util.MixedCollection(false, function(o){
7464             return o.id || ("item" + (++autoId));
7465         });
7466         if(this.buttons){
7467             this.add.apply(this, this.buttons);
7468             delete this.buttons;
7469         }
7470     },
7471
7472     /**
7473      * Adds element(s) to the toolbar -- this function takes a variable number of 
7474      * arguments of mixed type and adds them to the toolbar.
7475      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7476      * <ul>
7477      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7478      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7479      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7480      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7481      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7482      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7483      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7484      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7485      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7486      * </ul>
7487      * @param {Mixed} arg2
7488      * @param {Mixed} etc.
7489      */
7490     add : function(){
7491         var a = arguments, l = a.length;
7492         for(var i = 0; i < l; i++){
7493             this._add(a[i]);
7494         }
7495     },
7496     // private..
7497     _add : function(el) {
7498         
7499         if (el.xtype) {
7500             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7501         }
7502         
7503         if (el.applyTo){ // some kind of form field
7504             return this.addField(el);
7505         } 
7506         if (el.render){ // some kind of Toolbar.Item
7507             return this.addItem(el);
7508         }
7509         if (typeof el == "string"){ // string
7510             if(el == "separator" || el == "-"){
7511                 return this.addSeparator();
7512             }
7513             if (el == " "){
7514                 return this.addSpacer();
7515             }
7516             if(el == "->"){
7517                 return this.addFill();
7518             }
7519             return this.addText(el);
7520             
7521         }
7522         if(el.tagName){ // element
7523             return this.addElement(el);
7524         }
7525         if(typeof el == "object"){ // must be button config?
7526             return this.addButton(el);
7527         }
7528         // and now what?!?!
7529         return false;
7530         
7531     },
7532     
7533     /**
7534      * Add an Xtype element
7535      * @param {Object} xtype Xtype Object
7536      * @return {Object} created Object
7537      */
7538     addxtype : function(e){
7539         return this.add(e);  
7540     },
7541     
7542     /**
7543      * Returns the Element for this toolbar.
7544      * @return {Roo.Element}
7545      */
7546     getEl : function(){
7547         return this.el;  
7548     },
7549     
7550     /**
7551      * Adds a separator
7552      * @return {Roo.Toolbar.Item} The separator item
7553      */
7554     addSeparator : function(){
7555         return this.addItem(new Roo.Toolbar.Separator());
7556     },
7557
7558     /**
7559      * Adds a spacer element
7560      * @return {Roo.Toolbar.Spacer} The spacer item
7561      */
7562     addSpacer : function(){
7563         return this.addItem(new Roo.Toolbar.Spacer());
7564     },
7565
7566     /**
7567      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7568      * @return {Roo.Toolbar.Fill} The fill item
7569      */
7570     addFill : function(){
7571         return this.addItem(new Roo.Toolbar.Fill());
7572     },
7573
7574     /**
7575      * Adds any standard HTML element to the toolbar
7576      * @param {String/HTMLElement/Element} el The element or id of the element to add
7577      * @return {Roo.Toolbar.Item} The element's item
7578      */
7579     addElement : function(el){
7580         return this.addItem(new Roo.Toolbar.Item(el));
7581     },
7582     /**
7583      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7584      * @type Roo.util.MixedCollection  
7585      */
7586     items : false,
7587      
7588     /**
7589      * Adds any Toolbar.Item or subclass
7590      * @param {Roo.Toolbar.Item} item
7591      * @return {Roo.Toolbar.Item} The item
7592      */
7593     addItem : function(item){
7594         var td = this.nextBlock();
7595         item.render(td);
7596         this.items.add(item);
7597         return item;
7598     },
7599     
7600     /**
7601      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7602      * @param {Object/Array} config A button config or array of configs
7603      * @return {Roo.Toolbar.Button/Array}
7604      */
7605     addButton : function(config){
7606         if(config instanceof Array){
7607             var buttons = [];
7608             for(var i = 0, len = config.length; i < len; i++) {
7609                 buttons.push(this.addButton(config[i]));
7610             }
7611             return buttons;
7612         }
7613         var b = config;
7614         if(!(config instanceof Roo.Toolbar.Button)){
7615             b = config.split ?
7616                 new Roo.Toolbar.SplitButton(config) :
7617                 new Roo.Toolbar.Button(config);
7618         }
7619         var td = this.nextBlock();
7620         b.render(td);
7621         this.items.add(b);
7622         return b;
7623     },
7624     
7625     /**
7626      * Adds text to the toolbar
7627      * @param {String} text The text to add
7628      * @return {Roo.Toolbar.Item} The element's item
7629      */
7630     addText : function(text){
7631         return this.addItem(new Roo.Toolbar.TextItem(text));
7632     },
7633     
7634     /**
7635      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7636      * @param {Number} index The index where the item is to be inserted
7637      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7638      * @return {Roo.Toolbar.Button/Item}
7639      */
7640     insertButton : function(index, item){
7641         if(item instanceof Array){
7642             var buttons = [];
7643             for(var i = 0, len = item.length; i < len; i++) {
7644                buttons.push(this.insertButton(index + i, item[i]));
7645             }
7646             return buttons;
7647         }
7648         if (!(item instanceof Roo.Toolbar.Button)){
7649            item = new Roo.Toolbar.Button(item);
7650         }
7651         var td = document.createElement("td");
7652         this.tr.insertBefore(td, this.tr.childNodes[index]);
7653         item.render(td);
7654         this.items.insert(index, item);
7655         return item;
7656     },
7657     
7658     /**
7659      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7660      * @param {Object} config
7661      * @return {Roo.Toolbar.Item} The element's item
7662      */
7663     addDom : function(config, returnEl){
7664         var td = this.nextBlock();
7665         Roo.DomHelper.overwrite(td, config);
7666         var ti = new Roo.Toolbar.Item(td.firstChild);
7667         ti.render(td);
7668         this.items.add(ti);
7669         return ti;
7670     },
7671
7672     /**
7673      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7674      * @type Roo.util.MixedCollection  
7675      */
7676     fields : false,
7677     
7678     /**
7679      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7680      * Note: the field should not have been rendered yet. For a field that has already been
7681      * rendered, use {@link #addElement}.
7682      * @param {Roo.form.Field} field
7683      * @return {Roo.ToolbarItem}
7684      */
7685      
7686       
7687     addField : function(field) {
7688         if (!this.fields) {
7689             var autoId = 0;
7690             this.fields = new Roo.util.MixedCollection(false, function(o){
7691                 return o.id || ("item" + (++autoId));
7692             });
7693
7694         }
7695         
7696         var td = this.nextBlock();
7697         field.render(td);
7698         var ti = new Roo.Toolbar.Item(td.firstChild);
7699         ti.render(td);
7700         this.items.add(ti);
7701         this.fields.add(field);
7702         return ti;
7703     },
7704     /**
7705      * Hide the toolbar
7706      * @method hide
7707      */
7708      
7709       
7710     hide : function()
7711     {
7712         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7713         this.el.child('div').hide();
7714     },
7715     /**
7716      * Show the toolbar
7717      * @method show
7718      */
7719     show : function()
7720     {
7721         this.el.child('div').show();
7722     },
7723       
7724     // private
7725     nextBlock : function(){
7726         var td = document.createElement("td");
7727         this.tr.appendChild(td);
7728         return td;
7729     },
7730
7731     // private
7732     destroy : function(){
7733         if(this.items){ // rendered?
7734             Roo.destroy.apply(Roo, this.items.items);
7735         }
7736         if(this.fields){ // rendered?
7737             Roo.destroy.apply(Roo, this.fields.items);
7738         }
7739         Roo.Element.uncache(this.el, this.tr);
7740     }
7741 };
7742
7743 /**
7744  * @class Roo.Toolbar.Item
7745  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7746  * @constructor
7747  * Creates a new Item
7748  * @param {HTMLElement} el 
7749  */
7750 Roo.Toolbar.Item = function(el){
7751     var cfg = {};
7752     if (typeof (el.xtype) != 'undefined') {
7753         cfg = el;
7754         el = cfg.el;
7755     }
7756     
7757     this.el = Roo.getDom(el);
7758     this.id = Roo.id(this.el);
7759     this.hidden = false;
7760     
7761     this.addEvents({
7762          /**
7763              * @event render
7764              * Fires when the button is rendered
7765              * @param {Button} this
7766              */
7767         'render': true
7768     });
7769     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7770 };
7771 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7772 //Roo.Toolbar.Item.prototype = {
7773     
7774     /**
7775      * Get this item's HTML Element
7776      * @return {HTMLElement}
7777      */
7778     getEl : function(){
7779        return this.el;  
7780     },
7781
7782     // private
7783     render : function(td){
7784         
7785          this.td = td;
7786         td.appendChild(this.el);
7787         
7788         this.fireEvent('render', this);
7789     },
7790     
7791     /**
7792      * Removes and destroys this item.
7793      */
7794     destroy : function(){
7795         this.td.parentNode.removeChild(this.td);
7796     },
7797     
7798     /**
7799      * Shows this item.
7800      */
7801     show: function(){
7802         this.hidden = false;
7803         this.td.style.display = "";
7804     },
7805     
7806     /**
7807      * Hides this item.
7808      */
7809     hide: function(){
7810         this.hidden = true;
7811         this.td.style.display = "none";
7812     },
7813     
7814     /**
7815      * Convenience function for boolean show/hide.
7816      * @param {Boolean} visible true to show/false to hide
7817      */
7818     setVisible: function(visible){
7819         if(visible) {
7820             this.show();
7821         }else{
7822             this.hide();
7823         }
7824     },
7825     
7826     /**
7827      * Try to focus this item.
7828      */
7829     focus : function(){
7830         Roo.fly(this.el).focus();
7831     },
7832     
7833     /**
7834      * Disables this item.
7835      */
7836     disable : function(){
7837         Roo.fly(this.td).addClass("x-item-disabled");
7838         this.disabled = true;
7839         this.el.disabled = true;
7840     },
7841     
7842     /**
7843      * Enables this item.
7844      */
7845     enable : function(){
7846         Roo.fly(this.td).removeClass("x-item-disabled");
7847         this.disabled = false;
7848         this.el.disabled = false;
7849     }
7850 });
7851
7852
7853 /**
7854  * @class Roo.Toolbar.Separator
7855  * @extends Roo.Toolbar.Item
7856  * A simple toolbar separator class
7857  * @constructor
7858  * Creates a new Separator
7859  */
7860 Roo.Toolbar.Separator = function(cfg){
7861     
7862     var s = document.createElement("span");
7863     s.className = "ytb-sep";
7864     if (cfg) {
7865         cfg.el = s;
7866     }
7867     
7868     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7869 };
7870 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7871     enable:Roo.emptyFn,
7872     disable:Roo.emptyFn,
7873     focus:Roo.emptyFn
7874 });
7875
7876 /**
7877  * @class Roo.Toolbar.Spacer
7878  * @extends Roo.Toolbar.Item
7879  * A simple element that adds extra horizontal space to a toolbar.
7880  * @constructor
7881  * Creates a new Spacer
7882  */
7883 Roo.Toolbar.Spacer = function(cfg){
7884     var s = document.createElement("div");
7885     s.className = "ytb-spacer";
7886     if (cfg) {
7887         cfg.el = s;
7888     }
7889     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7890 };
7891 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7892     enable:Roo.emptyFn,
7893     disable:Roo.emptyFn,
7894     focus:Roo.emptyFn
7895 });
7896
7897 /**
7898  * @class Roo.Toolbar.Fill
7899  * @extends Roo.Toolbar.Spacer
7900  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7901  * @constructor
7902  * Creates a new Spacer
7903  */
7904 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7905     // private
7906     render : function(td){
7907         td.style.width = '100%';
7908         Roo.Toolbar.Fill.superclass.render.call(this, td);
7909     }
7910 });
7911
7912 /**
7913  * @class Roo.Toolbar.TextItem
7914  * @extends Roo.Toolbar.Item
7915  * A simple class that renders text directly into a toolbar.
7916  * @constructor
7917  * Creates a new TextItem
7918  * @param {String} text
7919  */
7920 Roo.Toolbar.TextItem = function(cfg){
7921     var  text = cfg || "";
7922     if (typeof(cfg) == 'object') {
7923         text = cfg.text || "";
7924     }  else {
7925         cfg = null;
7926     }
7927     var s = document.createElement("span");
7928     s.className = "ytb-text";
7929     s.innerHTML = text;
7930     if (cfg) {
7931         cfg.el  = s;
7932     }
7933     
7934     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7935 };
7936 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7937     
7938      
7939     enable:Roo.emptyFn,
7940     disable:Roo.emptyFn,
7941     focus:Roo.emptyFn
7942 });
7943
7944 /**
7945  * @class Roo.Toolbar.Button
7946  * @extends Roo.Button
7947  * A button that renders into a toolbar.
7948  * @constructor
7949  * Creates a new Button
7950  * @param {Object} config A standard {@link Roo.Button} config object
7951  */
7952 Roo.Toolbar.Button = function(config){
7953     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7954 };
7955 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7956     render : function(td){
7957         this.td = td;
7958         Roo.Toolbar.Button.superclass.render.call(this, td);
7959     },
7960     
7961     /**
7962      * Removes and destroys this button
7963      */
7964     destroy : function(){
7965         Roo.Toolbar.Button.superclass.destroy.call(this);
7966         this.td.parentNode.removeChild(this.td);
7967     },
7968     
7969     /**
7970      * Shows this button
7971      */
7972     show: function(){
7973         this.hidden = false;
7974         this.td.style.display = "";
7975     },
7976     
7977     /**
7978      * Hides this button
7979      */
7980     hide: function(){
7981         this.hidden = true;
7982         this.td.style.display = "none";
7983     },
7984
7985     /**
7986      * Disables this item
7987      */
7988     disable : function(){
7989         Roo.fly(this.td).addClass("x-item-disabled");
7990         this.disabled = true;
7991     },
7992
7993     /**
7994      * Enables this item
7995      */
7996     enable : function(){
7997         Roo.fly(this.td).removeClass("x-item-disabled");
7998         this.disabled = false;
7999     }
8000 });
8001 // backwards compat
8002 Roo.ToolbarButton = Roo.Toolbar.Button;
8003
8004 /**
8005  * @class Roo.Toolbar.SplitButton
8006  * @extends Roo.SplitButton
8007  * A menu button that renders into a toolbar.
8008  * @constructor
8009  * Creates a new SplitButton
8010  * @param {Object} config A standard {@link Roo.SplitButton} config object
8011  */
8012 Roo.Toolbar.SplitButton = function(config){
8013     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8014 };
8015 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8016     render : function(td){
8017         this.td = td;
8018         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8019     },
8020     
8021     /**
8022      * Removes and destroys this button
8023      */
8024     destroy : function(){
8025         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8026         this.td.parentNode.removeChild(this.td);
8027     },
8028     
8029     /**
8030      * Shows this button
8031      */
8032     show: function(){
8033         this.hidden = false;
8034         this.td.style.display = "";
8035     },
8036     
8037     /**
8038      * Hides this button
8039      */
8040     hide: function(){
8041         this.hidden = true;
8042         this.td.style.display = "none";
8043     }
8044 });
8045
8046 // backwards compat
8047 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8048  * Based on:
8049  * Ext JS Library 1.1.1
8050  * Copyright(c) 2006-2007, Ext JS, LLC.
8051  *
8052  * Originally Released Under LGPL - original licence link has changed is not relivant.
8053  *
8054  * Fork - LGPL
8055  * <script type="text/javascript">
8056  */
8057  
8058 /**
8059  * @class Roo.PagingToolbar
8060  * @extends Roo.Toolbar
8061  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8062  * @constructor
8063  * Create a new PagingToolbar
8064  * @param {Object} config The config object
8065  */
8066 Roo.PagingToolbar = function(el, ds, config)
8067 {
8068     // old args format still supported... - xtype is prefered..
8069     if (typeof(el) == 'object' && el.xtype) {
8070         // created from xtype...
8071         config = el;
8072         ds = el.dataSource;
8073         el = config.container;
8074     }
8075     var items = [];
8076     if (config.items) {
8077         items = config.items;
8078         config.items = [];
8079     }
8080     
8081     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8082     this.ds = ds;
8083     this.cursor = 0;
8084     this.renderButtons(this.el);
8085     this.bind(ds);
8086     
8087     // supprot items array.
8088    
8089     Roo.each(items, function(e) {
8090         this.add(Roo.factory(e));
8091     },this);
8092     
8093 };
8094
8095 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8096     /**
8097      * @cfg {Roo.data.Store} dataSource
8098      * The underlying data store providing the paged data
8099      */
8100     /**
8101      * @cfg {String/HTMLElement/Element} container
8102      * container The id or element that will contain the toolbar
8103      */
8104     /**
8105      * @cfg {Boolean} displayInfo
8106      * True to display the displayMsg (defaults to false)
8107      */
8108     /**
8109      * @cfg {Number} pageSize
8110      * The number of records to display per page (defaults to 20)
8111      */
8112     pageSize: 20,
8113     /**
8114      * @cfg {String} displayMsg
8115      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8116      */
8117     displayMsg : 'Displaying {0} - {1} of {2}',
8118     /**
8119      * @cfg {String} emptyMsg
8120      * The message to display when no records are found (defaults to "No data to display")
8121      */
8122     emptyMsg : 'No data to display',
8123     /**
8124      * Customizable piece of the default paging text (defaults to "Page")
8125      * @type String
8126      */
8127     beforePageText : "Page",
8128     /**
8129      * Customizable piece of the default paging text (defaults to "of %0")
8130      * @type String
8131      */
8132     afterPageText : "of {0}",
8133     /**
8134      * Customizable piece of the default paging text (defaults to "First Page")
8135      * @type String
8136      */
8137     firstText : "First Page",
8138     /**
8139      * Customizable piece of the default paging text (defaults to "Previous Page")
8140      * @type String
8141      */
8142     prevText : "Previous Page",
8143     /**
8144      * Customizable piece of the default paging text (defaults to "Next Page")
8145      * @type String
8146      */
8147     nextText : "Next Page",
8148     /**
8149      * Customizable piece of the default paging text (defaults to "Last Page")
8150      * @type String
8151      */
8152     lastText : "Last Page",
8153     /**
8154      * Customizable piece of the default paging text (defaults to "Refresh")
8155      * @type String
8156      */
8157     refreshText : "Refresh",
8158
8159     // private
8160     renderButtons : function(el){
8161         Roo.PagingToolbar.superclass.render.call(this, el);
8162         this.first = this.addButton({
8163             tooltip: this.firstText,
8164             cls: "x-btn-icon x-grid-page-first",
8165             disabled: true,
8166             handler: this.onClick.createDelegate(this, ["first"])
8167         });
8168         this.prev = this.addButton({
8169             tooltip: this.prevText,
8170             cls: "x-btn-icon x-grid-page-prev",
8171             disabled: true,
8172             handler: this.onClick.createDelegate(this, ["prev"])
8173         });
8174         //this.addSeparator();
8175         this.add(this.beforePageText);
8176         this.field = Roo.get(this.addDom({
8177            tag: "input",
8178            type: "text",
8179            size: "3",
8180            value: "1",
8181            cls: "x-grid-page-number"
8182         }).el);
8183         this.field.on("keydown", this.onPagingKeydown, this);
8184         this.field.on("focus", function(){this.dom.select();});
8185         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8186         this.field.setHeight(18);
8187         //this.addSeparator();
8188         this.next = this.addButton({
8189             tooltip: this.nextText,
8190             cls: "x-btn-icon x-grid-page-next",
8191             disabled: true,
8192             handler: this.onClick.createDelegate(this, ["next"])
8193         });
8194         this.last = this.addButton({
8195             tooltip: this.lastText,
8196             cls: "x-btn-icon x-grid-page-last",
8197             disabled: true,
8198             handler: this.onClick.createDelegate(this, ["last"])
8199         });
8200         //this.addSeparator();
8201         this.loading = this.addButton({
8202             tooltip: this.refreshText,
8203             cls: "x-btn-icon x-grid-loading",
8204             handler: this.onClick.createDelegate(this, ["refresh"])
8205         });
8206
8207         if(this.displayInfo){
8208             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8209         }
8210     },
8211
8212     // private
8213     updateInfo : function(){
8214         if(this.displayEl){
8215             var count = this.ds.getCount();
8216             var msg = count == 0 ?
8217                 this.emptyMsg :
8218                 String.format(
8219                     this.displayMsg,
8220                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8221                 );
8222             this.displayEl.update(msg);
8223         }
8224     },
8225
8226     // private
8227     onLoad : function(ds, r, o){
8228        this.cursor = o.params ? o.params.start : 0;
8229        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8230
8231        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8232        this.field.dom.value = ap;
8233        this.first.setDisabled(ap == 1);
8234        this.prev.setDisabled(ap == 1);
8235        this.next.setDisabled(ap == ps);
8236        this.last.setDisabled(ap == ps);
8237        this.loading.enable();
8238        this.updateInfo();
8239     },
8240
8241     // private
8242     getPageData : function(){
8243         var total = this.ds.getTotalCount();
8244         return {
8245             total : total,
8246             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8247             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8248         };
8249     },
8250
8251     // private
8252     onLoadError : function(){
8253         this.loading.enable();
8254     },
8255
8256     // private
8257     onPagingKeydown : function(e){
8258         var k = e.getKey();
8259         var d = this.getPageData();
8260         if(k == e.RETURN){
8261             var v = this.field.dom.value, pageNum;
8262             if(!v || isNaN(pageNum = parseInt(v, 10))){
8263                 this.field.dom.value = d.activePage;
8264                 return;
8265             }
8266             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8267             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8268             e.stopEvent();
8269         }
8270         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))
8271         {
8272           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8273           this.field.dom.value = pageNum;
8274           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8275           e.stopEvent();
8276         }
8277         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8278         {
8279           var v = this.field.dom.value, pageNum; 
8280           var increment = (e.shiftKey) ? 10 : 1;
8281           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8282             increment *= -1;
8283           }
8284           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8285             this.field.dom.value = d.activePage;
8286             return;
8287           }
8288           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8289           {
8290             this.field.dom.value = parseInt(v, 10) + increment;
8291             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8292             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8293           }
8294           e.stopEvent();
8295         }
8296     },
8297
8298     // private
8299     beforeLoad : function(){
8300         if(this.loading){
8301             this.loading.disable();
8302         }
8303     },
8304
8305     // private
8306     onClick : function(which){
8307         var ds = this.ds;
8308         switch(which){
8309             case "first":
8310                 ds.load({params:{start: 0, limit: this.pageSize}});
8311             break;
8312             case "prev":
8313                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8314             break;
8315             case "next":
8316                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8317             break;
8318             case "last":
8319                 var total = ds.getTotalCount();
8320                 var extra = total % this.pageSize;
8321                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8322                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8323             break;
8324             case "refresh":
8325                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8326             break;
8327         }
8328     },
8329
8330     /**
8331      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8332      * @param {Roo.data.Store} store The data store to unbind
8333      */
8334     unbind : function(ds){
8335         ds.un("beforeload", this.beforeLoad, this);
8336         ds.un("load", this.onLoad, this);
8337         ds.un("loadexception", this.onLoadError, this);
8338         ds.un("remove", this.updateInfo, this);
8339         ds.un("add", this.updateInfo, this);
8340         this.ds = undefined;
8341     },
8342
8343     /**
8344      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8345      * @param {Roo.data.Store} store The data store to bind
8346      */
8347     bind : function(ds){
8348         ds.on("beforeload", this.beforeLoad, this);
8349         ds.on("load", this.onLoad, this);
8350         ds.on("loadexception", this.onLoadError, this);
8351         ds.on("remove", this.updateInfo, this);
8352         ds.on("add", this.updateInfo, this);
8353         this.ds = ds;
8354     }
8355 });/*
8356  * Based on:
8357  * Ext JS Library 1.1.1
8358  * Copyright(c) 2006-2007, Ext JS, LLC.
8359  *
8360  * Originally Released Under LGPL - original licence link has changed is not relivant.
8361  *
8362  * Fork - LGPL
8363  * <script type="text/javascript">
8364  */
8365
8366 /**
8367  * @class Roo.Resizable
8368  * @extends Roo.util.Observable
8369  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8370  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8371  * 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
8372  * the element will be wrapped for you automatically.</p>
8373  * <p>Here is the list of valid resize handles:</p>
8374  * <pre>
8375 Value   Description
8376 ------  -------------------
8377  'n'     north
8378  's'     south
8379  'e'     east
8380  'w'     west
8381  'nw'    northwest
8382  'sw'    southwest
8383  'se'    southeast
8384  'ne'    northeast
8385  'hd'    horizontal drag
8386  'all'   all
8387 </pre>
8388  * <p>Here's an example showing the creation of a typical Resizable:</p>
8389  * <pre><code>
8390 var resizer = new Roo.Resizable("element-id", {
8391     handles: 'all',
8392     minWidth: 200,
8393     minHeight: 100,
8394     maxWidth: 500,
8395     maxHeight: 400,
8396     pinned: true
8397 });
8398 resizer.on("resize", myHandler);
8399 </code></pre>
8400  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8401  * resizer.east.setDisplayed(false);</p>
8402  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8403  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8404  * resize operation's new size (defaults to [0, 0])
8405  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8406  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8407  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8408  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8409  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8410  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8411  * @cfg {Number} width The width of the element in pixels (defaults to null)
8412  * @cfg {Number} height The height of the element in pixels (defaults to null)
8413  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8414  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8415  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8416  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8417  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8418  * in favor of the handles config option (defaults to false)
8419  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8420  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8421  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8422  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8423  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8424  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8425  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8426  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8427  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8428  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8429  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8430  * @constructor
8431  * Create a new resizable component
8432  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8433  * @param {Object} config configuration options
8434   */
8435 Roo.Resizable = function(el, config)
8436 {
8437     this.el = Roo.get(el);
8438
8439     if(config && config.wrap){
8440         config.resizeChild = this.el;
8441         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8442         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8443         this.el.setStyle("overflow", "hidden");
8444         this.el.setPositioning(config.resizeChild.getPositioning());
8445         config.resizeChild.clearPositioning();
8446         if(!config.width || !config.height){
8447             var csize = config.resizeChild.getSize();
8448             this.el.setSize(csize.width, csize.height);
8449         }
8450         if(config.pinned && !config.adjustments){
8451             config.adjustments = "auto";
8452         }
8453     }
8454
8455     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8456     this.proxy.unselectable();
8457     this.proxy.enableDisplayMode('block');
8458
8459     Roo.apply(this, config);
8460
8461     if(this.pinned){
8462         this.disableTrackOver = true;
8463         this.el.addClass("x-resizable-pinned");
8464     }
8465     // if the element isn't positioned, make it relative
8466     var position = this.el.getStyle("position");
8467     if(position != "absolute" && position != "fixed"){
8468         this.el.setStyle("position", "relative");
8469     }
8470     if(!this.handles){ // no handles passed, must be legacy style
8471         this.handles = 's,e,se';
8472         if(this.multiDirectional){
8473             this.handles += ',n,w';
8474         }
8475     }
8476     if(this.handles == "all"){
8477         this.handles = "n s e w ne nw se sw";
8478     }
8479     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8480     var ps = Roo.Resizable.positions;
8481     for(var i = 0, len = hs.length; i < len; i++){
8482         if(hs[i] && ps[hs[i]]){
8483             var pos = ps[hs[i]];
8484             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8485         }
8486     }
8487     // legacy
8488     this.corner = this.southeast;
8489     
8490     // updateBox = the box can move..
8491     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8492         this.updateBox = true;
8493     }
8494
8495     this.activeHandle = null;
8496
8497     if(this.resizeChild){
8498         if(typeof this.resizeChild == "boolean"){
8499             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8500         }else{
8501             this.resizeChild = Roo.get(this.resizeChild, true);
8502         }
8503     }
8504     
8505     if(this.adjustments == "auto"){
8506         var rc = this.resizeChild;
8507         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8508         if(rc && (hw || hn)){
8509             rc.position("relative");
8510             rc.setLeft(hw ? hw.el.getWidth() : 0);
8511             rc.setTop(hn ? hn.el.getHeight() : 0);
8512         }
8513         this.adjustments = [
8514             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8515             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8516         ];
8517     }
8518
8519     if(this.draggable){
8520         this.dd = this.dynamic ?
8521             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8522         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8523     }
8524
8525     // public events
8526     this.addEvents({
8527         /**
8528          * @event beforeresize
8529          * Fired before resize is allowed. Set enabled to false to cancel resize.
8530          * @param {Roo.Resizable} this
8531          * @param {Roo.EventObject} e The mousedown event
8532          */
8533         "beforeresize" : true,
8534         /**
8535          * @event resizing
8536          * Fired a resizing.
8537          * @param {Roo.Resizable} this
8538          * @param {Number} x The new x position
8539          * @param {Number} y The new y position
8540          * @param {Number} w The new w width
8541          * @param {Number} h The new h hight
8542          * @param {Roo.EventObject} e The mouseup event
8543          */
8544         "resizing" : true,
8545         /**
8546          * @event resize
8547          * Fired after a resize.
8548          * @param {Roo.Resizable} this
8549          * @param {Number} width The new width
8550          * @param {Number} height The new height
8551          * @param {Roo.EventObject} e The mouseup event
8552          */
8553         "resize" : true
8554     });
8555
8556     if(this.width !== null && this.height !== null){
8557         this.resizeTo(this.width, this.height);
8558     }else{
8559         this.updateChildSize();
8560     }
8561     if(Roo.isIE){
8562         this.el.dom.style.zoom = 1;
8563     }
8564     Roo.Resizable.superclass.constructor.call(this);
8565 };
8566
8567 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8568         resizeChild : false,
8569         adjustments : [0, 0],
8570         minWidth : 5,
8571         minHeight : 5,
8572         maxWidth : 10000,
8573         maxHeight : 10000,
8574         enabled : true,
8575         animate : false,
8576         duration : .35,
8577         dynamic : false,
8578         handles : false,
8579         multiDirectional : false,
8580         disableTrackOver : false,
8581         easing : 'easeOutStrong',
8582         widthIncrement : 0,
8583         heightIncrement : 0,
8584         pinned : false,
8585         width : null,
8586         height : null,
8587         preserveRatio : false,
8588         transparent: false,
8589         minX: 0,
8590         minY: 0,
8591         draggable: false,
8592
8593         /**
8594          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8595          */
8596         constrainTo: undefined,
8597         /**
8598          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8599          */
8600         resizeRegion: undefined,
8601
8602
8603     /**
8604      * Perform a manual resize
8605      * @param {Number} width
8606      * @param {Number} height
8607      */
8608     resizeTo : function(width, height){
8609         this.el.setSize(width, height);
8610         this.updateChildSize();
8611         this.fireEvent("resize", this, width, height, null);
8612     },
8613
8614     // private
8615     startSizing : function(e, handle){
8616         this.fireEvent("beforeresize", this, e);
8617         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8618
8619             if(!this.overlay){
8620                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8621                 this.overlay.unselectable();
8622                 this.overlay.enableDisplayMode("block");
8623                 this.overlay.on("mousemove", this.onMouseMove, this);
8624                 this.overlay.on("mouseup", this.onMouseUp, this);
8625             }
8626             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8627
8628             this.resizing = true;
8629             this.startBox = this.el.getBox();
8630             this.startPoint = e.getXY();
8631             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8632                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8633
8634             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8635             this.overlay.show();
8636
8637             if(this.constrainTo) {
8638                 var ct = Roo.get(this.constrainTo);
8639                 this.resizeRegion = ct.getRegion().adjust(
8640                     ct.getFrameWidth('t'),
8641                     ct.getFrameWidth('l'),
8642                     -ct.getFrameWidth('b'),
8643                     -ct.getFrameWidth('r')
8644                 );
8645             }
8646
8647             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8648             this.proxy.show();
8649             this.proxy.setBox(this.startBox);
8650             if(!this.dynamic){
8651                 this.proxy.setStyle('visibility', 'visible');
8652             }
8653         }
8654     },
8655
8656     // private
8657     onMouseDown : function(handle, e){
8658         if(this.enabled){
8659             e.stopEvent();
8660             this.activeHandle = handle;
8661             this.startSizing(e, handle);
8662         }
8663     },
8664
8665     // private
8666     onMouseUp : function(e){
8667         var size = this.resizeElement();
8668         this.resizing = false;
8669         this.handleOut();
8670         this.overlay.hide();
8671         this.proxy.hide();
8672         this.fireEvent("resize", this, size.width, size.height, e);
8673     },
8674
8675     // private
8676     updateChildSize : function(){
8677         
8678         if(this.resizeChild){
8679             var el = this.el;
8680             var child = this.resizeChild;
8681             var adj = this.adjustments;
8682             if(el.dom.offsetWidth){
8683                 var b = el.getSize(true);
8684                 child.setSize(b.width+adj[0], b.height+adj[1]);
8685             }
8686             // Second call here for IE
8687             // The first call enables instant resizing and
8688             // the second call corrects scroll bars if they
8689             // exist
8690             if(Roo.isIE){
8691                 setTimeout(function(){
8692                     if(el.dom.offsetWidth){
8693                         var b = el.getSize(true);
8694                         child.setSize(b.width+adj[0], b.height+adj[1]);
8695                     }
8696                 }, 10);
8697             }
8698         }
8699     },
8700
8701     // private
8702     snap : function(value, inc, min){
8703         if(!inc || !value) {
8704             return value;
8705         }
8706         var newValue = value;
8707         var m = value % inc;
8708         if(m > 0){
8709             if(m > (inc/2)){
8710                 newValue = value + (inc-m);
8711             }else{
8712                 newValue = value - m;
8713             }
8714         }
8715         return Math.max(min, newValue);
8716     },
8717
8718     // private
8719     resizeElement : function(){
8720         var box = this.proxy.getBox();
8721         if(this.updateBox){
8722             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8723         }else{
8724             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8725         }
8726         this.updateChildSize();
8727         if(!this.dynamic){
8728             this.proxy.hide();
8729         }
8730         return box;
8731     },
8732
8733     // private
8734     constrain : function(v, diff, m, mx){
8735         if(v - diff < m){
8736             diff = v - m;
8737         }else if(v - diff > mx){
8738             diff = mx - v;
8739         }
8740         return diff;
8741     },
8742
8743     // private
8744     onMouseMove : function(e){
8745         
8746         if(this.enabled){
8747             try{// try catch so if something goes wrong the user doesn't get hung
8748
8749             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8750                 return;
8751             }
8752
8753             //var curXY = this.startPoint;
8754             var curSize = this.curSize || this.startBox;
8755             var x = this.startBox.x, y = this.startBox.y;
8756             var ox = x, oy = y;
8757             var w = curSize.width, h = curSize.height;
8758             var ow = w, oh = h;
8759             var mw = this.minWidth, mh = this.minHeight;
8760             var mxw = this.maxWidth, mxh = this.maxHeight;
8761             var wi = this.widthIncrement;
8762             var hi = this.heightIncrement;
8763
8764             var eventXY = e.getXY();
8765             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8766             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8767
8768             var pos = this.activeHandle.position;
8769
8770             switch(pos){
8771                 case "east":
8772                     w += diffX;
8773                     w = Math.min(Math.max(mw, w), mxw);
8774                     break;
8775              
8776                 case "south":
8777                     h += diffY;
8778                     h = Math.min(Math.max(mh, h), mxh);
8779                     break;
8780                 case "southeast":
8781                     w += diffX;
8782                     h += diffY;
8783                     w = Math.min(Math.max(mw, w), mxw);
8784                     h = Math.min(Math.max(mh, h), mxh);
8785                     break;
8786                 case "north":
8787                     diffY = this.constrain(h, diffY, mh, mxh);
8788                     y += diffY;
8789                     h -= diffY;
8790                     break;
8791                 case "hdrag":
8792                     
8793                     if (wi) {
8794                         var adiffX = Math.abs(diffX);
8795                         var sub = (adiffX % wi); // how much 
8796                         if (sub > (wi/2)) { // far enough to snap
8797                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8798                         } else {
8799                             // remove difference.. 
8800                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8801                         }
8802                     }
8803                     x += diffX;
8804                     x = Math.max(this.minX, x);
8805                     break;
8806                 case "west":
8807                     diffX = this.constrain(w, diffX, mw, mxw);
8808                     x += diffX;
8809                     w -= diffX;
8810                     break;
8811                 case "northeast":
8812                     w += diffX;
8813                     w = Math.min(Math.max(mw, w), mxw);
8814                     diffY = this.constrain(h, diffY, mh, mxh);
8815                     y += diffY;
8816                     h -= diffY;
8817                     break;
8818                 case "northwest":
8819                     diffX = this.constrain(w, diffX, mw, mxw);
8820                     diffY = this.constrain(h, diffY, mh, mxh);
8821                     y += diffY;
8822                     h -= diffY;
8823                     x += diffX;
8824                     w -= diffX;
8825                     break;
8826                case "southwest":
8827                     diffX = this.constrain(w, diffX, mw, mxw);
8828                     h += diffY;
8829                     h = Math.min(Math.max(mh, h), mxh);
8830                     x += diffX;
8831                     w -= diffX;
8832                     break;
8833             }
8834
8835             var sw = this.snap(w, wi, mw);
8836             var sh = this.snap(h, hi, mh);
8837             if(sw != w || sh != h){
8838                 switch(pos){
8839                     case "northeast":
8840                         y -= sh - h;
8841                     break;
8842                     case "north":
8843                         y -= sh - h;
8844                         break;
8845                     case "southwest":
8846                         x -= sw - w;
8847                     break;
8848                     case "west":
8849                         x -= sw - w;
8850                         break;
8851                     case "northwest":
8852                         x -= sw - w;
8853                         y -= sh - h;
8854                     break;
8855                 }
8856                 w = sw;
8857                 h = sh;
8858             }
8859
8860             if(this.preserveRatio){
8861                 switch(pos){
8862                     case "southeast":
8863                     case "east":
8864                         h = oh * (w/ow);
8865                         h = Math.min(Math.max(mh, h), mxh);
8866                         w = ow * (h/oh);
8867                        break;
8868                     case "south":
8869                         w = ow * (h/oh);
8870                         w = Math.min(Math.max(mw, w), mxw);
8871                         h = oh * (w/ow);
8872                         break;
8873                     case "northeast":
8874                         w = ow * (h/oh);
8875                         w = Math.min(Math.max(mw, w), mxw);
8876                         h = oh * (w/ow);
8877                     break;
8878                     case "north":
8879                         var tw = w;
8880                         w = ow * (h/oh);
8881                         w = Math.min(Math.max(mw, w), mxw);
8882                         h = oh * (w/ow);
8883                         x += (tw - w) / 2;
8884                         break;
8885                     case "southwest":
8886                         h = oh * (w/ow);
8887                         h = Math.min(Math.max(mh, h), mxh);
8888                         var tw = w;
8889                         w = ow * (h/oh);
8890                         x += tw - w;
8891                         break;
8892                     case "west":
8893                         var th = h;
8894                         h = oh * (w/ow);
8895                         h = Math.min(Math.max(mh, h), mxh);
8896                         y += (th - h) / 2;
8897                         var tw = w;
8898                         w = ow * (h/oh);
8899                         x += tw - w;
8900                        break;
8901                     case "northwest":
8902                         var tw = w;
8903                         var th = h;
8904                         h = oh * (w/ow);
8905                         h = Math.min(Math.max(mh, h), mxh);
8906                         w = ow * (h/oh);
8907                         y += th - h;
8908                         x += tw - w;
8909                        break;
8910
8911                 }
8912             }
8913             if (pos == 'hdrag') {
8914                 w = ow;
8915             }
8916             this.proxy.setBounds(x, y, w, h);
8917             if(this.dynamic){
8918                 this.resizeElement();
8919             }
8920             }catch(e){}
8921         }
8922         this.fireEvent("resizing", this, x, y, w, h, e);
8923     },
8924
8925     // private
8926     handleOver : function(){
8927         if(this.enabled){
8928             this.el.addClass("x-resizable-over");
8929         }
8930     },
8931
8932     // private
8933     handleOut : function(){
8934         if(!this.resizing){
8935             this.el.removeClass("x-resizable-over");
8936         }
8937     },
8938
8939     /**
8940      * Returns the element this component is bound to.
8941      * @return {Roo.Element}
8942      */
8943     getEl : function(){
8944         return this.el;
8945     },
8946
8947     /**
8948      * Returns the resizeChild element (or null).
8949      * @return {Roo.Element}
8950      */
8951     getResizeChild : function(){
8952         return this.resizeChild;
8953     },
8954     groupHandler : function()
8955     {
8956         
8957     },
8958     /**
8959      * Destroys this resizable. If the element was wrapped and
8960      * removeEl is not true then the element remains.
8961      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8962      */
8963     destroy : function(removeEl){
8964         this.proxy.remove();
8965         if(this.overlay){
8966             this.overlay.removeAllListeners();
8967             this.overlay.remove();
8968         }
8969         var ps = Roo.Resizable.positions;
8970         for(var k in ps){
8971             if(typeof ps[k] != "function" && this[ps[k]]){
8972                 var h = this[ps[k]];
8973                 h.el.removeAllListeners();
8974                 h.el.remove();
8975             }
8976         }
8977         if(removeEl){
8978             this.el.update("");
8979             this.el.remove();
8980         }
8981     }
8982 });
8983
8984 // private
8985 // hash to map config positions to true positions
8986 Roo.Resizable.positions = {
8987     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8988     hd: "hdrag"
8989 };
8990
8991 // private
8992 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8993     if(!this.tpl){
8994         // only initialize the template if resizable is used
8995         var tpl = Roo.DomHelper.createTemplate(
8996             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8997         );
8998         tpl.compile();
8999         Roo.Resizable.Handle.prototype.tpl = tpl;
9000     }
9001     this.position = pos;
9002     this.rz = rz;
9003     // show north drag fro topdra
9004     var handlepos = pos == 'hdrag' ? 'north' : pos;
9005     
9006     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9007     if (pos == 'hdrag') {
9008         this.el.setStyle('cursor', 'pointer');
9009     }
9010     this.el.unselectable();
9011     if(transparent){
9012         this.el.setOpacity(0);
9013     }
9014     this.el.on("mousedown", this.onMouseDown, this);
9015     if(!disableTrackOver){
9016         this.el.on("mouseover", this.onMouseOver, this);
9017         this.el.on("mouseout", this.onMouseOut, this);
9018     }
9019 };
9020
9021 // private
9022 Roo.Resizable.Handle.prototype = {
9023     afterResize : function(rz){
9024         Roo.log('after?');
9025         // do nothing
9026     },
9027     // private
9028     onMouseDown : function(e){
9029         this.rz.onMouseDown(this, e);
9030     },
9031     // private
9032     onMouseOver : function(e){
9033         this.rz.handleOver(this, e);
9034     },
9035     // private
9036     onMouseOut : function(e){
9037         this.rz.handleOut(this, e);
9038     }
9039 };/*
9040  * Based on:
9041  * Ext JS Library 1.1.1
9042  * Copyright(c) 2006-2007, Ext JS, LLC.
9043  *
9044  * Originally Released Under LGPL - original licence link has changed is not relivant.
9045  *
9046  * Fork - LGPL
9047  * <script type="text/javascript">
9048  */
9049
9050 /**
9051  * @class Roo.Editor
9052  * @extends Roo.Component
9053  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9054  * @constructor
9055  * Create a new Editor
9056  * @param {Roo.form.Field} field The Field object (or descendant)
9057  * @param {Object} config The config object
9058  */
9059 Roo.Editor = function(field, config){
9060     Roo.Editor.superclass.constructor.call(this, config);
9061     this.field = field;
9062     this.addEvents({
9063         /**
9064              * @event beforestartedit
9065              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9066              * false from the handler of this event.
9067              * @param {Editor} this
9068              * @param {Roo.Element} boundEl The underlying element bound to this editor
9069              * @param {Mixed} value The field value being set
9070              */
9071         "beforestartedit" : true,
9072         /**
9073              * @event startedit
9074              * Fires when this editor is displayed
9075              * @param {Roo.Element} boundEl The underlying element bound to this editor
9076              * @param {Mixed} value The starting field value
9077              */
9078         "startedit" : true,
9079         /**
9080              * @event beforecomplete
9081              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9082              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9083              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9084              * event will not fire since no edit actually occurred.
9085              * @param {Editor} this
9086              * @param {Mixed} value The current field value
9087              * @param {Mixed} startValue The original field value
9088              */
9089         "beforecomplete" : true,
9090         /**
9091              * @event complete
9092              * Fires after editing is complete and any changed value has been written to the underlying field.
9093              * @param {Editor} this
9094              * @param {Mixed} value The current field value
9095              * @param {Mixed} startValue The original field value
9096              */
9097         "complete" : true,
9098         /**
9099          * @event specialkey
9100          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9101          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9102          * @param {Roo.form.Field} this
9103          * @param {Roo.EventObject} e The event object
9104          */
9105         "specialkey" : true
9106     });
9107 };
9108
9109 Roo.extend(Roo.Editor, Roo.Component, {
9110     /**
9111      * @cfg {Boolean/String} autosize
9112      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9113      * or "height" to adopt the height only (defaults to false)
9114      */
9115     /**
9116      * @cfg {Boolean} revertInvalid
9117      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9118      * validation fails (defaults to true)
9119      */
9120     /**
9121      * @cfg {Boolean} ignoreNoChange
9122      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9123      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9124      * will never be ignored.
9125      */
9126     /**
9127      * @cfg {Boolean} hideEl
9128      * False to keep the bound element visible while the editor is displayed (defaults to true)
9129      */
9130     /**
9131      * @cfg {Mixed} value
9132      * The data value of the underlying field (defaults to "")
9133      */
9134     value : "",
9135     /**
9136      * @cfg {String} alignment
9137      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9138      */
9139     alignment: "c-c?",
9140     /**
9141      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9142      * for bottom-right shadow (defaults to "frame")
9143      */
9144     shadow : "frame",
9145     /**
9146      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9147      */
9148     constrain : false,
9149     /**
9150      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9151      */
9152     completeOnEnter : false,
9153     /**
9154      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9155      */
9156     cancelOnEsc : false,
9157     /**
9158      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9159      */
9160     updateEl : false,
9161
9162     // private
9163     onRender : function(ct, position){
9164         this.el = new Roo.Layer({
9165             shadow: this.shadow,
9166             cls: "x-editor",
9167             parentEl : ct,
9168             shim : this.shim,
9169             shadowOffset:4,
9170             id: this.id,
9171             constrain: this.constrain
9172         });
9173         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9174         if(this.field.msgTarget != 'title'){
9175             this.field.msgTarget = 'qtip';
9176         }
9177         this.field.render(this.el);
9178         if(Roo.isGecko){
9179             this.field.el.dom.setAttribute('autocomplete', 'off');
9180         }
9181         this.field.on("specialkey", this.onSpecialKey, this);
9182         if(this.swallowKeys){
9183             this.field.el.swallowEvent(['keydown','keypress']);
9184         }
9185         this.field.show();
9186         this.field.on("blur", this.onBlur, this);
9187         if(this.field.grow){
9188             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9189         }
9190     },
9191
9192     onSpecialKey : function(field, e)
9193     {
9194         //Roo.log('editor onSpecialKey');
9195         if(this.completeOnEnter && e.getKey() == e.ENTER){
9196             e.stopEvent();
9197             this.completeEdit();
9198             return;
9199         }
9200         // do not fire special key otherwise it might hide close the editor...
9201         if(e.getKey() == e.ENTER){    
9202             return;
9203         }
9204         if(this.cancelOnEsc && e.getKey() == e.ESC){
9205             this.cancelEdit();
9206             return;
9207         } 
9208         this.fireEvent('specialkey', field, e);
9209     
9210     },
9211
9212     /**
9213      * Starts the editing process and shows the editor.
9214      * @param {String/HTMLElement/Element} el The element to edit
9215      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9216       * to the innerHTML of el.
9217      */
9218     startEdit : function(el, value){
9219         if(this.editing){
9220             this.completeEdit();
9221         }
9222         this.boundEl = Roo.get(el);
9223         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9224         if(!this.rendered){
9225             this.render(this.parentEl || document.body);
9226         }
9227         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9228             return;
9229         }
9230         this.startValue = v;
9231         this.field.setValue(v);
9232         if(this.autoSize){
9233             var sz = this.boundEl.getSize();
9234             switch(this.autoSize){
9235                 case "width":
9236                 this.setSize(sz.width,  "");
9237                 break;
9238                 case "height":
9239                 this.setSize("",  sz.height);
9240                 break;
9241                 default:
9242                 this.setSize(sz.width,  sz.height);
9243             }
9244         }
9245         this.el.alignTo(this.boundEl, this.alignment);
9246         this.editing = true;
9247         if(Roo.QuickTips){
9248             Roo.QuickTips.disable();
9249         }
9250         this.show();
9251     },
9252
9253     /**
9254      * Sets the height and width of this editor.
9255      * @param {Number} width The new width
9256      * @param {Number} height The new height
9257      */
9258     setSize : function(w, h){
9259         this.field.setSize(w, h);
9260         if(this.el){
9261             this.el.sync();
9262         }
9263     },
9264
9265     /**
9266      * Realigns the editor to the bound field based on the current alignment config value.
9267      */
9268     realign : function(){
9269         this.el.alignTo(this.boundEl, this.alignment);
9270     },
9271
9272     /**
9273      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9274      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9275      */
9276     completeEdit : function(remainVisible){
9277         if(!this.editing){
9278             return;
9279         }
9280         var v = this.getValue();
9281         if(this.revertInvalid !== false && !this.field.isValid()){
9282             v = this.startValue;
9283             this.cancelEdit(true);
9284         }
9285         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9286             this.editing = false;
9287             this.hide();
9288             return;
9289         }
9290         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9291             this.editing = false;
9292             if(this.updateEl && this.boundEl){
9293                 this.boundEl.update(v);
9294             }
9295             if(remainVisible !== true){
9296                 this.hide();
9297             }
9298             this.fireEvent("complete", this, v, this.startValue);
9299         }
9300     },
9301
9302     // private
9303     onShow : function(){
9304         this.el.show();
9305         if(this.hideEl !== false){
9306             this.boundEl.hide();
9307         }
9308         this.field.show();
9309         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9310             this.fixIEFocus = true;
9311             this.deferredFocus.defer(50, this);
9312         }else{
9313             this.field.focus();
9314         }
9315         this.fireEvent("startedit", this.boundEl, this.startValue);
9316     },
9317
9318     deferredFocus : function(){
9319         if(this.editing){
9320             this.field.focus();
9321         }
9322     },
9323
9324     /**
9325      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9326      * reverted to the original starting value.
9327      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9328      * cancel (defaults to false)
9329      */
9330     cancelEdit : function(remainVisible){
9331         if(this.editing){
9332             this.setValue(this.startValue);
9333             if(remainVisible !== true){
9334                 this.hide();
9335             }
9336         }
9337     },
9338
9339     // private
9340     onBlur : function(){
9341         if(this.allowBlur !== true && this.editing){
9342             this.completeEdit();
9343         }
9344     },
9345
9346     // private
9347     onHide : function(){
9348         if(this.editing){
9349             this.completeEdit();
9350             return;
9351         }
9352         this.field.blur();
9353         if(this.field.collapse){
9354             this.field.collapse();
9355         }
9356         this.el.hide();
9357         if(this.hideEl !== false){
9358             this.boundEl.show();
9359         }
9360         if(Roo.QuickTips){
9361             Roo.QuickTips.enable();
9362         }
9363     },
9364
9365     /**
9366      * Sets the data value of the editor
9367      * @param {Mixed} value Any valid value supported by the underlying field
9368      */
9369     setValue : function(v){
9370         this.field.setValue(v);
9371     },
9372
9373     /**
9374      * Gets the data value of the editor
9375      * @return {Mixed} The data value
9376      */
9377     getValue : function(){
9378         return this.field.getValue();
9379     }
9380 });/*
9381  * Based on:
9382  * Ext JS Library 1.1.1
9383  * Copyright(c) 2006-2007, Ext JS, LLC.
9384  *
9385  * Originally Released Under LGPL - original licence link has changed is not relivant.
9386  *
9387  * Fork - LGPL
9388  * <script type="text/javascript">
9389  */
9390  
9391 /**
9392  * @class Roo.BasicDialog
9393  * @extends Roo.util.Observable
9394  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9395  * <pre><code>
9396 var dlg = new Roo.BasicDialog("my-dlg", {
9397     height: 200,
9398     width: 300,
9399     minHeight: 100,
9400     minWidth: 150,
9401     modal: true,
9402     proxyDrag: true,
9403     shadow: true
9404 });
9405 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9406 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9407 dlg.addButton('Cancel', dlg.hide, dlg);
9408 dlg.show();
9409 </code></pre>
9410   <b>A Dialog should always be a direct child of the body element.</b>
9411  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9412  * @cfg {String} title Default text to display in the title bar (defaults to null)
9413  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9414  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9415  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9416  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9417  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9418  * (defaults to null with no animation)
9419  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9420  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9421  * property for valid values (defaults to 'all')
9422  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9423  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9424  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9425  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9426  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9427  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9428  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9429  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9430  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9431  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9432  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9433  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9434  * draggable = true (defaults to false)
9435  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9436  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9437  * shadow (defaults to false)
9438  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9439  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9440  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9441  * @cfg {Array} buttons Array of buttons
9442  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9443  * @constructor
9444  * Create a new BasicDialog.
9445  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9446  * @param {Object} config Configuration options
9447  */
9448 Roo.BasicDialog = function(el, config){
9449     this.el = Roo.get(el);
9450     var dh = Roo.DomHelper;
9451     if(!this.el && config && config.autoCreate){
9452         if(typeof config.autoCreate == "object"){
9453             if(!config.autoCreate.id){
9454                 config.autoCreate.id = el;
9455             }
9456             this.el = dh.append(document.body,
9457                         config.autoCreate, true);
9458         }else{
9459             this.el = dh.append(document.body,
9460                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9461         }
9462     }
9463     el = this.el;
9464     el.setDisplayed(true);
9465     el.hide = this.hideAction;
9466     this.id = el.id;
9467     el.addClass("x-dlg");
9468
9469     Roo.apply(this, config);
9470
9471     this.proxy = el.createProxy("x-dlg-proxy");
9472     this.proxy.hide = this.hideAction;
9473     this.proxy.setOpacity(.5);
9474     this.proxy.hide();
9475
9476     if(config.width){
9477         el.setWidth(config.width);
9478     }
9479     if(config.height){
9480         el.setHeight(config.height);
9481     }
9482     this.size = el.getSize();
9483     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9484         this.xy = [config.x,config.y];
9485     }else{
9486         this.xy = el.getCenterXY(true);
9487     }
9488     /** The header element @type Roo.Element */
9489     this.header = el.child("> .x-dlg-hd");
9490     /** The body element @type Roo.Element */
9491     this.body = el.child("> .x-dlg-bd");
9492     /** The footer element @type Roo.Element */
9493     this.footer = el.child("> .x-dlg-ft");
9494
9495     if(!this.header){
9496         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9497     }
9498     if(!this.body){
9499         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9500     }
9501
9502     this.header.unselectable();
9503     if(this.title){
9504         this.header.update(this.title);
9505     }
9506     // this element allows the dialog to be focused for keyboard event
9507     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9508     this.focusEl.swallowEvent("click", true);
9509
9510     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9511
9512     // wrap the body and footer for special rendering
9513     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9514     if(this.footer){
9515         this.bwrap.dom.appendChild(this.footer.dom);
9516     }
9517
9518     this.bg = this.el.createChild({
9519         tag: "div", cls:"x-dlg-bg",
9520         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9521     });
9522     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9523
9524
9525     if(this.autoScroll !== false && !this.autoTabs){
9526         this.body.setStyle("overflow", "auto");
9527     }
9528
9529     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9530
9531     if(this.closable !== false){
9532         this.el.addClass("x-dlg-closable");
9533         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9534         this.close.on("click", this.closeClick, this);
9535         this.close.addClassOnOver("x-dlg-close-over");
9536     }
9537     if(this.collapsible !== false){
9538         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9539         this.collapseBtn.on("click", this.collapseClick, this);
9540         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9541         this.header.on("dblclick", this.collapseClick, this);
9542     }
9543     if(this.resizable !== false){
9544         this.el.addClass("x-dlg-resizable");
9545         this.resizer = new Roo.Resizable(el, {
9546             minWidth: this.minWidth || 80,
9547             minHeight:this.minHeight || 80,
9548             handles: this.resizeHandles || "all",
9549             pinned: true
9550         });
9551         this.resizer.on("beforeresize", this.beforeResize, this);
9552         this.resizer.on("resize", this.onResize, this);
9553     }
9554     if(this.draggable !== false){
9555         el.addClass("x-dlg-draggable");
9556         if (!this.proxyDrag) {
9557             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9558         }
9559         else {
9560             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9561         }
9562         dd.setHandleElId(this.header.id);
9563         dd.endDrag = this.endMove.createDelegate(this);
9564         dd.startDrag = this.startMove.createDelegate(this);
9565         dd.onDrag = this.onDrag.createDelegate(this);
9566         dd.scroll = false;
9567         this.dd = dd;
9568     }
9569     if(this.modal){
9570         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9571         this.mask.enableDisplayMode("block");
9572         this.mask.hide();
9573         this.el.addClass("x-dlg-modal");
9574     }
9575     if(this.shadow){
9576         this.shadow = new Roo.Shadow({
9577             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9578             offset : this.shadowOffset
9579         });
9580     }else{
9581         this.shadowOffset = 0;
9582     }
9583     if(Roo.useShims && this.shim !== false){
9584         this.shim = this.el.createShim();
9585         this.shim.hide = this.hideAction;
9586         this.shim.hide();
9587     }else{
9588         this.shim = false;
9589     }
9590     if(this.autoTabs){
9591         this.initTabs();
9592     }
9593     if (this.buttons) { 
9594         var bts= this.buttons;
9595         this.buttons = [];
9596         Roo.each(bts, function(b) {
9597             this.addButton(b);
9598         }, this);
9599     }
9600     
9601     
9602     this.addEvents({
9603         /**
9604          * @event keydown
9605          * Fires when a key is pressed
9606          * @param {Roo.BasicDialog} this
9607          * @param {Roo.EventObject} e
9608          */
9609         "keydown" : true,
9610         /**
9611          * @event move
9612          * Fires when this dialog is moved by the user.
9613          * @param {Roo.BasicDialog} this
9614          * @param {Number} x The new page X
9615          * @param {Number} y The new page Y
9616          */
9617         "move" : true,
9618         /**
9619          * @event resize
9620          * Fires when this dialog is resized by the user.
9621          * @param {Roo.BasicDialog} this
9622          * @param {Number} width The new width
9623          * @param {Number} height The new height
9624          */
9625         "resize" : true,
9626         /**
9627          * @event beforehide
9628          * Fires before this dialog is hidden.
9629          * @param {Roo.BasicDialog} this
9630          */
9631         "beforehide" : true,
9632         /**
9633          * @event hide
9634          * Fires when this dialog is hidden.
9635          * @param {Roo.BasicDialog} this
9636          */
9637         "hide" : true,
9638         /**
9639          * @event beforeshow
9640          * Fires before this dialog is shown.
9641          * @param {Roo.BasicDialog} this
9642          */
9643         "beforeshow" : true,
9644         /**
9645          * @event show
9646          * Fires when this dialog is shown.
9647          * @param {Roo.BasicDialog} this
9648          */
9649         "show" : true
9650     });
9651     el.on("keydown", this.onKeyDown, this);
9652     el.on("mousedown", this.toFront, this);
9653     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9654     this.el.hide();
9655     Roo.DialogManager.register(this);
9656     Roo.BasicDialog.superclass.constructor.call(this);
9657 };
9658
9659 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9660     shadowOffset: Roo.isIE ? 6 : 5,
9661     minHeight: 80,
9662     minWidth: 200,
9663     minButtonWidth: 75,
9664     defaultButton: null,
9665     buttonAlign: "right",
9666     tabTag: 'div',
9667     firstShow: true,
9668
9669     /**
9670      * Sets the dialog title text
9671      * @param {String} text The title text to display
9672      * @return {Roo.BasicDialog} this
9673      */
9674     setTitle : function(text){
9675         this.header.update(text);
9676         return this;
9677     },
9678
9679     // private
9680     closeClick : function(){
9681         this.hide();
9682     },
9683
9684     // private
9685     collapseClick : function(){
9686         this[this.collapsed ? "expand" : "collapse"]();
9687     },
9688
9689     /**
9690      * Collapses the dialog to its minimized state (only the title bar is visible).
9691      * Equivalent to the user clicking the collapse dialog button.
9692      */
9693     collapse : function(){
9694         if(!this.collapsed){
9695             this.collapsed = true;
9696             this.el.addClass("x-dlg-collapsed");
9697             this.restoreHeight = this.el.getHeight();
9698             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9699         }
9700     },
9701
9702     /**
9703      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9704      * clicking the expand dialog button.
9705      */
9706     expand : function(){
9707         if(this.collapsed){
9708             this.collapsed = false;
9709             this.el.removeClass("x-dlg-collapsed");
9710             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9711         }
9712     },
9713
9714     /**
9715      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9716      * @return {Roo.TabPanel} The tabs component
9717      */
9718     initTabs : function(){
9719         var tabs = this.getTabs();
9720         while(tabs.getTab(0)){
9721             tabs.removeTab(0);
9722         }
9723         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9724             var dom = el.dom;
9725             tabs.addTab(Roo.id(dom), dom.title);
9726             dom.title = "";
9727         });
9728         tabs.activate(0);
9729         return tabs;
9730     },
9731
9732     // private
9733     beforeResize : function(){
9734         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9735     },
9736
9737     // private
9738     onResize : function(){
9739         this.refreshSize();
9740         this.syncBodyHeight();
9741         this.adjustAssets();
9742         this.focus();
9743         this.fireEvent("resize", this, this.size.width, this.size.height);
9744     },
9745
9746     // private
9747     onKeyDown : function(e){
9748         if(this.isVisible()){
9749             this.fireEvent("keydown", this, e);
9750         }
9751     },
9752
9753     /**
9754      * Resizes the dialog.
9755      * @param {Number} width
9756      * @param {Number} height
9757      * @return {Roo.BasicDialog} this
9758      */
9759     resizeTo : function(width, height){
9760         this.el.setSize(width, height);
9761         this.size = {width: width, height: height};
9762         this.syncBodyHeight();
9763         if(this.fixedcenter){
9764             this.center();
9765         }
9766         if(this.isVisible()){
9767             this.constrainXY();
9768             this.adjustAssets();
9769         }
9770         this.fireEvent("resize", this, width, height);
9771         return this;
9772     },
9773
9774
9775     /**
9776      * Resizes the dialog to fit the specified content size.
9777      * @param {Number} width
9778      * @param {Number} height
9779      * @return {Roo.BasicDialog} this
9780      */
9781     setContentSize : function(w, h){
9782         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9783         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9784         //if(!this.el.isBorderBox()){
9785             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9786             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9787         //}
9788         if(this.tabs){
9789             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9790             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9791         }
9792         this.resizeTo(w, h);
9793         return this;
9794     },
9795
9796     /**
9797      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9798      * executed in response to a particular key being pressed while the dialog is active.
9799      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9800      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9801      * @param {Function} fn The function to call
9802      * @param {Object} scope (optional) The scope of the function
9803      * @return {Roo.BasicDialog} this
9804      */
9805     addKeyListener : function(key, fn, scope){
9806         var keyCode, shift, ctrl, alt;
9807         if(typeof key == "object" && !(key instanceof Array)){
9808             keyCode = key["key"];
9809             shift = key["shift"];
9810             ctrl = key["ctrl"];
9811             alt = key["alt"];
9812         }else{
9813             keyCode = key;
9814         }
9815         var handler = function(dlg, e){
9816             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9817                 var k = e.getKey();
9818                 if(keyCode instanceof Array){
9819                     for(var i = 0, len = keyCode.length; i < len; i++){
9820                         if(keyCode[i] == k){
9821                           fn.call(scope || window, dlg, k, e);
9822                           return;
9823                         }
9824                     }
9825                 }else{
9826                     if(k == keyCode){
9827                         fn.call(scope || window, dlg, k, e);
9828                     }
9829                 }
9830             }
9831         };
9832         this.on("keydown", handler);
9833         return this;
9834     },
9835
9836     /**
9837      * Returns the TabPanel component (creates it if it doesn't exist).
9838      * Note: If you wish to simply check for the existence of tabs without creating them,
9839      * check for a null 'tabs' property.
9840      * @return {Roo.TabPanel} The tabs component
9841      */
9842     getTabs : function(){
9843         if(!this.tabs){
9844             this.el.addClass("x-dlg-auto-tabs");
9845             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9846             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9847         }
9848         return this.tabs;
9849     },
9850
9851     /**
9852      * Adds a button to the footer section of the dialog.
9853      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9854      * object or a valid Roo.DomHelper element config
9855      * @param {Function} handler The function called when the button is clicked
9856      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9857      * @return {Roo.Button} The new button
9858      */
9859     addButton : function(config, handler, scope){
9860         var dh = Roo.DomHelper;
9861         if(!this.footer){
9862             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9863         }
9864         if(!this.btnContainer){
9865             var tb = this.footer.createChild({
9866
9867                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9868                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9869             }, null, true);
9870             this.btnContainer = tb.firstChild.firstChild.firstChild;
9871         }
9872         var bconfig = {
9873             handler: handler,
9874             scope: scope,
9875             minWidth: this.minButtonWidth,
9876             hideParent:true
9877         };
9878         if(typeof config == "string"){
9879             bconfig.text = config;
9880         }else{
9881             if(config.tag){
9882                 bconfig.dhconfig = config;
9883             }else{
9884                 Roo.apply(bconfig, config);
9885             }
9886         }
9887         var fc = false;
9888         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9889             bconfig.position = Math.max(0, bconfig.position);
9890             fc = this.btnContainer.childNodes[bconfig.position];
9891         }
9892          
9893         var btn = new Roo.Button(
9894             fc ? 
9895                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9896                 : this.btnContainer.appendChild(document.createElement("td")),
9897             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9898             bconfig
9899         );
9900         this.syncBodyHeight();
9901         if(!this.buttons){
9902             /**
9903              * Array of all the buttons that have been added to this dialog via addButton
9904              * @type Array
9905              */
9906             this.buttons = [];
9907         }
9908         this.buttons.push(btn);
9909         return btn;
9910     },
9911
9912     /**
9913      * Sets the default button to be focused when the dialog is displayed.
9914      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9915      * @return {Roo.BasicDialog} this
9916      */
9917     setDefaultButton : function(btn){
9918         this.defaultButton = btn;
9919         return this;
9920     },
9921
9922     // private
9923     getHeaderFooterHeight : function(safe){
9924         var height = 0;
9925         if(this.header){
9926            height += this.header.getHeight();
9927         }
9928         if(this.footer){
9929            var fm = this.footer.getMargins();
9930             height += (this.footer.getHeight()+fm.top+fm.bottom);
9931         }
9932         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9933         height += this.centerBg.getPadding("tb");
9934         return height;
9935     },
9936
9937     // private
9938     syncBodyHeight : function()
9939     {
9940         var bd = this.body, // the text
9941             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9942             bw = this.bwrap;
9943         var height = this.size.height - this.getHeaderFooterHeight(false);
9944         bd.setHeight(height-bd.getMargins("tb"));
9945         var hh = this.header.getHeight();
9946         var h = this.size.height-hh;
9947         cb.setHeight(h);
9948         
9949         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9950         bw.setHeight(h-cb.getPadding("tb"));
9951         
9952         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9953         bd.setWidth(bw.getWidth(true));
9954         if(this.tabs){
9955             this.tabs.syncHeight();
9956             if(Roo.isIE){
9957                 this.tabs.el.repaint();
9958             }
9959         }
9960     },
9961
9962     /**
9963      * Restores the previous state of the dialog if Roo.state is configured.
9964      * @return {Roo.BasicDialog} this
9965      */
9966     restoreState : function(){
9967         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9968         if(box && box.width){
9969             this.xy = [box.x, box.y];
9970             this.resizeTo(box.width, box.height);
9971         }
9972         return this;
9973     },
9974
9975     // private
9976     beforeShow : function(){
9977         this.expand();
9978         if(this.fixedcenter){
9979             this.xy = this.el.getCenterXY(true);
9980         }
9981         if(this.modal){
9982             Roo.get(document.body).addClass("x-body-masked");
9983             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9984             this.mask.show();
9985         }
9986         this.constrainXY();
9987     },
9988
9989     // private
9990     animShow : function(){
9991         var b = Roo.get(this.animateTarget).getBox();
9992         this.proxy.setSize(b.width, b.height);
9993         this.proxy.setLocation(b.x, b.y);
9994         this.proxy.show();
9995         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9996                     true, .35, this.showEl.createDelegate(this));
9997     },
9998
9999     /**
10000      * Shows the dialog.
10001      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10002      * @return {Roo.BasicDialog} this
10003      */
10004     show : function(animateTarget){
10005         if (this.fireEvent("beforeshow", this) === false){
10006             return;
10007         }
10008         if(this.syncHeightBeforeShow){
10009             this.syncBodyHeight();
10010         }else if(this.firstShow){
10011             this.firstShow = false;
10012             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10013         }
10014         this.animateTarget = animateTarget || this.animateTarget;
10015         if(!this.el.isVisible()){
10016             this.beforeShow();
10017             if(this.animateTarget && Roo.get(this.animateTarget)){
10018                 this.animShow();
10019             }else{
10020                 this.showEl();
10021             }
10022         }
10023         return this;
10024     },
10025
10026     // private
10027     showEl : function(){
10028         this.proxy.hide();
10029         this.el.setXY(this.xy);
10030         this.el.show();
10031         this.adjustAssets(true);
10032         this.toFront();
10033         this.focus();
10034         // IE peekaboo bug - fix found by Dave Fenwick
10035         if(Roo.isIE){
10036             this.el.repaint();
10037         }
10038         this.fireEvent("show", this);
10039     },
10040
10041     /**
10042      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10043      * dialog itself will receive focus.
10044      */
10045     focus : function(){
10046         if(this.defaultButton){
10047             this.defaultButton.focus();
10048         }else{
10049             this.focusEl.focus();
10050         }
10051     },
10052
10053     // private
10054     constrainXY : function(){
10055         if(this.constraintoviewport !== false){
10056             if(!this.viewSize){
10057                 if(this.container){
10058                     var s = this.container.getSize();
10059                     this.viewSize = [s.width, s.height];
10060                 }else{
10061                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10062                 }
10063             }
10064             var s = Roo.get(this.container||document).getScroll();
10065
10066             var x = this.xy[0], y = this.xy[1];
10067             var w = this.size.width, h = this.size.height;
10068             var vw = this.viewSize[0], vh = this.viewSize[1];
10069             // only move it if it needs it
10070             var moved = false;
10071             // first validate right/bottom
10072             if(x + w > vw+s.left){
10073                 x = vw - w;
10074                 moved = true;
10075             }
10076             if(y + h > vh+s.top){
10077                 y = vh - h;
10078                 moved = true;
10079             }
10080             // then make sure top/left isn't negative
10081             if(x < s.left){
10082                 x = s.left;
10083                 moved = true;
10084             }
10085             if(y < s.top){
10086                 y = s.top;
10087                 moved = true;
10088             }
10089             if(moved){
10090                 // cache xy
10091                 this.xy = [x, y];
10092                 if(this.isVisible()){
10093                     this.el.setLocation(x, y);
10094                     this.adjustAssets();
10095                 }
10096             }
10097         }
10098     },
10099
10100     // private
10101     onDrag : function(){
10102         if(!this.proxyDrag){
10103             this.xy = this.el.getXY();
10104             this.adjustAssets();
10105         }
10106     },
10107
10108     // private
10109     adjustAssets : function(doShow){
10110         var x = this.xy[0], y = this.xy[1];
10111         var w = this.size.width, h = this.size.height;
10112         if(doShow === true){
10113             if(this.shadow){
10114                 this.shadow.show(this.el);
10115             }
10116             if(this.shim){
10117                 this.shim.show();
10118             }
10119         }
10120         if(this.shadow && this.shadow.isVisible()){
10121             this.shadow.show(this.el);
10122         }
10123         if(this.shim && this.shim.isVisible()){
10124             this.shim.setBounds(x, y, w, h);
10125         }
10126     },
10127
10128     // private
10129     adjustViewport : function(w, h){
10130         if(!w || !h){
10131             w = Roo.lib.Dom.getViewWidth();
10132             h = Roo.lib.Dom.getViewHeight();
10133         }
10134         // cache the size
10135         this.viewSize = [w, h];
10136         if(this.modal && this.mask.isVisible()){
10137             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10138             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10139         }
10140         if(this.isVisible()){
10141             this.constrainXY();
10142         }
10143     },
10144
10145     /**
10146      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10147      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10148      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10149      */
10150     destroy : function(removeEl){
10151         if(this.isVisible()){
10152             this.animateTarget = null;
10153             this.hide();
10154         }
10155         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10156         if(this.tabs){
10157             this.tabs.destroy(removeEl);
10158         }
10159         Roo.destroy(
10160              this.shim,
10161              this.proxy,
10162              this.resizer,
10163              this.close,
10164              this.mask
10165         );
10166         if(this.dd){
10167             this.dd.unreg();
10168         }
10169         if(this.buttons){
10170            for(var i = 0, len = this.buttons.length; i < len; i++){
10171                this.buttons[i].destroy();
10172            }
10173         }
10174         this.el.removeAllListeners();
10175         if(removeEl === true){
10176             this.el.update("");
10177             this.el.remove();
10178         }
10179         Roo.DialogManager.unregister(this);
10180     },
10181
10182     // private
10183     startMove : function(){
10184         if(this.proxyDrag){
10185             this.proxy.show();
10186         }
10187         if(this.constraintoviewport !== false){
10188             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10189         }
10190     },
10191
10192     // private
10193     endMove : function(){
10194         if(!this.proxyDrag){
10195             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10196         }else{
10197             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10198             this.proxy.hide();
10199         }
10200         this.refreshSize();
10201         this.adjustAssets();
10202         this.focus();
10203         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10204     },
10205
10206     /**
10207      * Brings this dialog to the front of any other visible dialogs
10208      * @return {Roo.BasicDialog} this
10209      */
10210     toFront : function(){
10211         Roo.DialogManager.bringToFront(this);
10212         return this;
10213     },
10214
10215     /**
10216      * Sends this dialog to the back (under) of any other visible dialogs
10217      * @return {Roo.BasicDialog} this
10218      */
10219     toBack : function(){
10220         Roo.DialogManager.sendToBack(this);
10221         return this;
10222     },
10223
10224     /**
10225      * Centers this dialog in the viewport
10226      * @return {Roo.BasicDialog} this
10227      */
10228     center : function(){
10229         var xy = this.el.getCenterXY(true);
10230         this.moveTo(xy[0], xy[1]);
10231         return this;
10232     },
10233
10234     /**
10235      * Moves the dialog's top-left corner to the specified point
10236      * @param {Number} x
10237      * @param {Number} y
10238      * @return {Roo.BasicDialog} this
10239      */
10240     moveTo : function(x, y){
10241         this.xy = [x,y];
10242         if(this.isVisible()){
10243             this.el.setXY(this.xy);
10244             this.adjustAssets();
10245         }
10246         return this;
10247     },
10248
10249     /**
10250      * Aligns the dialog to the specified element
10251      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10252      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10253      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10254      * @return {Roo.BasicDialog} this
10255      */
10256     alignTo : function(element, position, offsets){
10257         this.xy = this.el.getAlignToXY(element, position, offsets);
10258         if(this.isVisible()){
10259             this.el.setXY(this.xy);
10260             this.adjustAssets();
10261         }
10262         return this;
10263     },
10264
10265     /**
10266      * Anchors an element to another element and realigns it when the window is resized.
10267      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10268      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10269      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10270      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10271      * is a number, it is used as the buffer delay (defaults to 50ms).
10272      * @return {Roo.BasicDialog} this
10273      */
10274     anchorTo : function(el, alignment, offsets, monitorScroll){
10275         var action = function(){
10276             this.alignTo(el, alignment, offsets);
10277         };
10278         Roo.EventManager.onWindowResize(action, this);
10279         var tm = typeof monitorScroll;
10280         if(tm != 'undefined'){
10281             Roo.EventManager.on(window, 'scroll', action, this,
10282                 {buffer: tm == 'number' ? monitorScroll : 50});
10283         }
10284         action.call(this);
10285         return this;
10286     },
10287
10288     /**
10289      * Returns true if the dialog is visible
10290      * @return {Boolean}
10291      */
10292     isVisible : function(){
10293         return this.el.isVisible();
10294     },
10295
10296     // private
10297     animHide : function(callback){
10298         var b = Roo.get(this.animateTarget).getBox();
10299         this.proxy.show();
10300         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10301         this.el.hide();
10302         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10303                     this.hideEl.createDelegate(this, [callback]));
10304     },
10305
10306     /**
10307      * Hides the dialog.
10308      * @param {Function} callback (optional) Function to call when the dialog is hidden
10309      * @return {Roo.BasicDialog} this
10310      */
10311     hide : function(callback){
10312         if (this.fireEvent("beforehide", this) === false){
10313             return;
10314         }
10315         if(this.shadow){
10316             this.shadow.hide();
10317         }
10318         if(this.shim) {
10319           this.shim.hide();
10320         }
10321         // sometimes animateTarget seems to get set.. causing problems...
10322         // this just double checks..
10323         if(this.animateTarget && Roo.get(this.animateTarget)) {
10324            this.animHide(callback);
10325         }else{
10326             this.el.hide();
10327             this.hideEl(callback);
10328         }
10329         return this;
10330     },
10331
10332     // private
10333     hideEl : function(callback){
10334         this.proxy.hide();
10335         if(this.modal){
10336             this.mask.hide();
10337             Roo.get(document.body).removeClass("x-body-masked");
10338         }
10339         this.fireEvent("hide", this);
10340         if(typeof callback == "function"){
10341             callback();
10342         }
10343     },
10344
10345     // private
10346     hideAction : function(){
10347         this.setLeft("-10000px");
10348         this.setTop("-10000px");
10349         this.setStyle("visibility", "hidden");
10350     },
10351
10352     // private
10353     refreshSize : function(){
10354         this.size = this.el.getSize();
10355         this.xy = this.el.getXY();
10356         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10357     },
10358
10359     // private
10360     // z-index is managed by the DialogManager and may be overwritten at any time
10361     setZIndex : function(index){
10362         if(this.modal){
10363             this.mask.setStyle("z-index", index);
10364         }
10365         if(this.shim){
10366             this.shim.setStyle("z-index", ++index);
10367         }
10368         if(this.shadow){
10369             this.shadow.setZIndex(++index);
10370         }
10371         this.el.setStyle("z-index", ++index);
10372         if(this.proxy){
10373             this.proxy.setStyle("z-index", ++index);
10374         }
10375         if(this.resizer){
10376             this.resizer.proxy.setStyle("z-index", ++index);
10377         }
10378
10379         this.lastZIndex = index;
10380     },
10381
10382     /**
10383      * Returns the element for this dialog
10384      * @return {Roo.Element} The underlying dialog Element
10385      */
10386     getEl : function(){
10387         return this.el;
10388     }
10389 });
10390
10391 /**
10392  * @class Roo.DialogManager
10393  * Provides global access to BasicDialogs that have been created and
10394  * support for z-indexing (layering) multiple open dialogs.
10395  */
10396 Roo.DialogManager = function(){
10397     var list = {};
10398     var accessList = [];
10399     var front = null;
10400
10401     // private
10402     var sortDialogs = function(d1, d2){
10403         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10404     };
10405
10406     // private
10407     var orderDialogs = function(){
10408         accessList.sort(sortDialogs);
10409         var seed = Roo.DialogManager.zseed;
10410         for(var i = 0, len = accessList.length; i < len; i++){
10411             var dlg = accessList[i];
10412             if(dlg){
10413                 dlg.setZIndex(seed + (i*10));
10414             }
10415         }
10416     };
10417
10418     return {
10419         /**
10420          * The starting z-index for BasicDialogs (defaults to 9000)
10421          * @type Number The z-index value
10422          */
10423         zseed : 9000,
10424
10425         // private
10426         register : function(dlg){
10427             list[dlg.id] = dlg;
10428             accessList.push(dlg);
10429         },
10430
10431         // private
10432         unregister : function(dlg){
10433             delete list[dlg.id];
10434             var i=0;
10435             var len=0;
10436             if(!accessList.indexOf){
10437                 for(  i = 0, len = accessList.length; i < len; i++){
10438                     if(accessList[i] == dlg){
10439                         accessList.splice(i, 1);
10440                         return;
10441                     }
10442                 }
10443             }else{
10444                  i = accessList.indexOf(dlg);
10445                 if(i != -1){
10446                     accessList.splice(i, 1);
10447                 }
10448             }
10449         },
10450
10451         /**
10452          * Gets a registered dialog by id
10453          * @param {String/Object} id The id of the dialog or a dialog
10454          * @return {Roo.BasicDialog} this
10455          */
10456         get : function(id){
10457             return typeof id == "object" ? id : list[id];
10458         },
10459
10460         /**
10461          * Brings the specified dialog to the front
10462          * @param {String/Object} dlg The id of the dialog or a dialog
10463          * @return {Roo.BasicDialog} this
10464          */
10465         bringToFront : function(dlg){
10466             dlg = this.get(dlg);
10467             if(dlg != front){
10468                 front = dlg;
10469                 dlg._lastAccess = new Date().getTime();
10470                 orderDialogs();
10471             }
10472             return dlg;
10473         },
10474
10475         /**
10476          * Sends the specified dialog to the back
10477          * @param {String/Object} dlg The id of the dialog or a dialog
10478          * @return {Roo.BasicDialog} this
10479          */
10480         sendToBack : function(dlg){
10481             dlg = this.get(dlg);
10482             dlg._lastAccess = -(new Date().getTime());
10483             orderDialogs();
10484             return dlg;
10485         },
10486
10487         /**
10488          * Hides all dialogs
10489          */
10490         hideAll : function(){
10491             for(var id in list){
10492                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10493                     list[id].hide();
10494                 }
10495             }
10496         }
10497     };
10498 }();
10499
10500 /**
10501  * @class Roo.LayoutDialog
10502  * @extends Roo.BasicDialog
10503  * Dialog which provides adjustments for working with a layout in a Dialog.
10504  * Add your necessary layout config options to the dialog's config.<br>
10505  * Example usage (including a nested layout):
10506  * <pre><code>
10507 if(!dialog){
10508     dialog = new Roo.LayoutDialog("download-dlg", {
10509         modal: true,
10510         width:600,
10511         height:450,
10512         shadow:true,
10513         minWidth:500,
10514         minHeight:350,
10515         autoTabs:true,
10516         proxyDrag:true,
10517         // layout config merges with the dialog config
10518         center:{
10519             tabPosition: "top",
10520             alwaysShowTabs: true
10521         }
10522     });
10523     dialog.addKeyListener(27, dialog.hide, dialog);
10524     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10525     dialog.addButton("Build It!", this.getDownload, this);
10526
10527     // we can even add nested layouts
10528     var innerLayout = new Roo.BorderLayout("dl-inner", {
10529         east: {
10530             initialSize: 200,
10531             autoScroll:true,
10532             split:true
10533         },
10534         center: {
10535             autoScroll:true
10536         }
10537     });
10538     innerLayout.beginUpdate();
10539     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10540     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10541     innerLayout.endUpdate(true);
10542
10543     var layout = dialog.getLayout();
10544     layout.beginUpdate();
10545     layout.add("center", new Roo.ContentPanel("standard-panel",
10546                         {title: "Download the Source", fitToFrame:true}));
10547     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10548                {title: "Build your own roo.js"}));
10549     layout.getRegion("center").showPanel(sp);
10550     layout.endUpdate();
10551 }
10552 </code></pre>
10553     * @constructor
10554     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10555     * @param {Object} config configuration options
10556   */
10557 Roo.LayoutDialog = function(el, cfg){
10558     
10559     var config=  cfg;
10560     if (typeof(cfg) == 'undefined') {
10561         config = Roo.apply({}, el);
10562         // not sure why we use documentElement here.. - it should always be body.
10563         // IE7 borks horribly if we use documentElement.
10564         // webkit also does not like documentElement - it creates a body element...
10565         el = Roo.get( document.body || document.documentElement ).createChild();
10566         //config.autoCreate = true;
10567     }
10568     
10569     
10570     config.autoTabs = false;
10571     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10572     this.body.setStyle({overflow:"hidden", position:"relative"});
10573     this.layout = new Roo.BorderLayout(this.body.dom, config);
10574     this.layout.monitorWindowResize = false;
10575     this.el.addClass("x-dlg-auto-layout");
10576     // fix case when center region overwrites center function
10577     this.center = Roo.BasicDialog.prototype.center;
10578     this.on("show", this.layout.layout, this.layout, true);
10579     if (config.items) {
10580         var xitems = config.items;
10581         delete config.items;
10582         Roo.each(xitems, this.addxtype, this);
10583     }
10584     
10585     
10586 };
10587 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10588     /**
10589      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10590      * @deprecated
10591      */
10592     endUpdate : function(){
10593         this.layout.endUpdate();
10594     },
10595
10596     /**
10597      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10598      *  @deprecated
10599      */
10600     beginUpdate : function(){
10601         this.layout.beginUpdate();
10602     },
10603
10604     /**
10605      * Get the BorderLayout for this dialog
10606      * @return {Roo.BorderLayout}
10607      */
10608     getLayout : function(){
10609         return this.layout;
10610     },
10611
10612     showEl : function(){
10613         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10614         if(Roo.isIE7){
10615             this.layout.layout();
10616         }
10617     },
10618
10619     // private
10620     // Use the syncHeightBeforeShow config option to control this automatically
10621     syncBodyHeight : function(){
10622         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10623         if(this.layout){this.layout.layout();}
10624     },
10625     
10626       /**
10627      * Add an xtype element (actually adds to the layout.)
10628      * @return {Object} xdata xtype object data.
10629      */
10630     
10631     addxtype : function(c) {
10632         return this.layout.addxtype(c);
10633     }
10634 });/*
10635  * Based on:
10636  * Ext JS Library 1.1.1
10637  * Copyright(c) 2006-2007, Ext JS, LLC.
10638  *
10639  * Originally Released Under LGPL - original licence link has changed is not relivant.
10640  *
10641  * Fork - LGPL
10642  * <script type="text/javascript">
10643  */
10644  
10645 /**
10646  * @class Roo.MessageBox
10647  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10648  * Example usage:
10649  *<pre><code>
10650 // Basic alert:
10651 Roo.Msg.alert('Status', 'Changes saved successfully.');
10652
10653 // Prompt for user data:
10654 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10655     if (btn == 'ok'){
10656         // process text value...
10657     }
10658 });
10659
10660 // Show a dialog using config options:
10661 Roo.Msg.show({
10662    title:'Save Changes?',
10663    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10664    buttons: Roo.Msg.YESNOCANCEL,
10665    fn: processResult,
10666    animEl: 'elId'
10667 });
10668 </code></pre>
10669  * @singleton
10670  */
10671 Roo.MessageBox = function(){
10672     var dlg, opt, mask, waitTimer;
10673     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10674     var buttons, activeTextEl, bwidth;
10675
10676     // private
10677     var handleButton = function(button){
10678         dlg.hide();
10679         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10680     };
10681
10682     // private
10683     var handleHide = function(){
10684         if(opt && opt.cls){
10685             dlg.el.removeClass(opt.cls);
10686         }
10687         if(waitTimer){
10688             Roo.TaskMgr.stop(waitTimer);
10689             waitTimer = null;
10690         }
10691     };
10692
10693     // private
10694     var updateButtons = function(b){
10695         var width = 0;
10696         if(!b){
10697             buttons["ok"].hide();
10698             buttons["cancel"].hide();
10699             buttons["yes"].hide();
10700             buttons["no"].hide();
10701             dlg.footer.dom.style.display = 'none';
10702             return width;
10703         }
10704         dlg.footer.dom.style.display = '';
10705         for(var k in buttons){
10706             if(typeof buttons[k] != "function"){
10707                 if(b[k]){
10708                     buttons[k].show();
10709                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10710                     width += buttons[k].el.getWidth()+15;
10711                 }else{
10712                     buttons[k].hide();
10713                 }
10714             }
10715         }
10716         return width;
10717     };
10718
10719     // private
10720     var handleEsc = function(d, k, e){
10721         if(opt && opt.closable !== false){
10722             dlg.hide();
10723         }
10724         if(e){
10725             e.stopEvent();
10726         }
10727     };
10728
10729     return {
10730         /**
10731          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10732          * @return {Roo.BasicDialog} The BasicDialog element
10733          */
10734         getDialog : function(){
10735            if(!dlg){
10736                 dlg = new Roo.BasicDialog("x-msg-box", {
10737                     autoCreate : true,
10738                     shadow: true,
10739                     draggable: true,
10740                     resizable:false,
10741                     constraintoviewport:false,
10742                     fixedcenter:true,
10743                     collapsible : false,
10744                     shim:true,
10745                     modal: true,
10746                     width:400, height:100,
10747                     buttonAlign:"center",
10748                     closeClick : function(){
10749                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10750                             handleButton("no");
10751                         }else{
10752                             handleButton("cancel");
10753                         }
10754                     }
10755                 });
10756                 dlg.on("hide", handleHide);
10757                 mask = dlg.mask;
10758                 dlg.addKeyListener(27, handleEsc);
10759                 buttons = {};
10760                 var bt = this.buttonText;
10761                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10762                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10763                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10764                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10765                 bodyEl = dlg.body.createChild({
10766
10767                     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>'
10768                 });
10769                 msgEl = bodyEl.dom.firstChild;
10770                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10771                 textboxEl.enableDisplayMode();
10772                 textboxEl.addKeyListener([10,13], function(){
10773                     if(dlg.isVisible() && opt && opt.buttons){
10774                         if(opt.buttons.ok){
10775                             handleButton("ok");
10776                         }else if(opt.buttons.yes){
10777                             handleButton("yes");
10778                         }
10779                     }
10780                 });
10781                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10782                 textareaEl.enableDisplayMode();
10783                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10784                 progressEl.enableDisplayMode();
10785                 var pf = progressEl.dom.firstChild;
10786                 if (pf) {
10787                     pp = Roo.get(pf.firstChild);
10788                     pp.setHeight(pf.offsetHeight);
10789                 }
10790                 
10791             }
10792             return dlg;
10793         },
10794
10795         /**
10796          * Updates the message box body text
10797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10798          * the XHTML-compliant non-breaking space character '&amp;#160;')
10799          * @return {Roo.MessageBox} This message box
10800          */
10801         updateText : function(text){
10802             if(!dlg.isVisible() && !opt.width){
10803                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10804             }
10805             msgEl.innerHTML = text || '&#160;';
10806       
10807             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10808             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10809             var w = Math.max(
10810                     Math.min(opt.width || cw , this.maxWidth), 
10811                     Math.max(opt.minWidth || this.minWidth, bwidth)
10812             );
10813             if(opt.prompt){
10814                 activeTextEl.setWidth(w);
10815             }
10816             if(dlg.isVisible()){
10817                 dlg.fixedcenter = false;
10818             }
10819             // to big, make it scroll. = But as usual stupid IE does not support
10820             // !important..
10821             
10822             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10823                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10824                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10825             } else {
10826                 bodyEl.dom.style.height = '';
10827                 bodyEl.dom.style.overflowY = '';
10828             }
10829             if (cw > w) {
10830                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10831             } else {
10832                 bodyEl.dom.style.overflowX = '';
10833             }
10834             
10835             dlg.setContentSize(w, bodyEl.getHeight());
10836             if(dlg.isVisible()){
10837                 dlg.fixedcenter = true;
10838             }
10839             return this;
10840         },
10841
10842         /**
10843          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10844          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10845          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10846          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10847          * @return {Roo.MessageBox} This message box
10848          */
10849         updateProgress : function(value, text){
10850             if(text){
10851                 this.updateText(text);
10852             }
10853             if (pp) { // weird bug on my firefox - for some reason this is not defined
10854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10855             }
10856             return this;
10857         },        
10858
10859         /**
10860          * Returns true if the message box is currently displayed
10861          * @return {Boolean} True if the message box is visible, else false
10862          */
10863         isVisible : function(){
10864             return dlg && dlg.isVisible();  
10865         },
10866
10867         /**
10868          * Hides the message box if it is displayed
10869          */
10870         hide : function(){
10871             if(this.isVisible()){
10872                 dlg.hide();
10873             }  
10874         },
10875
10876         /**
10877          * Displays a new message box, or reinitializes an existing message box, based on the config options
10878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10879          * The following config object properties are supported:
10880          * <pre>
10881 Property    Type             Description
10882 ----------  ---------------  ------------------------------------------------------------------------------------
10883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10884                                    closes (defaults to undefined)
10885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10888                                    progress and wait dialogs will ignore this property and always hide the
10889                                    close button as they can only be closed programmatically.
10890 cls               String           A custom CSS class to apply to the message box element
10891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10892                                    displayed (defaults to 75)
10893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10894                                    function will be btn (the name of the button that was clicked, if applicable,
10895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10896                                    Progress and wait dialogs will ignore this option since they do not respond to
10897                                    user actions and can only be closed programmatically, so any required function
10898                                    should be called by the same code after it closes the dialog.
10899 icon              String           A CSS class that provides a background image to be used as an icon for
10900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10903 modal             Boolean          False to allow user interaction with the page while the message box is
10904                                    displayed (defaults to true)
10905 msg               String           A string that will replace the existing message box body text (defaults
10906                                    to the XHTML-compliant non-breaking space character '&#160;')
10907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10908 progress          Boolean          True to display a progress bar (defaults to false)
10909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10912 title             String           The title text
10913 value             String           The string value to set into the active textbox element if displayed
10914 wait              Boolean          True to display a progress bar (defaults to false)
10915 width             Number           The width of the dialog in pixels
10916 </pre>
10917          *
10918          * Example usage:
10919          * <pre><code>
10920 Roo.Msg.show({
10921    title: 'Address',
10922    msg: 'Please enter your address:',
10923    width: 300,
10924    buttons: Roo.MessageBox.OKCANCEL,
10925    multiline: true,
10926    fn: saveAddress,
10927    animEl: 'addAddressBtn'
10928 });
10929 </code></pre>
10930          * @param {Object} config Configuration options
10931          * @return {Roo.MessageBox} This message box
10932          */
10933         show : function(options)
10934         {
10935             
10936             // this causes nightmares if you show one dialog after another
10937             // especially on callbacks..
10938              
10939             if(this.isVisible()){
10940                 
10941                 this.hide();
10942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10944                 Roo.log("New Dialog Message:" +  options.msg )
10945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10947                 
10948             }
10949             var d = this.getDialog();
10950             opt = options;
10951             d.setTitle(opt.title || "&#160;");
10952             d.close.setDisplayed(opt.closable !== false);
10953             activeTextEl = textboxEl;
10954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10955             if(opt.prompt){
10956                 if(opt.multiline){
10957                     textboxEl.hide();
10958                     textareaEl.show();
10959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10960                         opt.multiline : this.defaultTextHeight);
10961                     activeTextEl = textareaEl;
10962                 }else{
10963                     textboxEl.show();
10964                     textareaEl.hide();
10965                 }
10966             }else{
10967                 textboxEl.hide();
10968                 textareaEl.hide();
10969             }
10970             progressEl.setDisplayed(opt.progress === true);
10971             this.updateProgress(0);
10972             activeTextEl.dom.value = opt.value || "";
10973             if(opt.prompt){
10974                 dlg.setDefaultButton(activeTextEl);
10975             }else{
10976                 var bs = opt.buttons;
10977                 var db = null;
10978                 if(bs && bs.ok){
10979                     db = buttons["ok"];
10980                 }else if(bs && bs.yes){
10981                     db = buttons["yes"];
10982                 }
10983                 dlg.setDefaultButton(db);
10984             }
10985             bwidth = updateButtons(opt.buttons);
10986             this.updateText(opt.msg);
10987             if(opt.cls){
10988                 d.el.addClass(opt.cls);
10989             }
10990             d.proxyDrag = opt.proxyDrag === true;
10991             d.modal = opt.modal !== false;
10992             d.mask = opt.modal !== false ? mask : false;
10993             if(!d.isVisible()){
10994                 // force it to the end of the z-index stack so it gets a cursor in FF
10995                 document.body.appendChild(dlg.el.dom);
10996                 d.animateTarget = null;
10997                 d.show(options.animEl);
10998             }
10999             return this;
11000         },
11001
11002         /**
11003          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11004          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11005          * and closing the message box when the process is complete.
11006          * @param {String} title The title bar text
11007          * @param {String} msg The message box body text
11008          * @return {Roo.MessageBox} This message box
11009          */
11010         progress : function(title, msg){
11011             this.show({
11012                 title : title,
11013                 msg : msg,
11014                 buttons: false,
11015                 progress:true,
11016                 closable:false,
11017                 minWidth: this.minProgressWidth,
11018                 modal : true
11019             });
11020             return this;
11021         },
11022
11023         /**
11024          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11025          * If a callback function is passed it will be called after the user clicks the button, and the
11026          * id of the button that was clicked will be passed as the only parameter to the callback
11027          * (could also be the top-right close button).
11028          * @param {String} title The title bar text
11029          * @param {String} msg The message box body text
11030          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11031          * @param {Object} scope (optional) The scope of the callback function
11032          * @return {Roo.MessageBox} This message box
11033          */
11034         alert : function(title, msg, fn, scope){
11035             this.show({
11036                 title : title,
11037                 msg : msg,
11038                 buttons: this.OK,
11039                 fn: fn,
11040                 scope : scope,
11041                 modal : true
11042             });
11043             return this;
11044         },
11045
11046         /**
11047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11049          * You are responsible for closing the message box when the process is complete.
11050          * @param {String} msg The message box body text
11051          * @param {String} title (optional) The title bar text
11052          * @return {Roo.MessageBox} This message box
11053          */
11054         wait : function(msg, title){
11055             this.show({
11056                 title : title,
11057                 msg : msg,
11058                 buttons: false,
11059                 closable:false,
11060                 progress:true,
11061                 modal:true,
11062                 width:300,
11063                 wait:true
11064             });
11065             waitTimer = Roo.TaskMgr.start({
11066                 run: function(i){
11067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11068                 },
11069                 interval: 1000
11070             });
11071             return this;
11072         },
11073
11074         /**
11075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11078          * @param {String} title The title bar text
11079          * @param {String} msg The message box body text
11080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11081          * @param {Object} scope (optional) The scope of the callback function
11082          * @return {Roo.MessageBox} This message box
11083          */
11084         confirm : function(title, msg, fn, scope){
11085             this.show({
11086                 title : title,
11087                 msg : msg,
11088                 buttons: this.YESNO,
11089                 fn: fn,
11090                 scope : scope,
11091                 modal : true
11092             });
11093             return this;
11094         },
11095
11096         /**
11097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11100          * (could also be the top-right close button) and the text that was entered will be passed as the two
11101          * parameters to the callback.
11102          * @param {String} title The title bar text
11103          * @param {String} msg The message box body text
11104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11105          * @param {Object} scope (optional) The scope of the callback function
11106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11108          * @return {Roo.MessageBox} This message box
11109          */
11110         prompt : function(title, msg, fn, scope, multiline){
11111             this.show({
11112                 title : title,
11113                 msg : msg,
11114                 buttons: this.OKCANCEL,
11115                 fn: fn,
11116                 minWidth:250,
11117                 scope : scope,
11118                 prompt:true,
11119                 multiline: multiline,
11120                 modal : true
11121             });
11122             return this;
11123         },
11124
11125         /**
11126          * Button config that displays a single OK button
11127          * @type Object
11128          */
11129         OK : {ok:true},
11130         /**
11131          * Button config that displays Yes and No buttons
11132          * @type Object
11133          */
11134         YESNO : {yes:true, no:true},
11135         /**
11136          * Button config that displays OK and Cancel buttons
11137          * @type Object
11138          */
11139         OKCANCEL : {ok:true, cancel:true},
11140         /**
11141          * Button config that displays Yes, No and Cancel buttons
11142          * @type Object
11143          */
11144         YESNOCANCEL : {yes:true, no:true, cancel:true},
11145
11146         /**
11147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11148          * @type Number
11149          */
11150         defaultTextHeight : 75,
11151         /**
11152          * The maximum width in pixels of the message box (defaults to 600)
11153          * @type Number
11154          */
11155         maxWidth : 600,
11156         /**
11157          * The minimum width in pixels of the message box (defaults to 100)
11158          * @type Number
11159          */
11160         minWidth : 100,
11161         /**
11162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11164          * @type Number
11165          */
11166         minProgressWidth : 250,
11167         /**
11168          * An object containing the default button text strings that can be overriden for localized language support.
11169          * Supported properties are: ok, cancel, yes and no.
11170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11171          * @type Object
11172          */
11173         buttonText : {
11174             ok : "OK",
11175             cancel : "Cancel",
11176             yes : "Yes",
11177             no : "No"
11178         }
11179     };
11180 }();
11181
11182 /**
11183  * Shorthand for {@link Roo.MessageBox}
11184  */
11185 Roo.Msg = Roo.MessageBox;/*
11186  * Based on:
11187  * Ext JS Library 1.1.1
11188  * Copyright(c) 2006-2007, Ext JS, LLC.
11189  *
11190  * Originally Released Under LGPL - original licence link has changed is not relivant.
11191  *
11192  * Fork - LGPL
11193  * <script type="text/javascript">
11194  */
11195 /**
11196  * @class Roo.QuickTips
11197  * Provides attractive and customizable tooltips for any element.
11198  * @singleton
11199  */
11200 Roo.QuickTips = function(){
11201     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11202     var ce, bd, xy, dd;
11203     var visible = false, disabled = true, inited = false;
11204     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11205     
11206     var onOver = function(e){
11207         if(disabled){
11208             return;
11209         }
11210         var t = e.getTarget();
11211         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11212             return;
11213         }
11214         if(ce && t == ce.el){
11215             clearTimeout(hideProc);
11216             return;
11217         }
11218         if(t && tagEls[t.id]){
11219             tagEls[t.id].el = t;
11220             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11221             return;
11222         }
11223         var ttp, et = Roo.fly(t);
11224         var ns = cfg.namespace;
11225         if(tm.interceptTitles && t.title){
11226             ttp = t.title;
11227             t.qtip = ttp;
11228             t.removeAttribute("title");
11229             e.preventDefault();
11230         }else{
11231             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11232         }
11233         if(ttp){
11234             showProc = show.defer(tm.showDelay, tm, [{
11235                 el: t, 
11236                 text: ttp.replace(/\\n/g,'<br/>'),
11237                 width: et.getAttributeNS(ns, cfg.width),
11238                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11239                 title: et.getAttributeNS(ns, cfg.title),
11240                     cls: et.getAttributeNS(ns, cfg.cls)
11241             }]);
11242         }
11243     };
11244     
11245     var onOut = function(e){
11246         clearTimeout(showProc);
11247         var t = e.getTarget();
11248         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11249             hideProc = setTimeout(hide, tm.hideDelay);
11250         }
11251     };
11252     
11253     var onMove = function(e){
11254         if(disabled){
11255             return;
11256         }
11257         xy = e.getXY();
11258         xy[1] += 18;
11259         if(tm.trackMouse && ce){
11260             el.setXY(xy);
11261         }
11262     };
11263     
11264     var onDown = function(e){
11265         clearTimeout(showProc);
11266         clearTimeout(hideProc);
11267         if(!e.within(el)){
11268             if(tm.hideOnClick){
11269                 hide();
11270                 tm.disable();
11271                 tm.enable.defer(100, tm);
11272             }
11273         }
11274     };
11275     
11276     var getPad = function(){
11277         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11278     };
11279
11280     var show = function(o){
11281         if(disabled){
11282             return;
11283         }
11284         clearTimeout(dismissProc);
11285         ce = o;
11286         if(removeCls){ // in case manually hidden
11287             el.removeClass(removeCls);
11288             removeCls = null;
11289         }
11290         if(ce.cls){
11291             el.addClass(ce.cls);
11292             removeCls = ce.cls;
11293         }
11294         if(ce.title){
11295             tipTitle.update(ce.title);
11296             tipTitle.show();
11297         }else{
11298             tipTitle.update('');
11299             tipTitle.hide();
11300         }
11301         el.dom.style.width  = tm.maxWidth+'px';
11302         //tipBody.dom.style.width = '';
11303         tipBodyText.update(o.text);
11304         var p = getPad(), w = ce.width;
11305         if(!w){
11306             var td = tipBodyText.dom;
11307             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11308             if(aw > tm.maxWidth){
11309                 w = tm.maxWidth;
11310             }else if(aw < tm.minWidth){
11311                 w = tm.minWidth;
11312             }else{
11313                 w = aw;
11314             }
11315         }
11316         //tipBody.setWidth(w);
11317         el.setWidth(parseInt(w, 10) + p);
11318         if(ce.autoHide === false){
11319             close.setDisplayed(true);
11320             if(dd){
11321                 dd.unlock();
11322             }
11323         }else{
11324             close.setDisplayed(false);
11325             if(dd){
11326                 dd.lock();
11327             }
11328         }
11329         if(xy){
11330             el.avoidY = xy[1]-18;
11331             el.setXY(xy);
11332         }
11333         if(tm.animate){
11334             el.setOpacity(.1);
11335             el.setStyle("visibility", "visible");
11336             el.fadeIn({callback: afterShow});
11337         }else{
11338             afterShow();
11339         }
11340     };
11341     
11342     var afterShow = function(){
11343         if(ce){
11344             el.show();
11345             esc.enable();
11346             if(tm.autoDismiss && ce.autoHide !== false){
11347                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11348             }
11349         }
11350     };
11351     
11352     var hide = function(noanim){
11353         clearTimeout(dismissProc);
11354         clearTimeout(hideProc);
11355         ce = null;
11356         if(el.isVisible()){
11357             esc.disable();
11358             if(noanim !== true && tm.animate){
11359                 el.fadeOut({callback: afterHide});
11360             }else{
11361                 afterHide();
11362             } 
11363         }
11364     };
11365     
11366     var afterHide = function(){
11367         el.hide();
11368         if(removeCls){
11369             el.removeClass(removeCls);
11370             removeCls = null;
11371         }
11372     };
11373     
11374     return {
11375         /**
11376         * @cfg {Number} minWidth
11377         * The minimum width of the quick tip (defaults to 40)
11378         */
11379        minWidth : 40,
11380         /**
11381         * @cfg {Number} maxWidth
11382         * The maximum width of the quick tip (defaults to 300)
11383         */
11384        maxWidth : 300,
11385         /**
11386         * @cfg {Boolean} interceptTitles
11387         * True to automatically use the element's DOM title value if available (defaults to false)
11388         */
11389        interceptTitles : false,
11390         /**
11391         * @cfg {Boolean} trackMouse
11392         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11393         */
11394        trackMouse : false,
11395         /**
11396         * @cfg {Boolean} hideOnClick
11397         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11398         */
11399        hideOnClick : true,
11400         /**
11401         * @cfg {Number} showDelay
11402         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11403         */
11404        showDelay : 500,
11405         /**
11406         * @cfg {Number} hideDelay
11407         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11408         */
11409        hideDelay : 200,
11410         /**
11411         * @cfg {Boolean} autoHide
11412         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11413         * Used in conjunction with hideDelay.
11414         */
11415        autoHide : true,
11416         /**
11417         * @cfg {Boolean}
11418         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11419         * (defaults to true).  Used in conjunction with autoDismissDelay.
11420         */
11421        autoDismiss : true,
11422         /**
11423         * @cfg {Number}
11424         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11425         */
11426        autoDismissDelay : 5000,
11427        /**
11428         * @cfg {Boolean} animate
11429         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11430         */
11431        animate : false,
11432
11433        /**
11434         * @cfg {String} title
11435         * Title text to display (defaults to '').  This can be any valid HTML markup.
11436         */
11437         title: '',
11438        /**
11439         * @cfg {String} text
11440         * Body text to display (defaults to '').  This can be any valid HTML markup.
11441         */
11442         text : '',
11443        /**
11444         * @cfg {String} cls
11445         * A CSS class to apply to the base quick tip element (defaults to '').
11446         */
11447         cls : '',
11448        /**
11449         * @cfg {Number} width
11450         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11451         * minWidth or maxWidth.
11452         */
11453         width : null,
11454
11455     /**
11456      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11457      * or display QuickTips in a page.
11458      */
11459        init : function(){
11460           tm = Roo.QuickTips;
11461           cfg = tm.tagConfig;
11462           if(!inited){
11463               if(!Roo.isReady){ // allow calling of init() before onReady
11464                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11465                   return;
11466               }
11467               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11468               el.fxDefaults = {stopFx: true};
11469               // maximum custom styling
11470               //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>');
11471               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>');              
11472               tipTitle = el.child('h3');
11473               tipTitle.enableDisplayMode("block");
11474               tipBody = el.child('div.x-tip-bd');
11475               tipBodyText = el.child('div.x-tip-bd-inner');
11476               //bdLeft = el.child('div.x-tip-bd-left');
11477               //bdRight = el.child('div.x-tip-bd-right');
11478               close = el.child('div.x-tip-close');
11479               close.enableDisplayMode("block");
11480               close.on("click", hide);
11481               var d = Roo.get(document);
11482               d.on("mousedown", onDown);
11483               d.on("mouseover", onOver);
11484               d.on("mouseout", onOut);
11485               d.on("mousemove", onMove);
11486               esc = d.addKeyListener(27, hide);
11487               esc.disable();
11488               if(Roo.dd.DD){
11489                   dd = el.initDD("default", null, {
11490                       onDrag : function(){
11491                           el.sync();  
11492                       }
11493                   });
11494                   dd.setHandleElId(tipTitle.id);
11495                   dd.lock();
11496               }
11497               inited = true;
11498           }
11499           this.enable(); 
11500        },
11501
11502     /**
11503      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11504      * are supported:
11505      * <pre>
11506 Property    Type                   Description
11507 ----------  ---------------------  ------------------------------------------------------------------------
11508 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11509      * </ul>
11510      * @param {Object} config The config object
11511      */
11512        register : function(config){
11513            var cs = config instanceof Array ? config : arguments;
11514            for(var i = 0, len = cs.length; i < len; i++) {
11515                var c = cs[i];
11516                var target = c.target;
11517                if(target){
11518                    if(target instanceof Array){
11519                        for(var j = 0, jlen = target.length; j < jlen; j++){
11520                            tagEls[target[j]] = c;
11521                        }
11522                    }else{
11523                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11524                    }
11525                }
11526            }
11527        },
11528
11529     /**
11530      * Removes this quick tip from its element and destroys it.
11531      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11532      */
11533        unregister : function(el){
11534            delete tagEls[Roo.id(el)];
11535        },
11536
11537     /**
11538      * Enable this quick tip.
11539      */
11540        enable : function(){
11541            if(inited && disabled){
11542                locks.pop();
11543                if(locks.length < 1){
11544                    disabled = false;
11545                }
11546            }
11547        },
11548
11549     /**
11550      * Disable this quick tip.
11551      */
11552        disable : function(){
11553           disabled = true;
11554           clearTimeout(showProc);
11555           clearTimeout(hideProc);
11556           clearTimeout(dismissProc);
11557           if(ce){
11558               hide(true);
11559           }
11560           locks.push(1);
11561        },
11562
11563     /**
11564      * Returns true if the quick tip is enabled, else false.
11565      */
11566        isEnabled : function(){
11567             return !disabled;
11568        },
11569
11570         // private
11571        tagConfig : {
11572            namespace : "roo", // was ext?? this may break..
11573            alt_namespace : "ext",
11574            attribute : "qtip",
11575            width : "width",
11576            target : "target",
11577            title : "qtitle",
11578            hide : "hide",
11579            cls : "qclass"
11580        }
11581    };
11582 }();
11583
11584 // backwards compat
11585 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11586  * Based on:
11587  * Ext JS Library 1.1.1
11588  * Copyright(c) 2006-2007, Ext JS, LLC.
11589  *
11590  * Originally Released Under LGPL - original licence link has changed is not relivant.
11591  *
11592  * Fork - LGPL
11593  * <script type="text/javascript">
11594  */
11595  
11596
11597 /**
11598  * @class Roo.tree.TreePanel
11599  * @extends Roo.data.Tree
11600
11601  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11602  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11603  * @cfg {Boolean} enableDD true to enable drag and drop
11604  * @cfg {Boolean} enableDrag true to enable just drag
11605  * @cfg {Boolean} enableDrop true to enable just drop
11606  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11607  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11608  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11609  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11610  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11611  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11612  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11613  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11614  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11615  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11616  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11617  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11618  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11619  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11620  * @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>
11621  * @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>
11622  * 
11623  * @constructor
11624  * @param {String/HTMLElement/Element} el The container element
11625  * @param {Object} config
11626  */
11627 Roo.tree.TreePanel = function(el, config){
11628     var root = false;
11629     var loader = false;
11630     if (config.root) {
11631         root = config.root;
11632         delete config.root;
11633     }
11634     if (config.loader) {
11635         loader = config.loader;
11636         delete config.loader;
11637     }
11638     
11639     Roo.apply(this, config);
11640     Roo.tree.TreePanel.superclass.constructor.call(this);
11641     this.el = Roo.get(el);
11642     this.el.addClass('x-tree');
11643     //console.log(root);
11644     if (root) {
11645         this.setRootNode( Roo.factory(root, Roo.tree));
11646     }
11647     if (loader) {
11648         this.loader = Roo.factory(loader, Roo.tree);
11649     }
11650    /**
11651     * Read-only. The id of the container element becomes this TreePanel's id.
11652     */
11653     this.id = this.el.id;
11654     this.addEvents({
11655         /**
11656         * @event beforeload
11657         * Fires before a node is loaded, return false to cancel
11658         * @param {Node} node The node being loaded
11659         */
11660         "beforeload" : true,
11661         /**
11662         * @event load
11663         * Fires when a node is loaded
11664         * @param {Node} node The node that was loaded
11665         */
11666         "load" : true,
11667         /**
11668         * @event textchange
11669         * Fires when the text for a node is changed
11670         * @param {Node} node The node
11671         * @param {String} text The new text
11672         * @param {String} oldText The old text
11673         */
11674         "textchange" : true,
11675         /**
11676         * @event beforeexpand
11677         * Fires before a node is expanded, return false to cancel.
11678         * @param {Node} node The node
11679         * @param {Boolean} deep
11680         * @param {Boolean} anim
11681         */
11682         "beforeexpand" : true,
11683         /**
11684         * @event beforecollapse
11685         * Fires before a node is collapsed, return false to cancel.
11686         * @param {Node} node The node
11687         * @param {Boolean} deep
11688         * @param {Boolean} anim
11689         */
11690         "beforecollapse" : true,
11691         /**
11692         * @event expand
11693         * Fires when a node is expanded
11694         * @param {Node} node The node
11695         */
11696         "expand" : true,
11697         /**
11698         * @event disabledchange
11699         * Fires when the disabled status of a node changes
11700         * @param {Node} node The node
11701         * @param {Boolean} disabled
11702         */
11703         "disabledchange" : true,
11704         /**
11705         * @event collapse
11706         * Fires when a node is collapsed
11707         * @param {Node} node The node
11708         */
11709         "collapse" : true,
11710         /**
11711         * @event beforeclick
11712         * Fires before click processing on a node. Return false to cancel the default action.
11713         * @param {Node} node The node
11714         * @param {Roo.EventObject} e The event object
11715         */
11716         "beforeclick":true,
11717         /**
11718         * @event checkchange
11719         * Fires when a node with a checkbox's checked property changes
11720         * @param {Node} this This node
11721         * @param {Boolean} checked
11722         */
11723         "checkchange":true,
11724         /**
11725         * @event click
11726         * Fires when a node is clicked
11727         * @param {Node} node The node
11728         * @param {Roo.EventObject} e The event object
11729         */
11730         "click":true,
11731         /**
11732         * @event dblclick
11733         * Fires when a node is double clicked
11734         * @param {Node} node The node
11735         * @param {Roo.EventObject} e The event object
11736         */
11737         "dblclick":true,
11738         /**
11739         * @event contextmenu
11740         * Fires when a node is right clicked
11741         * @param {Node} node The node
11742         * @param {Roo.EventObject} e The event object
11743         */
11744         "contextmenu":true,
11745         /**
11746         * @event beforechildrenrendered
11747         * Fires right before the child nodes for a node are rendered
11748         * @param {Node} node The node
11749         */
11750         "beforechildrenrendered":true,
11751         /**
11752         * @event startdrag
11753         * Fires when a node starts being dragged
11754         * @param {Roo.tree.TreePanel} this
11755         * @param {Roo.tree.TreeNode} node
11756         * @param {event} e The raw browser event
11757         */ 
11758        "startdrag" : true,
11759        /**
11760         * @event enddrag
11761         * Fires when a drag operation is complete
11762         * @param {Roo.tree.TreePanel} this
11763         * @param {Roo.tree.TreeNode} node
11764         * @param {event} e The raw browser event
11765         */
11766        "enddrag" : true,
11767        /**
11768         * @event dragdrop
11769         * Fires when a dragged node is dropped on a valid DD target
11770         * @param {Roo.tree.TreePanel} this
11771         * @param {Roo.tree.TreeNode} node
11772         * @param {DD} dd The dd it was dropped on
11773         * @param {event} e The raw browser event
11774         */
11775        "dragdrop" : true,
11776        /**
11777         * @event beforenodedrop
11778         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11779         * passed to handlers has the following properties:<br />
11780         * <ul style="padding:5px;padding-left:16px;">
11781         * <li>tree - The TreePanel</li>
11782         * <li>target - The node being targeted for the drop</li>
11783         * <li>data - The drag data from the drag source</li>
11784         * <li>point - The point of the drop - append, above or below</li>
11785         * <li>source - The drag source</li>
11786         * <li>rawEvent - Raw mouse event</li>
11787         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11788         * to be inserted by setting them on this object.</li>
11789         * <li>cancel - Set this to true to cancel the drop.</li>
11790         * </ul>
11791         * @param {Object} dropEvent
11792         */
11793        "beforenodedrop" : true,
11794        /**
11795         * @event nodedrop
11796         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11797         * passed to handlers has the following properties:<br />
11798         * <ul style="padding:5px;padding-left:16px;">
11799         * <li>tree - The TreePanel</li>
11800         * <li>target - The node being targeted for the drop</li>
11801         * <li>data - The drag data from the drag source</li>
11802         * <li>point - The point of the drop - append, above or below</li>
11803         * <li>source - The drag source</li>
11804         * <li>rawEvent - Raw mouse event</li>
11805         * <li>dropNode - Dropped node(s).</li>
11806         * </ul>
11807         * @param {Object} dropEvent
11808         */
11809        "nodedrop" : true,
11810         /**
11811         * @event nodedragover
11812         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11813         * passed to handlers has the following properties:<br />
11814         * <ul style="padding:5px;padding-left:16px;">
11815         * <li>tree - The TreePanel</li>
11816         * <li>target - The node being targeted for the drop</li>
11817         * <li>data - The drag data from the drag source</li>
11818         * <li>point - The point of the drop - append, above or below</li>
11819         * <li>source - The drag source</li>
11820         * <li>rawEvent - Raw mouse event</li>
11821         * <li>dropNode - Drop node(s) provided by the source.</li>
11822         * <li>cancel - Set this to true to signal drop not allowed.</li>
11823         * </ul>
11824         * @param {Object} dragOverEvent
11825         */
11826        "nodedragover" : true
11827         
11828     });
11829     if(this.singleExpand){
11830        this.on("beforeexpand", this.restrictExpand, this);
11831     }
11832     if (this.editor) {
11833         this.editor.tree = this;
11834         this.editor = Roo.factory(this.editor, Roo.tree);
11835     }
11836     
11837     if (this.selModel) {
11838         this.selModel = Roo.factory(this.selModel, Roo.tree);
11839     }
11840    
11841 };
11842 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11843     rootVisible : true,
11844     animate: Roo.enableFx,
11845     lines : true,
11846     enableDD : false,
11847     hlDrop : Roo.enableFx,
11848   
11849     renderer: false,
11850     
11851     rendererTip: false,
11852     // private
11853     restrictExpand : function(node){
11854         var p = node.parentNode;
11855         if(p){
11856             if(p.expandedChild && p.expandedChild.parentNode == p){
11857                 p.expandedChild.collapse();
11858             }
11859             p.expandedChild = node;
11860         }
11861     },
11862
11863     // private override
11864     setRootNode : function(node){
11865         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11866         if(!this.rootVisible){
11867             node.ui = new Roo.tree.RootTreeNodeUI(node);
11868         }
11869         return node;
11870     },
11871
11872     /**
11873      * Returns the container element for this TreePanel
11874      */
11875     getEl : function(){
11876         return this.el;
11877     },
11878
11879     /**
11880      * Returns the default TreeLoader for this TreePanel
11881      */
11882     getLoader : function(){
11883         return this.loader;
11884     },
11885
11886     /**
11887      * Expand all nodes
11888      */
11889     expandAll : function(){
11890         this.root.expand(true);
11891     },
11892
11893     /**
11894      * Collapse all nodes
11895      */
11896     collapseAll : function(){
11897         this.root.collapse(true);
11898     },
11899
11900     /**
11901      * Returns the selection model used by this TreePanel
11902      */
11903     getSelectionModel : function(){
11904         if(!this.selModel){
11905             this.selModel = new Roo.tree.DefaultSelectionModel();
11906         }
11907         return this.selModel;
11908     },
11909
11910     /**
11911      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11912      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11913      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11914      * @return {Array}
11915      */
11916     getChecked : function(a, startNode){
11917         startNode = startNode || this.root;
11918         var r = [];
11919         var f = function(){
11920             if(this.attributes.checked){
11921                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11922             }
11923         }
11924         startNode.cascade(f);
11925         return r;
11926     },
11927
11928     /**
11929      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11930      * @param {String} path
11931      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11932      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11933      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11934      */
11935     expandPath : function(path, attr, callback){
11936         attr = attr || "id";
11937         var keys = path.split(this.pathSeparator);
11938         var curNode = this.root;
11939         if(curNode.attributes[attr] != keys[1]){ // invalid root
11940             if(callback){
11941                 callback(false, null);
11942             }
11943             return;
11944         }
11945         var index = 1;
11946         var f = function(){
11947             if(++index == keys.length){
11948                 if(callback){
11949                     callback(true, curNode);
11950                 }
11951                 return;
11952             }
11953             var c = curNode.findChild(attr, keys[index]);
11954             if(!c){
11955                 if(callback){
11956                     callback(false, curNode);
11957                 }
11958                 return;
11959             }
11960             curNode = c;
11961             c.expand(false, false, f);
11962         };
11963         curNode.expand(false, false, f);
11964     },
11965
11966     /**
11967      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11968      * @param {String} path
11969      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11970      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11971      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11972      */
11973     selectPath : function(path, attr, callback){
11974         attr = attr || "id";
11975         var keys = path.split(this.pathSeparator);
11976         var v = keys.pop();
11977         if(keys.length > 0){
11978             var f = function(success, node){
11979                 if(success && node){
11980                     var n = node.findChild(attr, v);
11981                     if(n){
11982                         n.select();
11983                         if(callback){
11984                             callback(true, n);
11985                         }
11986                     }else if(callback){
11987                         callback(false, n);
11988                     }
11989                 }else{
11990                     if(callback){
11991                         callback(false, n);
11992                     }
11993                 }
11994             };
11995             this.expandPath(keys.join(this.pathSeparator), attr, f);
11996         }else{
11997             this.root.select();
11998             if(callback){
11999                 callback(true, this.root);
12000             }
12001         }
12002     },
12003
12004     getTreeEl : function(){
12005         return this.el;
12006     },
12007
12008     /**
12009      * Trigger rendering of this TreePanel
12010      */
12011     render : function(){
12012         if (this.innerCt) {
12013             return this; // stop it rendering more than once!!
12014         }
12015         
12016         this.innerCt = this.el.createChild({tag:"ul",
12017                cls:"x-tree-root-ct " +
12018                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12019
12020         if(this.containerScroll){
12021             Roo.dd.ScrollManager.register(this.el);
12022         }
12023         if((this.enableDD || this.enableDrop) && !this.dropZone){
12024            /**
12025             * The dropZone used by this tree if drop is enabled
12026             * @type Roo.tree.TreeDropZone
12027             */
12028              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12029                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12030            });
12031         }
12032         if((this.enableDD || this.enableDrag) && !this.dragZone){
12033            /**
12034             * The dragZone used by this tree if drag is enabled
12035             * @type Roo.tree.TreeDragZone
12036             */
12037             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12038                ddGroup: this.ddGroup || "TreeDD",
12039                scroll: this.ddScroll
12040            });
12041         }
12042         this.getSelectionModel().init(this);
12043         if (!this.root) {
12044             Roo.log("ROOT not set in tree");
12045             return this;
12046         }
12047         this.root.render();
12048         if(!this.rootVisible){
12049             this.root.renderChildren();
12050         }
12051         return this;
12052     }
12053 });/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064
12065 /**
12066  * @class Roo.tree.DefaultSelectionModel
12067  * @extends Roo.util.Observable
12068  * The default single selection for a TreePanel.
12069  * @param {Object} cfg Configuration
12070  */
12071 Roo.tree.DefaultSelectionModel = function(cfg){
12072    this.selNode = null;
12073    
12074    
12075    
12076    this.addEvents({
12077        /**
12078         * @event selectionchange
12079         * Fires when the selected node changes
12080         * @param {DefaultSelectionModel} this
12081         * @param {TreeNode} node the new selection
12082         */
12083        "selectionchange" : true,
12084
12085        /**
12086         * @event beforeselect
12087         * Fires before the selected node changes, return false to cancel the change
12088         * @param {DefaultSelectionModel} this
12089         * @param {TreeNode} node the new selection
12090         * @param {TreeNode} node the old selection
12091         */
12092        "beforeselect" : true
12093    });
12094    
12095     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12096 };
12097
12098 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12099     init : function(tree){
12100         this.tree = tree;
12101         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12102         tree.on("click", this.onNodeClick, this);
12103     },
12104     
12105     onNodeClick : function(node, e){
12106         if (e.ctrlKey && this.selNode == node)  {
12107             this.unselect(node);
12108             return;
12109         }
12110         this.select(node);
12111     },
12112     
12113     /**
12114      * Select a node.
12115      * @param {TreeNode} node The node to select
12116      * @return {TreeNode} The selected node
12117      */
12118     select : function(node){
12119         var last = this.selNode;
12120         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12121             if(last){
12122                 last.ui.onSelectedChange(false);
12123             }
12124             this.selNode = node;
12125             node.ui.onSelectedChange(true);
12126             this.fireEvent("selectionchange", this, node, last);
12127         }
12128         return node;
12129     },
12130     
12131     /**
12132      * Deselect a node.
12133      * @param {TreeNode} node The node to unselect
12134      */
12135     unselect : function(node){
12136         if(this.selNode == node){
12137             this.clearSelections();
12138         }    
12139     },
12140     
12141     /**
12142      * Clear all selections
12143      */
12144     clearSelections : function(){
12145         var n = this.selNode;
12146         if(n){
12147             n.ui.onSelectedChange(false);
12148             this.selNode = null;
12149             this.fireEvent("selectionchange", this, null);
12150         }
12151         return n;
12152     },
12153     
12154     /**
12155      * Get the selected node
12156      * @return {TreeNode} The selected node
12157      */
12158     getSelectedNode : function(){
12159         return this.selNode;    
12160     },
12161     
12162     /**
12163      * Returns true if the node is selected
12164      * @param {TreeNode} node The node to check
12165      * @return {Boolean}
12166      */
12167     isSelected : function(node){
12168         return this.selNode == node;  
12169     },
12170
12171     /**
12172      * Selects the node above the selected node in the tree, intelligently walking the nodes
12173      * @return TreeNode The new selection
12174      */
12175     selectPrevious : function(){
12176         var s = this.selNode || this.lastSelNode;
12177         if(!s){
12178             return null;
12179         }
12180         var ps = s.previousSibling;
12181         if(ps){
12182             if(!ps.isExpanded() || ps.childNodes.length < 1){
12183                 return this.select(ps);
12184             } else{
12185                 var lc = ps.lastChild;
12186                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12187                     lc = lc.lastChild;
12188                 }
12189                 return this.select(lc);
12190             }
12191         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12192             return this.select(s.parentNode);
12193         }
12194         return null;
12195     },
12196
12197     /**
12198      * Selects the node above the selected node in the tree, intelligently walking the nodes
12199      * @return TreeNode The new selection
12200      */
12201     selectNext : function(){
12202         var s = this.selNode || this.lastSelNode;
12203         if(!s){
12204             return null;
12205         }
12206         if(s.firstChild && s.isExpanded()){
12207              return this.select(s.firstChild);
12208          }else if(s.nextSibling){
12209              return this.select(s.nextSibling);
12210          }else if(s.parentNode){
12211             var newS = null;
12212             s.parentNode.bubble(function(){
12213                 if(this.nextSibling){
12214                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12215                     return false;
12216                 }
12217             });
12218             return newS;
12219          }
12220         return null;
12221     },
12222
12223     onKeyDown : function(e){
12224         var s = this.selNode || this.lastSelNode;
12225         // undesirable, but required
12226         var sm = this;
12227         if(!s){
12228             return;
12229         }
12230         var k = e.getKey();
12231         switch(k){
12232              case e.DOWN:
12233                  e.stopEvent();
12234                  this.selectNext();
12235              break;
12236              case e.UP:
12237                  e.stopEvent();
12238                  this.selectPrevious();
12239              break;
12240              case e.RIGHT:
12241                  e.preventDefault();
12242                  if(s.hasChildNodes()){
12243                      if(!s.isExpanded()){
12244                          s.expand();
12245                      }else if(s.firstChild){
12246                          this.select(s.firstChild, e);
12247                      }
12248                  }
12249              break;
12250              case e.LEFT:
12251                  e.preventDefault();
12252                  if(s.hasChildNodes() && s.isExpanded()){
12253                      s.collapse();
12254                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12255                      this.select(s.parentNode, e);
12256                  }
12257              break;
12258         };
12259     }
12260 });
12261
12262 /**
12263  * @class Roo.tree.MultiSelectionModel
12264  * @extends Roo.util.Observable
12265  * Multi selection for a TreePanel.
12266  * @param {Object} cfg Configuration
12267  */
12268 Roo.tree.MultiSelectionModel = function(){
12269    this.selNodes = [];
12270    this.selMap = {};
12271    this.addEvents({
12272        /**
12273         * @event selectionchange
12274         * Fires when the selected nodes change
12275         * @param {MultiSelectionModel} this
12276         * @param {Array} nodes Array of the selected nodes
12277         */
12278        "selectionchange" : true
12279    });
12280    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12281    
12282 };
12283
12284 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12285     init : function(tree){
12286         this.tree = tree;
12287         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12288         tree.on("click", this.onNodeClick, this);
12289     },
12290     
12291     onNodeClick : function(node, e){
12292         this.select(node, e, e.ctrlKey);
12293     },
12294     
12295     /**
12296      * Select a node.
12297      * @param {TreeNode} node The node to select
12298      * @param {EventObject} e (optional) An event associated with the selection
12299      * @param {Boolean} keepExisting True to retain existing selections
12300      * @return {TreeNode} The selected node
12301      */
12302     select : function(node, e, keepExisting){
12303         if(keepExisting !== true){
12304             this.clearSelections(true);
12305         }
12306         if(this.isSelected(node)){
12307             this.lastSelNode = node;
12308             return node;
12309         }
12310         this.selNodes.push(node);
12311         this.selMap[node.id] = node;
12312         this.lastSelNode = node;
12313         node.ui.onSelectedChange(true);
12314         this.fireEvent("selectionchange", this, this.selNodes);
12315         return node;
12316     },
12317     
12318     /**
12319      * Deselect a node.
12320      * @param {TreeNode} node The node to unselect
12321      */
12322     unselect : function(node){
12323         if(this.selMap[node.id]){
12324             node.ui.onSelectedChange(false);
12325             var sn = this.selNodes;
12326             var index = -1;
12327             if(sn.indexOf){
12328                 index = sn.indexOf(node);
12329             }else{
12330                 for(var i = 0, len = sn.length; i < len; i++){
12331                     if(sn[i] == node){
12332                         index = i;
12333                         break;
12334                     }
12335                 }
12336             }
12337             if(index != -1){
12338                 this.selNodes.splice(index, 1);
12339             }
12340             delete this.selMap[node.id];
12341             this.fireEvent("selectionchange", this, this.selNodes);
12342         }
12343     },
12344     
12345     /**
12346      * Clear all selections
12347      */
12348     clearSelections : function(suppressEvent){
12349         var sn = this.selNodes;
12350         if(sn.length > 0){
12351             for(var i = 0, len = sn.length; i < len; i++){
12352                 sn[i].ui.onSelectedChange(false);
12353             }
12354             this.selNodes = [];
12355             this.selMap = {};
12356             if(suppressEvent !== true){
12357                 this.fireEvent("selectionchange", this, this.selNodes);
12358             }
12359         }
12360     },
12361     
12362     /**
12363      * Returns true if the node is selected
12364      * @param {TreeNode} node The node to check
12365      * @return {Boolean}
12366      */
12367     isSelected : function(node){
12368         return this.selMap[node.id] ? true : false;  
12369     },
12370     
12371     /**
12372      * Returns an array of the selected nodes
12373      * @return {Array}
12374      */
12375     getSelectedNodes : function(){
12376         return this.selNodes;    
12377     },
12378
12379     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12380
12381     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12382
12383     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12384 });/*
12385  * Based on:
12386  * Ext JS Library 1.1.1
12387  * Copyright(c) 2006-2007, Ext JS, LLC.
12388  *
12389  * Originally Released Under LGPL - original licence link has changed is not relivant.
12390  *
12391  * Fork - LGPL
12392  * <script type="text/javascript">
12393  */
12394  
12395 /**
12396  * @class Roo.tree.TreeNode
12397  * @extends Roo.data.Node
12398  * @cfg {String} text The text for this node
12399  * @cfg {Boolean} expanded true to start the node expanded
12400  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12401  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12402  * @cfg {Boolean} disabled true to start the node disabled
12403  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12404  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12405  * @cfg {String} cls A css class to be added to the node
12406  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12407  * @cfg {String} href URL of the link used for the node (defaults to #)
12408  * @cfg {String} hrefTarget target frame for the link
12409  * @cfg {String} qtip An Ext QuickTip for the node
12410  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12411  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12412  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12413  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12414  * (defaults to undefined with no checkbox rendered)
12415  * @constructor
12416  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12417  */
12418 Roo.tree.TreeNode = function(attributes){
12419     attributes = attributes || {};
12420     if(typeof attributes == "string"){
12421         attributes = {text: attributes};
12422     }
12423     this.childrenRendered = false;
12424     this.rendered = false;
12425     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12426     this.expanded = attributes.expanded === true;
12427     this.isTarget = attributes.isTarget !== false;
12428     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12429     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12430
12431     /**
12432      * Read-only. The text for this node. To change it use setText().
12433      * @type String
12434      */
12435     this.text = attributes.text;
12436     /**
12437      * True if this node is disabled.
12438      * @type Boolean
12439      */
12440     this.disabled = attributes.disabled === true;
12441
12442     this.addEvents({
12443         /**
12444         * @event textchange
12445         * Fires when the text for this node is changed
12446         * @param {Node} this This node
12447         * @param {String} text The new text
12448         * @param {String} oldText The old text
12449         */
12450         "textchange" : true,
12451         /**
12452         * @event beforeexpand
12453         * Fires before this node is expanded, return false to cancel.
12454         * @param {Node} this This node
12455         * @param {Boolean} deep
12456         * @param {Boolean} anim
12457         */
12458         "beforeexpand" : true,
12459         /**
12460         * @event beforecollapse
12461         * Fires before this node is collapsed, return false to cancel.
12462         * @param {Node} this This node
12463         * @param {Boolean} deep
12464         * @param {Boolean} anim
12465         */
12466         "beforecollapse" : true,
12467         /**
12468         * @event expand
12469         * Fires when this node is expanded
12470         * @param {Node} this This node
12471         */
12472         "expand" : true,
12473         /**
12474         * @event disabledchange
12475         * Fires when the disabled status of this node changes
12476         * @param {Node} this This node
12477         * @param {Boolean} disabled
12478         */
12479         "disabledchange" : true,
12480         /**
12481         * @event collapse
12482         * Fires when this node is collapsed
12483         * @param {Node} this This node
12484         */
12485         "collapse" : true,
12486         /**
12487         * @event beforeclick
12488         * Fires before click processing. Return false to cancel the default action.
12489         * @param {Node} this This node
12490         * @param {Roo.EventObject} e The event object
12491         */
12492         "beforeclick":true,
12493         /**
12494         * @event checkchange
12495         * Fires when a node with a checkbox's checked property changes
12496         * @param {Node} this This node
12497         * @param {Boolean} checked
12498         */
12499         "checkchange":true,
12500         /**
12501         * @event click
12502         * Fires when this node is clicked
12503         * @param {Node} this This node
12504         * @param {Roo.EventObject} e The event object
12505         */
12506         "click":true,
12507         /**
12508         * @event dblclick
12509         * Fires when this node is double clicked
12510         * @param {Node} this This node
12511         * @param {Roo.EventObject} e The event object
12512         */
12513         "dblclick":true,
12514         /**
12515         * @event contextmenu
12516         * Fires when this node is right clicked
12517         * @param {Node} this This node
12518         * @param {Roo.EventObject} e The event object
12519         */
12520         "contextmenu":true,
12521         /**
12522         * @event beforechildrenrendered
12523         * Fires right before the child nodes for this node are rendered
12524         * @param {Node} this This node
12525         */
12526         "beforechildrenrendered":true
12527     });
12528
12529     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12530
12531     /**
12532      * Read-only. The UI for this node
12533      * @type TreeNodeUI
12534      */
12535     this.ui = new uiClass(this);
12536     
12537     // finally support items[]
12538     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12539         return;
12540     }
12541     
12542     
12543     Roo.each(this.attributes.items, function(c) {
12544         this.appendChild(Roo.factory(c,Roo.Tree));
12545     }, this);
12546     delete this.attributes.items;
12547     
12548     
12549     
12550 };
12551 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12552     preventHScroll: true,
12553     /**
12554      * Returns true if this node is expanded
12555      * @return {Boolean}
12556      */
12557     isExpanded : function(){
12558         return this.expanded;
12559     },
12560
12561     /**
12562      * Returns the UI object for this node
12563      * @return {TreeNodeUI}
12564      */
12565     getUI : function(){
12566         return this.ui;
12567     },
12568
12569     // private override
12570     setFirstChild : function(node){
12571         var of = this.firstChild;
12572         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12573         if(this.childrenRendered && of && node != of){
12574             of.renderIndent(true, true);
12575         }
12576         if(this.rendered){
12577             this.renderIndent(true, true);
12578         }
12579     },
12580
12581     // private override
12582     setLastChild : function(node){
12583         var ol = this.lastChild;
12584         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12585         if(this.childrenRendered && ol && node != ol){
12586             ol.renderIndent(true, true);
12587         }
12588         if(this.rendered){
12589             this.renderIndent(true, true);
12590         }
12591     },
12592
12593     // these methods are overridden to provide lazy rendering support
12594     // private override
12595     appendChild : function()
12596     {
12597         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12598         if(node && this.childrenRendered){
12599             node.render();
12600         }
12601         this.ui.updateExpandIcon();
12602         return node;
12603     },
12604
12605     // private override
12606     removeChild : function(node){
12607         this.ownerTree.getSelectionModel().unselect(node);
12608         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12609         // if it's been rendered remove dom node
12610         if(this.childrenRendered){
12611             node.ui.remove();
12612         }
12613         if(this.childNodes.length < 1){
12614             this.collapse(false, false);
12615         }else{
12616             this.ui.updateExpandIcon();
12617         }
12618         if(!this.firstChild) {
12619             this.childrenRendered = false;
12620         }
12621         return node;
12622     },
12623
12624     // private override
12625     insertBefore : function(node, refNode){
12626         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12627         if(newNode && refNode && this.childrenRendered){
12628             node.render();
12629         }
12630         this.ui.updateExpandIcon();
12631         return newNode;
12632     },
12633
12634     /**
12635      * Sets the text for this node
12636      * @param {String} text
12637      */
12638     setText : function(text){
12639         var oldText = this.text;
12640         this.text = text;
12641         this.attributes.text = text;
12642         if(this.rendered){ // event without subscribing
12643             this.ui.onTextChange(this, text, oldText);
12644         }
12645         this.fireEvent("textchange", this, text, oldText);
12646     },
12647
12648     /**
12649      * Triggers selection of this node
12650      */
12651     select : function(){
12652         this.getOwnerTree().getSelectionModel().select(this);
12653     },
12654
12655     /**
12656      * Triggers deselection of this node
12657      */
12658     unselect : function(){
12659         this.getOwnerTree().getSelectionModel().unselect(this);
12660     },
12661
12662     /**
12663      * Returns true if this node is selected
12664      * @return {Boolean}
12665      */
12666     isSelected : function(){
12667         return this.getOwnerTree().getSelectionModel().isSelected(this);
12668     },
12669
12670     /**
12671      * Expand this node.
12672      * @param {Boolean} deep (optional) True to expand all children as well
12673      * @param {Boolean} anim (optional) false to cancel the default animation
12674      * @param {Function} callback (optional) A callback to be called when
12675      * expanding this node completes (does not wait for deep expand to complete).
12676      * Called with 1 parameter, this node.
12677      */
12678     expand : function(deep, anim, callback){
12679         if(!this.expanded){
12680             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12681                 return;
12682             }
12683             if(!this.childrenRendered){
12684                 this.renderChildren();
12685             }
12686             this.expanded = true;
12687             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12688                 this.ui.animExpand(function(){
12689                     this.fireEvent("expand", this);
12690                     if(typeof callback == "function"){
12691                         callback(this);
12692                     }
12693                     if(deep === true){
12694                         this.expandChildNodes(true);
12695                     }
12696                 }.createDelegate(this));
12697                 return;
12698             }else{
12699                 this.ui.expand();
12700                 this.fireEvent("expand", this);
12701                 if(typeof callback == "function"){
12702                     callback(this);
12703                 }
12704             }
12705         }else{
12706            if(typeof callback == "function"){
12707                callback(this);
12708            }
12709         }
12710         if(deep === true){
12711             this.expandChildNodes(true);
12712         }
12713     },
12714
12715     isHiddenRoot : function(){
12716         return this.isRoot && !this.getOwnerTree().rootVisible;
12717     },
12718
12719     /**
12720      * Collapse this node.
12721      * @param {Boolean} deep (optional) True to collapse all children as well
12722      * @param {Boolean} anim (optional) false to cancel the default animation
12723      */
12724     collapse : function(deep, anim){
12725         if(this.expanded && !this.isHiddenRoot()){
12726             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12727                 return;
12728             }
12729             this.expanded = false;
12730             if((this.getOwnerTree().animate && anim !== false) || anim){
12731                 this.ui.animCollapse(function(){
12732                     this.fireEvent("collapse", this);
12733                     if(deep === true){
12734                         this.collapseChildNodes(true);
12735                     }
12736                 }.createDelegate(this));
12737                 return;
12738             }else{
12739                 this.ui.collapse();
12740                 this.fireEvent("collapse", this);
12741             }
12742         }
12743         if(deep === true){
12744             var cs = this.childNodes;
12745             for(var i = 0, len = cs.length; i < len; i++) {
12746                 cs[i].collapse(true, false);
12747             }
12748         }
12749     },
12750
12751     // private
12752     delayedExpand : function(delay){
12753         if(!this.expandProcId){
12754             this.expandProcId = this.expand.defer(delay, this);
12755         }
12756     },
12757
12758     // private
12759     cancelExpand : function(){
12760         if(this.expandProcId){
12761             clearTimeout(this.expandProcId);
12762         }
12763         this.expandProcId = false;
12764     },
12765
12766     /**
12767      * Toggles expanded/collapsed state of the node
12768      */
12769     toggle : function(){
12770         if(this.expanded){
12771             this.collapse();
12772         }else{
12773             this.expand();
12774         }
12775     },
12776
12777     /**
12778      * Ensures all parent nodes are expanded
12779      */
12780     ensureVisible : function(callback){
12781         var tree = this.getOwnerTree();
12782         tree.expandPath(this.parentNode.getPath(), false, function(){
12783             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12784             Roo.callback(callback);
12785         }.createDelegate(this));
12786     },
12787
12788     /**
12789      * Expand all child nodes
12790      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12791      */
12792     expandChildNodes : function(deep){
12793         var cs = this.childNodes;
12794         for(var i = 0, len = cs.length; i < len; i++) {
12795                 cs[i].expand(deep);
12796         }
12797     },
12798
12799     /**
12800      * Collapse all child nodes
12801      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12802      */
12803     collapseChildNodes : function(deep){
12804         var cs = this.childNodes;
12805         for(var i = 0, len = cs.length; i < len; i++) {
12806                 cs[i].collapse(deep);
12807         }
12808     },
12809
12810     /**
12811      * Disables this node
12812      */
12813     disable : function(){
12814         this.disabled = true;
12815         this.unselect();
12816         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12817             this.ui.onDisableChange(this, true);
12818         }
12819         this.fireEvent("disabledchange", this, true);
12820     },
12821
12822     /**
12823      * Enables this node
12824      */
12825     enable : function(){
12826         this.disabled = false;
12827         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12828             this.ui.onDisableChange(this, false);
12829         }
12830         this.fireEvent("disabledchange", this, false);
12831     },
12832
12833     // private
12834     renderChildren : function(suppressEvent){
12835         if(suppressEvent !== false){
12836             this.fireEvent("beforechildrenrendered", this);
12837         }
12838         var cs = this.childNodes;
12839         for(var i = 0, len = cs.length; i < len; i++){
12840             cs[i].render(true);
12841         }
12842         this.childrenRendered = true;
12843     },
12844
12845     // private
12846     sort : function(fn, scope){
12847         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12848         if(this.childrenRendered){
12849             var cs = this.childNodes;
12850             for(var i = 0, len = cs.length; i < len; i++){
12851                 cs[i].render(true);
12852             }
12853         }
12854     },
12855
12856     // private
12857     render : function(bulkRender){
12858         this.ui.render(bulkRender);
12859         if(!this.rendered){
12860             this.rendered = true;
12861             if(this.expanded){
12862                 this.expanded = false;
12863                 this.expand(false, false);
12864             }
12865         }
12866     },
12867
12868     // private
12869     renderIndent : function(deep, refresh){
12870         if(refresh){
12871             this.ui.childIndent = null;
12872         }
12873         this.ui.renderIndent();
12874         if(deep === true && this.childrenRendered){
12875             var cs = this.childNodes;
12876             for(var i = 0, len = cs.length; i < len; i++){
12877                 cs[i].renderIndent(true, refresh);
12878             }
12879         }
12880     }
12881 });/*
12882  * Based on:
12883  * Ext JS Library 1.1.1
12884  * Copyright(c) 2006-2007, Ext JS, LLC.
12885  *
12886  * Originally Released Under LGPL - original licence link has changed is not relivant.
12887  *
12888  * Fork - LGPL
12889  * <script type="text/javascript">
12890  */
12891  
12892 /**
12893  * @class Roo.tree.AsyncTreeNode
12894  * @extends Roo.tree.TreeNode
12895  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12896  * @constructor
12897  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12898  */
12899  Roo.tree.AsyncTreeNode = function(config){
12900     this.loaded = false;
12901     this.loading = false;
12902     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12903     /**
12904     * @event beforeload
12905     * Fires before this node is loaded, return false to cancel
12906     * @param {Node} this This node
12907     */
12908     this.addEvents({'beforeload':true, 'load': true});
12909     /**
12910     * @event load
12911     * Fires when this node is loaded
12912     * @param {Node} this This node
12913     */
12914     /**
12915      * The loader used by this node (defaults to using the tree's defined loader)
12916      * @type TreeLoader
12917      * @property loader
12918      */
12919 };
12920 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12921     expand : function(deep, anim, callback){
12922         if(this.loading){ // if an async load is already running, waiting til it's done
12923             var timer;
12924             var f = function(){
12925                 if(!this.loading){ // done loading
12926                     clearInterval(timer);
12927                     this.expand(deep, anim, callback);
12928                 }
12929             }.createDelegate(this);
12930             timer = setInterval(f, 200);
12931             return;
12932         }
12933         if(!this.loaded){
12934             if(this.fireEvent("beforeload", this) === false){
12935                 return;
12936             }
12937             this.loading = true;
12938             this.ui.beforeLoad(this);
12939             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12940             if(loader){
12941                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12942                 return;
12943             }
12944         }
12945         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12946     },
12947     
12948     /**
12949      * Returns true if this node is currently loading
12950      * @return {Boolean}
12951      */
12952     isLoading : function(){
12953         return this.loading;  
12954     },
12955     
12956     loadComplete : function(deep, anim, callback){
12957         this.loading = false;
12958         this.loaded = true;
12959         this.ui.afterLoad(this);
12960         this.fireEvent("load", this);
12961         this.expand(deep, anim, callback);
12962     },
12963     
12964     /**
12965      * Returns true if this node has been loaded
12966      * @return {Boolean}
12967      */
12968     isLoaded : function(){
12969         return this.loaded;
12970     },
12971     
12972     hasChildNodes : function(){
12973         if(!this.isLeaf() && !this.loaded){
12974             return true;
12975         }else{
12976             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12977         }
12978     },
12979
12980     /**
12981      * Trigger a reload for this node
12982      * @param {Function} callback
12983      */
12984     reload : function(callback){
12985         this.collapse(false, false);
12986         while(this.firstChild){
12987             this.removeChild(this.firstChild);
12988         }
12989         this.childrenRendered = false;
12990         this.loaded = false;
12991         if(this.isHiddenRoot()){
12992             this.expanded = false;
12993         }
12994         this.expand(false, false, callback);
12995     }
12996 });/*
12997  * Based on:
12998  * Ext JS Library 1.1.1
12999  * Copyright(c) 2006-2007, Ext JS, LLC.
13000  *
13001  * Originally Released Under LGPL - original licence link has changed is not relivant.
13002  *
13003  * Fork - LGPL
13004  * <script type="text/javascript">
13005  */
13006  
13007 /**
13008  * @class Roo.tree.TreeNodeUI
13009  * @constructor
13010  * @param {Object} node The node to render
13011  * The TreeNode UI implementation is separate from the
13012  * tree implementation. Unless you are customizing the tree UI,
13013  * you should never have to use this directly.
13014  */
13015 Roo.tree.TreeNodeUI = function(node){
13016     this.node = node;
13017     this.rendered = false;
13018     this.animating = false;
13019     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13020 };
13021
13022 Roo.tree.TreeNodeUI.prototype = {
13023     removeChild : function(node){
13024         if(this.rendered){
13025             this.ctNode.removeChild(node.ui.getEl());
13026         }
13027     },
13028
13029     beforeLoad : function(){
13030          this.addClass("x-tree-node-loading");
13031     },
13032
13033     afterLoad : function(){
13034          this.removeClass("x-tree-node-loading");
13035     },
13036
13037     onTextChange : function(node, text, oldText){
13038         if(this.rendered){
13039             this.textNode.innerHTML = text;
13040         }
13041     },
13042
13043     onDisableChange : function(node, state){
13044         this.disabled = state;
13045         if(state){
13046             this.addClass("x-tree-node-disabled");
13047         }else{
13048             this.removeClass("x-tree-node-disabled");
13049         }
13050     },
13051
13052     onSelectedChange : function(state){
13053         if(state){
13054             this.focus();
13055             this.addClass("x-tree-selected");
13056         }else{
13057             //this.blur();
13058             this.removeClass("x-tree-selected");
13059         }
13060     },
13061
13062     onMove : function(tree, node, oldParent, newParent, index, refNode){
13063         this.childIndent = null;
13064         if(this.rendered){
13065             var targetNode = newParent.ui.getContainer();
13066             if(!targetNode){//target not rendered
13067                 this.holder = document.createElement("div");
13068                 this.holder.appendChild(this.wrap);
13069                 return;
13070             }
13071             var insertBefore = refNode ? refNode.ui.getEl() : null;
13072             if(insertBefore){
13073                 targetNode.insertBefore(this.wrap, insertBefore);
13074             }else{
13075                 targetNode.appendChild(this.wrap);
13076             }
13077             this.node.renderIndent(true);
13078         }
13079     },
13080
13081     addClass : function(cls){
13082         if(this.elNode){
13083             Roo.fly(this.elNode).addClass(cls);
13084         }
13085     },
13086
13087     removeClass : function(cls){
13088         if(this.elNode){
13089             Roo.fly(this.elNode).removeClass(cls);
13090         }
13091     },
13092
13093     remove : function(){
13094         if(this.rendered){
13095             this.holder = document.createElement("div");
13096             this.holder.appendChild(this.wrap);
13097         }
13098     },
13099
13100     fireEvent : function(){
13101         return this.node.fireEvent.apply(this.node, arguments);
13102     },
13103
13104     initEvents : function(){
13105         this.node.on("move", this.onMove, this);
13106         var E = Roo.EventManager;
13107         var a = this.anchor;
13108
13109         var el = Roo.fly(a, '_treeui');
13110
13111         if(Roo.isOpera){ // opera render bug ignores the CSS
13112             el.setStyle("text-decoration", "none");
13113         }
13114
13115         el.on("click", this.onClick, this);
13116         el.on("dblclick", this.onDblClick, this);
13117
13118         if(this.checkbox){
13119             Roo.EventManager.on(this.checkbox,
13120                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13121         }
13122
13123         el.on("contextmenu", this.onContextMenu, this);
13124
13125         var icon = Roo.fly(this.iconNode);
13126         icon.on("click", this.onClick, this);
13127         icon.on("dblclick", this.onDblClick, this);
13128         icon.on("contextmenu", this.onContextMenu, this);
13129         E.on(this.ecNode, "click", this.ecClick, this, true);
13130
13131         if(this.node.disabled){
13132             this.addClass("x-tree-node-disabled");
13133         }
13134         if(this.node.hidden){
13135             this.addClass("x-tree-node-disabled");
13136         }
13137         var ot = this.node.getOwnerTree();
13138         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13139         if(dd && (!this.node.isRoot || ot.rootVisible)){
13140             Roo.dd.Registry.register(this.elNode, {
13141                 node: this.node,
13142                 handles: this.getDDHandles(),
13143                 isHandle: false
13144             });
13145         }
13146     },
13147
13148     getDDHandles : function(){
13149         return [this.iconNode, this.textNode];
13150     },
13151
13152     hide : function(){
13153         if(this.rendered){
13154             this.wrap.style.display = "none";
13155         }
13156     },
13157
13158     show : function(){
13159         if(this.rendered){
13160             this.wrap.style.display = "";
13161         }
13162     },
13163
13164     onContextMenu : function(e){
13165         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13166             e.preventDefault();
13167             this.focus();
13168             this.fireEvent("contextmenu", this.node, e);
13169         }
13170     },
13171
13172     onClick : function(e){
13173         if(this.dropping){
13174             e.stopEvent();
13175             return;
13176         }
13177         if(this.fireEvent("beforeclick", this.node, e) !== false){
13178             if(!this.disabled && this.node.attributes.href){
13179                 this.fireEvent("click", this.node, e);
13180                 return;
13181             }
13182             e.preventDefault();
13183             if(this.disabled){
13184                 return;
13185             }
13186
13187             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13188                 this.node.toggle();
13189             }
13190
13191             this.fireEvent("click", this.node, e);
13192         }else{
13193             e.stopEvent();
13194         }
13195     },
13196
13197     onDblClick : function(e){
13198         e.preventDefault();
13199         if(this.disabled){
13200             return;
13201         }
13202         if(this.checkbox){
13203             this.toggleCheck();
13204         }
13205         if(!this.animating && this.node.hasChildNodes()){
13206             this.node.toggle();
13207         }
13208         this.fireEvent("dblclick", this.node, e);
13209     },
13210
13211     onCheckChange : function(){
13212         var checked = this.checkbox.checked;
13213         this.node.attributes.checked = checked;
13214         this.fireEvent('checkchange', this.node, checked);
13215     },
13216
13217     ecClick : function(e){
13218         if(!this.animating && this.node.hasChildNodes()){
13219             this.node.toggle();
13220         }
13221     },
13222
13223     startDrop : function(){
13224         this.dropping = true;
13225     },
13226
13227     // delayed drop so the click event doesn't get fired on a drop
13228     endDrop : function(){
13229        setTimeout(function(){
13230            this.dropping = false;
13231        }.createDelegate(this), 50);
13232     },
13233
13234     expand : function(){
13235         this.updateExpandIcon();
13236         this.ctNode.style.display = "";
13237     },
13238
13239     focus : function(){
13240         if(!this.node.preventHScroll){
13241             try{this.anchor.focus();
13242             }catch(e){}
13243         }else if(!Roo.isIE){
13244             try{
13245                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13246                 var l = noscroll.scrollLeft;
13247                 this.anchor.focus();
13248                 noscroll.scrollLeft = l;
13249             }catch(e){}
13250         }
13251     },
13252
13253     toggleCheck : function(value){
13254         var cb = this.checkbox;
13255         if(cb){
13256             cb.checked = (value === undefined ? !cb.checked : value);
13257         }
13258     },
13259
13260     blur : function(){
13261         try{
13262             this.anchor.blur();
13263         }catch(e){}
13264     },
13265
13266     animExpand : function(callback){
13267         var ct = Roo.get(this.ctNode);
13268         ct.stopFx();
13269         if(!this.node.hasChildNodes()){
13270             this.updateExpandIcon();
13271             this.ctNode.style.display = "";
13272             Roo.callback(callback);
13273             return;
13274         }
13275         this.animating = true;
13276         this.updateExpandIcon();
13277
13278         ct.slideIn('t', {
13279            callback : function(){
13280                this.animating = false;
13281                Roo.callback(callback);
13282             },
13283             scope: this,
13284             duration: this.node.ownerTree.duration || .25
13285         });
13286     },
13287
13288     highlight : function(){
13289         var tree = this.node.getOwnerTree();
13290         Roo.fly(this.wrap).highlight(
13291             tree.hlColor || "C3DAF9",
13292             {endColor: tree.hlBaseColor}
13293         );
13294     },
13295
13296     collapse : function(){
13297         this.updateExpandIcon();
13298         this.ctNode.style.display = "none";
13299     },
13300
13301     animCollapse : function(callback){
13302         var ct = Roo.get(this.ctNode);
13303         ct.enableDisplayMode('block');
13304         ct.stopFx();
13305
13306         this.animating = true;
13307         this.updateExpandIcon();
13308
13309         ct.slideOut('t', {
13310             callback : function(){
13311                this.animating = false;
13312                Roo.callback(callback);
13313             },
13314             scope: this,
13315             duration: this.node.ownerTree.duration || .25
13316         });
13317     },
13318
13319     getContainer : function(){
13320         return this.ctNode;
13321     },
13322
13323     getEl : function(){
13324         return this.wrap;
13325     },
13326
13327     appendDDGhost : function(ghostNode){
13328         ghostNode.appendChild(this.elNode.cloneNode(true));
13329     },
13330
13331     getDDRepairXY : function(){
13332         return Roo.lib.Dom.getXY(this.iconNode);
13333     },
13334
13335     onRender : function(){
13336         this.render();
13337     },
13338
13339     render : function(bulkRender){
13340         var n = this.node, a = n.attributes;
13341         var targetNode = n.parentNode ?
13342               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13343
13344         if(!this.rendered){
13345             this.rendered = true;
13346
13347             this.renderElements(n, a, targetNode, bulkRender);
13348
13349             if(a.qtip){
13350                if(this.textNode.setAttributeNS){
13351                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13352                    if(a.qtipTitle){
13353                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13354                    }
13355                }else{
13356                    this.textNode.setAttribute("ext:qtip", a.qtip);
13357                    if(a.qtipTitle){
13358                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13359                    }
13360                }
13361             }else if(a.qtipCfg){
13362                 a.qtipCfg.target = Roo.id(this.textNode);
13363                 Roo.QuickTips.register(a.qtipCfg);
13364             }
13365             this.initEvents();
13366             if(!this.node.expanded){
13367                 this.updateExpandIcon();
13368             }
13369         }else{
13370             if(bulkRender === true) {
13371                 targetNode.appendChild(this.wrap);
13372             }
13373         }
13374     },
13375
13376     renderElements : function(n, a, targetNode, bulkRender)
13377     {
13378         // add some indent caching, this helps performance when rendering a large tree
13379         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13380         var t = n.getOwnerTree();
13381         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13382         if (typeof(n.attributes.html) != 'undefined') {
13383             txt = n.attributes.html;
13384         }
13385         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13386         var cb = typeof a.checked == 'boolean';
13387         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13388         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13389             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13390             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13391             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13392             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13393             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13394              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13395                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13396             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13397             "</li>"];
13398
13399         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13400             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13401                                 n.nextSibling.ui.getEl(), buf.join(""));
13402         }else{
13403             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13404         }
13405
13406         this.elNode = this.wrap.childNodes[0];
13407         this.ctNode = this.wrap.childNodes[1];
13408         var cs = this.elNode.childNodes;
13409         this.indentNode = cs[0];
13410         this.ecNode = cs[1];
13411         this.iconNode = cs[2];
13412         var index = 3;
13413         if(cb){
13414             this.checkbox = cs[3];
13415             index++;
13416         }
13417         this.anchor = cs[index];
13418         this.textNode = cs[index].firstChild;
13419     },
13420
13421     getAnchor : function(){
13422         return this.anchor;
13423     },
13424
13425     getTextEl : function(){
13426         return this.textNode;
13427     },
13428
13429     getIconEl : function(){
13430         return this.iconNode;
13431     },
13432
13433     isChecked : function(){
13434         return this.checkbox ? this.checkbox.checked : false;
13435     },
13436
13437     updateExpandIcon : function(){
13438         if(this.rendered){
13439             var n = this.node, c1, c2;
13440             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13441             var hasChild = n.hasChildNodes();
13442             if(hasChild){
13443                 if(n.expanded){
13444                     cls += "-minus";
13445                     c1 = "x-tree-node-collapsed";
13446                     c2 = "x-tree-node-expanded";
13447                 }else{
13448                     cls += "-plus";
13449                     c1 = "x-tree-node-expanded";
13450                     c2 = "x-tree-node-collapsed";
13451                 }
13452                 if(this.wasLeaf){
13453                     this.removeClass("x-tree-node-leaf");
13454                     this.wasLeaf = false;
13455                 }
13456                 if(this.c1 != c1 || this.c2 != c2){
13457                     Roo.fly(this.elNode).replaceClass(c1, c2);
13458                     this.c1 = c1; this.c2 = c2;
13459                 }
13460             }else{
13461                 // this changes non-leafs into leafs if they have no children.
13462                 // it's not very rational behaviour..
13463                 
13464                 if(!this.wasLeaf && this.node.leaf){
13465                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13466                     delete this.c1;
13467                     delete this.c2;
13468                     this.wasLeaf = true;
13469                 }
13470             }
13471             var ecc = "x-tree-ec-icon "+cls;
13472             if(this.ecc != ecc){
13473                 this.ecNode.className = ecc;
13474                 this.ecc = ecc;
13475             }
13476         }
13477     },
13478
13479     getChildIndent : function(){
13480         if(!this.childIndent){
13481             var buf = [];
13482             var p = this.node;
13483             while(p){
13484                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13485                     if(!p.isLast()) {
13486                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13487                     } else {
13488                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13489                     }
13490                 }
13491                 p = p.parentNode;
13492             }
13493             this.childIndent = buf.join("");
13494         }
13495         return this.childIndent;
13496     },
13497
13498     renderIndent : function(){
13499         if(this.rendered){
13500             var indent = "";
13501             var p = this.node.parentNode;
13502             if(p){
13503                 indent = p.ui.getChildIndent();
13504             }
13505             if(this.indentMarkup != indent){ // don't rerender if not required
13506                 this.indentNode.innerHTML = indent;
13507                 this.indentMarkup = indent;
13508             }
13509             this.updateExpandIcon();
13510         }
13511     }
13512 };
13513
13514 Roo.tree.RootTreeNodeUI = function(){
13515     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13516 };
13517 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13518     render : function(){
13519         if(!this.rendered){
13520             var targetNode = this.node.ownerTree.innerCt.dom;
13521             this.node.expanded = true;
13522             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13523             this.wrap = this.ctNode = targetNode.firstChild;
13524         }
13525     },
13526     collapse : function(){
13527     },
13528     expand : function(){
13529     }
13530 });/*
13531  * Based on:
13532  * Ext JS Library 1.1.1
13533  * Copyright(c) 2006-2007, Ext JS, LLC.
13534  *
13535  * Originally Released Under LGPL - original licence link has changed is not relivant.
13536  *
13537  * Fork - LGPL
13538  * <script type="text/javascript">
13539  */
13540 /**
13541  * @class Roo.tree.TreeLoader
13542  * @extends Roo.util.Observable
13543  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13544  * nodes from a specified URL. The response must be a javascript Array definition
13545  * who's elements are node definition objects. eg:
13546  * <pre><code>
13547 {  success : true,
13548    data :      [
13549    
13550     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13551     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13552     ]
13553 }
13554
13555
13556 </code></pre>
13557  * <br><br>
13558  * The old style respose with just an array is still supported, but not recommended.
13559  * <br><br>
13560  *
13561  * A server request is sent, and child nodes are loaded only when a node is expanded.
13562  * The loading node's id is passed to the server under the parameter name "node" to
13563  * enable the server to produce the correct child nodes.
13564  * <br><br>
13565  * To pass extra parameters, an event handler may be attached to the "beforeload"
13566  * event, and the parameters specified in the TreeLoader's baseParams property:
13567  * <pre><code>
13568     myTreeLoader.on("beforeload", function(treeLoader, node) {
13569         this.baseParams.category = node.attributes.category;
13570     }, this);
13571     
13572 </code></pre>
13573  *
13574  * This would pass an HTTP parameter called "category" to the server containing
13575  * the value of the Node's "category" attribute.
13576  * @constructor
13577  * Creates a new Treeloader.
13578  * @param {Object} config A config object containing config properties.
13579  */
13580 Roo.tree.TreeLoader = function(config){
13581     this.baseParams = {};
13582     this.requestMethod = "POST";
13583     Roo.apply(this, config);
13584
13585     this.addEvents({
13586     
13587         /**
13588          * @event beforeload
13589          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13590          * @param {Object} This TreeLoader object.
13591          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13592          * @param {Object} callback The callback function specified in the {@link #load} call.
13593          */
13594         beforeload : true,
13595         /**
13596          * @event load
13597          * Fires when the node has been successfuly loaded.
13598          * @param {Object} This TreeLoader object.
13599          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13600          * @param {Object} response The response object containing the data from the server.
13601          */
13602         load : true,
13603         /**
13604          * @event loadexception
13605          * Fires if the network request failed.
13606          * @param {Object} This TreeLoader object.
13607          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13608          * @param {Object} response The response object containing the data from the server.
13609          */
13610         loadexception : true,
13611         /**
13612          * @event create
13613          * Fires before a node is created, enabling you to return custom Node types 
13614          * @param {Object} This TreeLoader object.
13615          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13616          */
13617         create : true
13618     });
13619
13620     Roo.tree.TreeLoader.superclass.constructor.call(this);
13621 };
13622
13623 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13624     /**
13625     * @cfg {String} dataUrl The URL from which to request a Json string which
13626     * specifies an array of node definition object representing the child nodes
13627     * to be loaded.
13628     */
13629     /**
13630     * @cfg {String} requestMethod either GET or POST
13631     * defaults to POST (due to BC)
13632     * to be loaded.
13633     */
13634     /**
13635     * @cfg {Object} baseParams (optional) An object containing properties which
13636     * specify HTTP parameters to be passed to each request for child nodes.
13637     */
13638     /**
13639     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13640     * created by this loader. If the attributes sent by the server have an attribute in this object,
13641     * they take priority.
13642     */
13643     /**
13644     * @cfg {Object} uiProviders (optional) An object containing properties which
13645     * 
13646     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13647     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13648     * <i>uiProvider</i> attribute of a returned child node is a string rather
13649     * than a reference to a TreeNodeUI implementation, this that string value
13650     * is used as a property name in the uiProviders object. You can define the provider named
13651     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13652     */
13653     uiProviders : {},
13654
13655     /**
13656     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13657     * child nodes before loading.
13658     */
13659     clearOnLoad : true,
13660
13661     /**
13662     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13663     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13664     * Grid query { data : [ .....] }
13665     */
13666     
13667     root : false,
13668      /**
13669     * @cfg {String} queryParam (optional) 
13670     * Name of the query as it will be passed on the querystring (defaults to 'node')
13671     * eg. the request will be ?node=[id]
13672     */
13673     
13674     
13675     queryParam: false,
13676     
13677     /**
13678      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13679      * This is called automatically when a node is expanded, but may be used to reload
13680      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13681      * @param {Roo.tree.TreeNode} node
13682      * @param {Function} callback
13683      */
13684     load : function(node, callback){
13685         if(this.clearOnLoad){
13686             while(node.firstChild){
13687                 node.removeChild(node.firstChild);
13688             }
13689         }
13690         if(node.attributes.children){ // preloaded json children
13691             var cs = node.attributes.children;
13692             for(var i = 0, len = cs.length; i < len; i++){
13693                 node.appendChild(this.createNode(cs[i]));
13694             }
13695             if(typeof callback == "function"){
13696                 callback();
13697             }
13698         }else if(this.dataUrl){
13699             this.requestData(node, callback);
13700         }
13701     },
13702
13703     getParams: function(node){
13704         var buf = [], bp = this.baseParams;
13705         for(var key in bp){
13706             if(typeof bp[key] != "function"){
13707                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13708             }
13709         }
13710         var n = this.queryParam === false ? 'node' : this.queryParam;
13711         buf.push(n + "=", encodeURIComponent(node.id));
13712         return buf.join("");
13713     },
13714
13715     requestData : function(node, callback){
13716         if(this.fireEvent("beforeload", this, node, callback) !== false){
13717             this.transId = Roo.Ajax.request({
13718                 method:this.requestMethod,
13719                 url: this.dataUrl||this.url,
13720                 success: this.handleResponse,
13721                 failure: this.handleFailure,
13722                 scope: this,
13723                 argument: {callback: callback, node: node},
13724                 params: this.getParams(node)
13725             });
13726         }else{
13727             // if the load is cancelled, make sure we notify
13728             // the node that we are done
13729             if(typeof callback == "function"){
13730                 callback();
13731             }
13732         }
13733     },
13734
13735     isLoading : function(){
13736         return this.transId ? true : false;
13737     },
13738
13739     abort : function(){
13740         if(this.isLoading()){
13741             Roo.Ajax.abort(this.transId);
13742         }
13743     },
13744
13745     // private
13746     createNode : function(attr)
13747     {
13748         // apply baseAttrs, nice idea Corey!
13749         if(this.baseAttrs){
13750             Roo.applyIf(attr, this.baseAttrs);
13751         }
13752         if(this.applyLoader !== false){
13753             attr.loader = this;
13754         }
13755         // uiProvider = depreciated..
13756         
13757         if(typeof(attr.uiProvider) == 'string'){
13758            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13759                 /**  eval:var:attr */ eval(attr.uiProvider);
13760         }
13761         if(typeof(this.uiProviders['default']) != 'undefined') {
13762             attr.uiProvider = this.uiProviders['default'];
13763         }
13764         
13765         this.fireEvent('create', this, attr);
13766         
13767         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13768         return(attr.leaf ?
13769                         new Roo.tree.TreeNode(attr) :
13770                         new Roo.tree.AsyncTreeNode(attr));
13771     },
13772
13773     processResponse : function(response, node, callback)
13774     {
13775         var json = response.responseText;
13776         try {
13777             
13778             var o = Roo.decode(json);
13779             
13780             if (this.root === false && typeof(o.success) != undefined) {
13781                 this.root = 'data'; // the default behaviour for list like data..
13782                 }
13783                 
13784             if (this.root !== false &&  !o.success) {
13785                 // it's a failure condition.
13786                 var a = response.argument;
13787                 this.fireEvent("loadexception", this, a.node, response);
13788                 Roo.log("Load failed - should have a handler really");
13789                 return;
13790             }
13791             
13792             
13793             
13794             if (this.root !== false) {
13795                  o = o[this.root];
13796             }
13797             
13798             for(var i = 0, len = o.length; i < len; i++){
13799                 var n = this.createNode(o[i]);
13800                 if(n){
13801                     node.appendChild(n);
13802                 }
13803             }
13804             if(typeof callback == "function"){
13805                 callback(this, node);
13806             }
13807         }catch(e){
13808             this.handleFailure(response);
13809         }
13810     },
13811
13812     handleResponse : function(response){
13813         this.transId = false;
13814         var a = response.argument;
13815         this.processResponse(response, a.node, a.callback);
13816         this.fireEvent("load", this, a.node, response);
13817     },
13818
13819     handleFailure : function(response)
13820     {
13821         // should handle failure better..
13822         this.transId = false;
13823         var a = response.argument;
13824         this.fireEvent("loadexception", this, a.node, response);
13825         if(typeof a.callback == "function"){
13826             a.callback(this, a.node);
13827         }
13828     }
13829 });/*
13830  * Based on:
13831  * Ext JS Library 1.1.1
13832  * Copyright(c) 2006-2007, Ext JS, LLC.
13833  *
13834  * Originally Released Under LGPL - original licence link has changed is not relivant.
13835  *
13836  * Fork - LGPL
13837  * <script type="text/javascript">
13838  */
13839
13840 /**
13841 * @class Roo.tree.TreeFilter
13842 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13843 * @param {TreePanel} tree
13844 * @param {Object} config (optional)
13845  */
13846 Roo.tree.TreeFilter = function(tree, config){
13847     this.tree = tree;
13848     this.filtered = {};
13849     Roo.apply(this, config);
13850 };
13851
13852 Roo.tree.TreeFilter.prototype = {
13853     clearBlank:false,
13854     reverse:false,
13855     autoClear:false,
13856     remove:false,
13857
13858      /**
13859      * Filter the data by a specific attribute.
13860      * @param {String/RegExp} value Either string that the attribute value
13861      * should start with or a RegExp to test against the attribute
13862      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13863      * @param {TreeNode} startNode (optional) The node to start the filter at.
13864      */
13865     filter : function(value, attr, startNode){
13866         attr = attr || "text";
13867         var f;
13868         if(typeof value == "string"){
13869             var vlen = value.length;
13870             // auto clear empty filter
13871             if(vlen == 0 && this.clearBlank){
13872                 this.clear();
13873                 return;
13874             }
13875             value = value.toLowerCase();
13876             f = function(n){
13877                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13878             };
13879         }else if(value.exec){ // regex?
13880             f = function(n){
13881                 return value.test(n.attributes[attr]);
13882             };
13883         }else{
13884             throw 'Illegal filter type, must be string or regex';
13885         }
13886         this.filterBy(f, null, startNode);
13887         },
13888
13889     /**
13890      * Filter by a function. The passed function will be called with each
13891      * node in the tree (or from the startNode). If the function returns true, the node is kept
13892      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13893      * @param {Function} fn The filter function
13894      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13895      */
13896     filterBy : function(fn, scope, startNode){
13897         startNode = startNode || this.tree.root;
13898         if(this.autoClear){
13899             this.clear();
13900         }
13901         var af = this.filtered, rv = this.reverse;
13902         var f = function(n){
13903             if(n == startNode){
13904                 return true;
13905             }
13906             if(af[n.id]){
13907                 return false;
13908             }
13909             var m = fn.call(scope || n, n);
13910             if(!m || rv){
13911                 af[n.id] = n;
13912                 n.ui.hide();
13913                 return false;
13914             }
13915             return true;
13916         };
13917         startNode.cascade(f);
13918         if(this.remove){
13919            for(var id in af){
13920                if(typeof id != "function"){
13921                    var n = af[id];
13922                    if(n && n.parentNode){
13923                        n.parentNode.removeChild(n);
13924                    }
13925                }
13926            }
13927         }
13928     },
13929
13930     /**
13931      * Clears the current filter. Note: with the "remove" option
13932      * set a filter cannot be cleared.
13933      */
13934     clear : function(){
13935         var t = this.tree;
13936         var af = this.filtered;
13937         for(var id in af){
13938             if(typeof id != "function"){
13939                 var n = af[id];
13940                 if(n){
13941                     n.ui.show();
13942                 }
13943             }
13944         }
13945         this.filtered = {};
13946     }
13947 };
13948 /*
13949  * Based on:
13950  * Ext JS Library 1.1.1
13951  * Copyright(c) 2006-2007, Ext JS, LLC.
13952  *
13953  * Originally Released Under LGPL - original licence link has changed is not relivant.
13954  *
13955  * Fork - LGPL
13956  * <script type="text/javascript">
13957  */
13958  
13959
13960 /**
13961  * @class Roo.tree.TreeSorter
13962  * Provides sorting of nodes in a TreePanel
13963  * 
13964  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13965  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13966  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13967  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13968  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13969  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13970  * @constructor
13971  * @param {TreePanel} tree
13972  * @param {Object} config
13973  */
13974 Roo.tree.TreeSorter = function(tree, config){
13975     Roo.apply(this, config);
13976     tree.on("beforechildrenrendered", this.doSort, this);
13977     tree.on("append", this.updateSort, this);
13978     tree.on("insert", this.updateSort, this);
13979     
13980     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13981     var p = this.property || "text";
13982     var sortType = this.sortType;
13983     var fs = this.folderSort;
13984     var cs = this.caseSensitive === true;
13985     var leafAttr = this.leafAttr || 'leaf';
13986
13987     this.sortFn = function(n1, n2){
13988         if(fs){
13989             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13990                 return 1;
13991             }
13992             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13993                 return -1;
13994             }
13995         }
13996         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13997         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13998         if(v1 < v2){
13999                         return dsc ? +1 : -1;
14000                 }else if(v1 > v2){
14001                         return dsc ? -1 : +1;
14002         }else{
14003                 return 0;
14004         }
14005     };
14006 };
14007
14008 Roo.tree.TreeSorter.prototype = {
14009     doSort : function(node){
14010         node.sort(this.sortFn);
14011     },
14012     
14013     compareNodes : function(n1, n2){
14014         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14015     },
14016     
14017     updateSort : function(tree, node){
14018         if(node.childrenRendered){
14019             this.doSort.defer(1, this, [node]);
14020         }
14021     }
14022 };/*
14023  * Based on:
14024  * Ext JS Library 1.1.1
14025  * Copyright(c) 2006-2007, Ext JS, LLC.
14026  *
14027  * Originally Released Under LGPL - original licence link has changed is not relivant.
14028  *
14029  * Fork - LGPL
14030  * <script type="text/javascript">
14031  */
14032
14033 if(Roo.dd.DropZone){
14034     
14035 Roo.tree.TreeDropZone = function(tree, config){
14036     this.allowParentInsert = false;
14037     this.allowContainerDrop = false;
14038     this.appendOnly = false;
14039     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14040     this.tree = tree;
14041     this.lastInsertClass = "x-tree-no-status";
14042     this.dragOverData = {};
14043 };
14044
14045 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14046     ddGroup : "TreeDD",
14047     scroll:  true,
14048     
14049     expandDelay : 1000,
14050     
14051     expandNode : function(node){
14052         if(node.hasChildNodes() && !node.isExpanded()){
14053             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14054         }
14055     },
14056     
14057     queueExpand : function(node){
14058         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14059     },
14060     
14061     cancelExpand : function(){
14062         if(this.expandProcId){
14063             clearTimeout(this.expandProcId);
14064             this.expandProcId = false;
14065         }
14066     },
14067     
14068     isValidDropPoint : function(n, pt, dd, e, data){
14069         if(!n || !data){ return false; }
14070         var targetNode = n.node;
14071         var dropNode = data.node;
14072         // default drop rules
14073         if(!(targetNode && targetNode.isTarget && pt)){
14074             return false;
14075         }
14076         if(pt == "append" && targetNode.allowChildren === false){
14077             return false;
14078         }
14079         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14080             return false;
14081         }
14082         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14083             return false;
14084         }
14085         // reuse the object
14086         var overEvent = this.dragOverData;
14087         overEvent.tree = this.tree;
14088         overEvent.target = targetNode;
14089         overEvent.data = data;
14090         overEvent.point = pt;
14091         overEvent.source = dd;
14092         overEvent.rawEvent = e;
14093         overEvent.dropNode = dropNode;
14094         overEvent.cancel = false;  
14095         var result = this.tree.fireEvent("nodedragover", overEvent);
14096         return overEvent.cancel === false && result !== false;
14097     },
14098     
14099     getDropPoint : function(e, n, dd)
14100     {
14101         var tn = n.node;
14102         if(tn.isRoot){
14103             return tn.allowChildren !== false ? "append" : false; // always append for root
14104         }
14105         var dragEl = n.ddel;
14106         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14107         var y = Roo.lib.Event.getPageY(e);
14108         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14109         
14110         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14111         var noAppend = tn.allowChildren === false;
14112         if(this.appendOnly || tn.parentNode.allowChildren === false){
14113             return noAppend ? false : "append";
14114         }
14115         var noBelow = false;
14116         if(!this.allowParentInsert){
14117             noBelow = tn.hasChildNodes() && tn.isExpanded();
14118         }
14119         var q = (b - t) / (noAppend ? 2 : 3);
14120         if(y >= t && y < (t + q)){
14121             return "above";
14122         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14123             return "below";
14124         }else{
14125             return "append";
14126         }
14127     },
14128     
14129     onNodeEnter : function(n, dd, e, data)
14130     {
14131         this.cancelExpand();
14132     },
14133     
14134     onNodeOver : function(n, dd, e, data)
14135     {
14136        
14137         var pt = this.getDropPoint(e, n, dd);
14138         var node = n.node;
14139         
14140         // auto node expand check
14141         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14142             this.queueExpand(node);
14143         }else if(pt != "append"){
14144             this.cancelExpand();
14145         }
14146         
14147         // set the insert point style on the target node
14148         var returnCls = this.dropNotAllowed;
14149         if(this.isValidDropPoint(n, pt, dd, e, data)){
14150            if(pt){
14151                var el = n.ddel;
14152                var cls;
14153                if(pt == "above"){
14154                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14155                    cls = "x-tree-drag-insert-above";
14156                }else if(pt == "below"){
14157                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14158                    cls = "x-tree-drag-insert-below";
14159                }else{
14160                    returnCls = "x-tree-drop-ok-append";
14161                    cls = "x-tree-drag-append";
14162                }
14163                if(this.lastInsertClass != cls){
14164                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14165                    this.lastInsertClass = cls;
14166                }
14167            }
14168        }
14169        return returnCls;
14170     },
14171     
14172     onNodeOut : function(n, dd, e, data){
14173         
14174         this.cancelExpand();
14175         this.removeDropIndicators(n);
14176     },
14177     
14178     onNodeDrop : function(n, dd, e, data){
14179         var point = this.getDropPoint(e, n, dd);
14180         var targetNode = n.node;
14181         targetNode.ui.startDrop();
14182         if(!this.isValidDropPoint(n, point, dd, e, data)){
14183             targetNode.ui.endDrop();
14184             return false;
14185         }
14186         // first try to find the drop node
14187         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14188         var dropEvent = {
14189             tree : this.tree,
14190             target: targetNode,
14191             data: data,
14192             point: point,
14193             source: dd,
14194             rawEvent: e,
14195             dropNode: dropNode,
14196             cancel: !dropNode   
14197         };
14198         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14199         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14200             targetNode.ui.endDrop();
14201             return false;
14202         }
14203         // allow target changing
14204         targetNode = dropEvent.target;
14205         if(point == "append" && !targetNode.isExpanded()){
14206             targetNode.expand(false, null, function(){
14207                 this.completeDrop(dropEvent);
14208             }.createDelegate(this));
14209         }else{
14210             this.completeDrop(dropEvent);
14211         }
14212         return true;
14213     },
14214     
14215     completeDrop : function(de){
14216         var ns = de.dropNode, p = de.point, t = de.target;
14217         if(!(ns instanceof Array)){
14218             ns = [ns];
14219         }
14220         var n;
14221         for(var i = 0, len = ns.length; i < len; i++){
14222             n = ns[i];
14223             if(p == "above"){
14224                 t.parentNode.insertBefore(n, t);
14225             }else if(p == "below"){
14226                 t.parentNode.insertBefore(n, t.nextSibling);
14227             }else{
14228                 t.appendChild(n);
14229             }
14230         }
14231         n.ui.focus();
14232         if(this.tree.hlDrop){
14233             n.ui.highlight();
14234         }
14235         t.ui.endDrop();
14236         this.tree.fireEvent("nodedrop", de);
14237     },
14238     
14239     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14240         if(this.tree.hlDrop){
14241             dropNode.ui.focus();
14242             dropNode.ui.highlight();
14243         }
14244         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14245     },
14246     
14247     getTree : function(){
14248         return this.tree;
14249     },
14250     
14251     removeDropIndicators : function(n){
14252         if(n && n.ddel){
14253             var el = n.ddel;
14254             Roo.fly(el).removeClass([
14255                     "x-tree-drag-insert-above",
14256                     "x-tree-drag-insert-below",
14257                     "x-tree-drag-append"]);
14258             this.lastInsertClass = "_noclass";
14259         }
14260     },
14261     
14262     beforeDragDrop : function(target, e, id){
14263         this.cancelExpand();
14264         return true;
14265     },
14266     
14267     afterRepair : function(data){
14268         if(data && Roo.enableFx){
14269             data.node.ui.highlight();
14270         }
14271         this.hideProxy();
14272     } 
14273     
14274 });
14275
14276 }
14277 /*
14278  * Based on:
14279  * Ext JS Library 1.1.1
14280  * Copyright(c) 2006-2007, Ext JS, LLC.
14281  *
14282  * Originally Released Under LGPL - original licence link has changed is not relivant.
14283  *
14284  * Fork - LGPL
14285  * <script type="text/javascript">
14286  */
14287  
14288
14289 if(Roo.dd.DragZone){
14290 Roo.tree.TreeDragZone = function(tree, config){
14291     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14292     this.tree = tree;
14293 };
14294
14295 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14296     ddGroup : "TreeDD",
14297    
14298     onBeforeDrag : function(data, e){
14299         var n = data.node;
14300         return n && n.draggable && !n.disabled;
14301     },
14302      
14303     
14304     onInitDrag : function(e){
14305         var data = this.dragData;
14306         this.tree.getSelectionModel().select(data.node);
14307         this.proxy.update("");
14308         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14309         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14310     },
14311     
14312     getRepairXY : function(e, data){
14313         return data.node.ui.getDDRepairXY();
14314     },
14315     
14316     onEndDrag : function(data, e){
14317         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14318         
14319         
14320     },
14321     
14322     onValidDrop : function(dd, e, id){
14323         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14324         this.hideProxy();
14325     },
14326     
14327     beforeInvalidDrop : function(e, id){
14328         // this scrolls the original position back into view
14329         var sm = this.tree.getSelectionModel();
14330         sm.clearSelections();
14331         sm.select(this.dragData.node);
14332     }
14333 });
14334 }/*
14335  * Based on:
14336  * Ext JS Library 1.1.1
14337  * Copyright(c) 2006-2007, Ext JS, LLC.
14338  *
14339  * Originally Released Under LGPL - original licence link has changed is not relivant.
14340  *
14341  * Fork - LGPL
14342  * <script type="text/javascript">
14343  */
14344 /**
14345  * @class Roo.tree.TreeEditor
14346  * @extends Roo.Editor
14347  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14348  * as the editor field.
14349  * @constructor
14350  * @param {Object} config (used to be the tree panel.)
14351  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14352  * 
14353  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14354  * @cfg {Roo.form.TextField|Object} field The field configuration
14355  *
14356  * 
14357  */
14358 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14359     var tree = config;
14360     var field;
14361     if (oldconfig) { // old style..
14362         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14363     } else {
14364         // new style..
14365         tree = config.tree;
14366         config.field = config.field  || {};
14367         config.field.xtype = 'TextField';
14368         field = Roo.factory(config.field, Roo.form);
14369     }
14370     config = config || {};
14371     
14372     
14373     this.addEvents({
14374         /**
14375          * @event beforenodeedit
14376          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14377          * false from the handler of this event.
14378          * @param {Editor} this
14379          * @param {Roo.tree.Node} node 
14380          */
14381         "beforenodeedit" : true
14382     });
14383     
14384     //Roo.log(config);
14385     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14386
14387     this.tree = tree;
14388
14389     tree.on('beforeclick', this.beforeNodeClick, this);
14390     tree.getTreeEl().on('mousedown', this.hide, this);
14391     this.on('complete', this.updateNode, this);
14392     this.on('beforestartedit', this.fitToTree, this);
14393     this.on('startedit', this.bindScroll, this, {delay:10});
14394     this.on('specialkey', this.onSpecialKey, this);
14395 };
14396
14397 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14398     /**
14399      * @cfg {String} alignment
14400      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14401      */
14402     alignment: "l-l",
14403     // inherit
14404     autoSize: false,
14405     /**
14406      * @cfg {Boolean} hideEl
14407      * True to hide the bound element while the editor is displayed (defaults to false)
14408      */
14409     hideEl : false,
14410     /**
14411      * @cfg {String} cls
14412      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14413      */
14414     cls: "x-small-editor x-tree-editor",
14415     /**
14416      * @cfg {Boolean} shim
14417      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14418      */
14419     shim:false,
14420     // inherit
14421     shadow:"frame",
14422     /**
14423      * @cfg {Number} maxWidth
14424      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14425      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14426      * scroll and client offsets into account prior to each edit.
14427      */
14428     maxWidth: 250,
14429
14430     editDelay : 350,
14431
14432     // private
14433     fitToTree : function(ed, el){
14434         var td = this.tree.getTreeEl().dom, nd = el.dom;
14435         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14436             td.scrollLeft = nd.offsetLeft;
14437         }
14438         var w = Math.min(
14439                 this.maxWidth,
14440                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14441         this.setSize(w, '');
14442         
14443         return this.fireEvent('beforenodeedit', this, this.editNode);
14444         
14445     },
14446
14447     // private
14448     triggerEdit : function(node){
14449         this.completeEdit();
14450         this.editNode = node;
14451         this.startEdit(node.ui.textNode, node.text);
14452     },
14453
14454     // private
14455     bindScroll : function(){
14456         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14457     },
14458
14459     // private
14460     beforeNodeClick : function(node, e){
14461         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14462         this.lastClick = new Date();
14463         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14464             e.stopEvent();
14465             this.triggerEdit(node);
14466             return false;
14467         }
14468         return true;
14469     },
14470
14471     // private
14472     updateNode : function(ed, value){
14473         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14474         this.editNode.setText(value);
14475     },
14476
14477     // private
14478     onHide : function(){
14479         Roo.tree.TreeEditor.superclass.onHide.call(this);
14480         if(this.editNode){
14481             this.editNode.ui.focus();
14482         }
14483     },
14484
14485     // private
14486     onSpecialKey : function(field, e){
14487         var k = e.getKey();
14488         if(k == e.ESC){
14489             e.stopEvent();
14490             this.cancelEdit();
14491         }else if(k == e.ENTER && !e.hasModifier()){
14492             e.stopEvent();
14493             this.completeEdit();
14494         }
14495     }
14496 });//<Script type="text/javascript">
14497 /*
14498  * Based on:
14499  * Ext JS Library 1.1.1
14500  * Copyright(c) 2006-2007, Ext JS, LLC.
14501  *
14502  * Originally Released Under LGPL - original licence link has changed is not relivant.
14503  *
14504  * Fork - LGPL
14505  * <script type="text/javascript">
14506  */
14507  
14508 /**
14509  * Not documented??? - probably should be...
14510  */
14511
14512 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14513     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14514     
14515     renderElements : function(n, a, targetNode, bulkRender){
14516         //consel.log("renderElements?");
14517         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14518
14519         var t = n.getOwnerTree();
14520         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14521         
14522         var cols = t.columns;
14523         var bw = t.borderWidth;
14524         var c = cols[0];
14525         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14526          var cb = typeof a.checked == "boolean";
14527         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14528         var colcls = 'x-t-' + tid + '-c0';
14529         var buf = [
14530             '<li class="x-tree-node">',
14531             
14532                 
14533                 '<div class="x-tree-node-el ', a.cls,'">',
14534                     // extran...
14535                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14536                 
14537                 
14538                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14539                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14540                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14541                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14542                            (a.iconCls ? ' '+a.iconCls : ''),
14543                            '" unselectable="on" />',
14544                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14545                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14546                              
14547                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14548                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14549                             '<span unselectable="on" qtip="' + tx + '">',
14550                              tx,
14551                              '</span></a>' ,
14552                     '</div>',
14553                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14554                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14555                  ];
14556         for(var i = 1, len = cols.length; i < len; i++){
14557             c = cols[i];
14558             colcls = 'x-t-' + tid + '-c' +i;
14559             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14560             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14561                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14562                       "</div>");
14563          }
14564          
14565          buf.push(
14566             '</a>',
14567             '<div class="x-clear"></div></div>',
14568             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14569             "</li>");
14570         
14571         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14572             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14573                                 n.nextSibling.ui.getEl(), buf.join(""));
14574         }else{
14575             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14576         }
14577         var el = this.wrap.firstChild;
14578         this.elRow = el;
14579         this.elNode = el.firstChild;
14580         this.ranchor = el.childNodes[1];
14581         this.ctNode = this.wrap.childNodes[1];
14582         var cs = el.firstChild.childNodes;
14583         this.indentNode = cs[0];
14584         this.ecNode = cs[1];
14585         this.iconNode = cs[2];
14586         var index = 3;
14587         if(cb){
14588             this.checkbox = cs[3];
14589             index++;
14590         }
14591         this.anchor = cs[index];
14592         
14593         this.textNode = cs[index].firstChild;
14594         
14595         //el.on("click", this.onClick, this);
14596         //el.on("dblclick", this.onDblClick, this);
14597         
14598         
14599        // console.log(this);
14600     },
14601     initEvents : function(){
14602         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14603         
14604             
14605         var a = this.ranchor;
14606
14607         var el = Roo.get(a);
14608
14609         if(Roo.isOpera){ // opera render bug ignores the CSS
14610             el.setStyle("text-decoration", "none");
14611         }
14612
14613         el.on("click", this.onClick, this);
14614         el.on("dblclick", this.onDblClick, this);
14615         el.on("contextmenu", this.onContextMenu, this);
14616         
14617     },
14618     
14619     /*onSelectedChange : function(state){
14620         if(state){
14621             this.focus();
14622             this.addClass("x-tree-selected");
14623         }else{
14624             //this.blur();
14625             this.removeClass("x-tree-selected");
14626         }
14627     },*/
14628     addClass : function(cls){
14629         if(this.elRow){
14630             Roo.fly(this.elRow).addClass(cls);
14631         }
14632         
14633     },
14634     
14635     
14636     removeClass : function(cls){
14637         if(this.elRow){
14638             Roo.fly(this.elRow).removeClass(cls);
14639         }
14640     }
14641
14642     
14643     
14644 });//<Script type="text/javascript">
14645
14646 /*
14647  * Based on:
14648  * Ext JS Library 1.1.1
14649  * Copyright(c) 2006-2007, Ext JS, LLC.
14650  *
14651  * Originally Released Under LGPL - original licence link has changed is not relivant.
14652  *
14653  * Fork - LGPL
14654  * <script type="text/javascript">
14655  */
14656  
14657
14658 /**
14659  * @class Roo.tree.ColumnTree
14660  * @extends Roo.data.TreePanel
14661  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14662  * @cfg {int} borderWidth  compined right/left border allowance
14663  * @constructor
14664  * @param {String/HTMLElement/Element} el The container element
14665  * @param {Object} config
14666  */
14667 Roo.tree.ColumnTree =  function(el, config)
14668 {
14669    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14670    this.addEvents({
14671         /**
14672         * @event resize
14673         * Fire this event on a container when it resizes
14674         * @param {int} w Width
14675         * @param {int} h Height
14676         */
14677        "resize" : true
14678     });
14679     this.on('resize', this.onResize, this);
14680 };
14681
14682 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14683     //lines:false,
14684     
14685     
14686     borderWidth: Roo.isBorderBox ? 0 : 2, 
14687     headEls : false,
14688     
14689     render : function(){
14690         // add the header.....
14691        
14692         Roo.tree.ColumnTree.superclass.render.apply(this);
14693         
14694         this.el.addClass('x-column-tree');
14695         
14696         this.headers = this.el.createChild(
14697             {cls:'x-tree-headers'},this.innerCt.dom);
14698    
14699         var cols = this.columns, c;
14700         var totalWidth = 0;
14701         this.headEls = [];
14702         var  len = cols.length;
14703         for(var i = 0; i < len; i++){
14704              c = cols[i];
14705              totalWidth += c.width;
14706             this.headEls.push(this.headers.createChild({
14707                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14708                  cn: {
14709                      cls:'x-tree-hd-text',
14710                      html: c.header
14711                  },
14712                  style:'width:'+(c.width-this.borderWidth)+'px;'
14713              }));
14714         }
14715         this.headers.createChild({cls:'x-clear'});
14716         // prevent floats from wrapping when clipped
14717         this.headers.setWidth(totalWidth);
14718         //this.innerCt.setWidth(totalWidth);
14719         this.innerCt.setStyle({ overflow: 'auto' });
14720         this.onResize(this.width, this.height);
14721              
14722         
14723     },
14724     onResize : function(w,h)
14725     {
14726         this.height = h;
14727         this.width = w;
14728         // resize cols..
14729         this.innerCt.setWidth(this.width);
14730         this.innerCt.setHeight(this.height-20);
14731         
14732         // headers...
14733         var cols = this.columns, c;
14734         var totalWidth = 0;
14735         var expEl = false;
14736         var len = cols.length;
14737         for(var i = 0; i < len; i++){
14738             c = cols[i];
14739             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14740                 // it's the expander..
14741                 expEl  = this.headEls[i];
14742                 continue;
14743             }
14744             totalWidth += c.width;
14745             
14746         }
14747         if (expEl) {
14748             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14749         }
14750         this.headers.setWidth(w-20);
14751
14752         
14753         
14754         
14755     }
14756 });
14757 /*
14758  * Based on:
14759  * Ext JS Library 1.1.1
14760  * Copyright(c) 2006-2007, Ext JS, LLC.
14761  *
14762  * Originally Released Under LGPL - original licence link has changed is not relivant.
14763  *
14764  * Fork - LGPL
14765  * <script type="text/javascript">
14766  */
14767  
14768 /**
14769  * @class Roo.menu.Menu
14770  * @extends Roo.util.Observable
14771  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14772  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14773  * @constructor
14774  * Creates a new Menu
14775  * @param {Object} config Configuration options
14776  */
14777 Roo.menu.Menu = function(config){
14778     
14779     Roo.menu.Menu.superclass.constructor.call(this, config);
14780     
14781     this.id = this.id || Roo.id();
14782     this.addEvents({
14783         /**
14784          * @event beforeshow
14785          * Fires before this menu is displayed
14786          * @param {Roo.menu.Menu} this
14787          */
14788         beforeshow : true,
14789         /**
14790          * @event beforehide
14791          * Fires before this menu is hidden
14792          * @param {Roo.menu.Menu} this
14793          */
14794         beforehide : true,
14795         /**
14796          * @event show
14797          * Fires after this menu is displayed
14798          * @param {Roo.menu.Menu} this
14799          */
14800         show : true,
14801         /**
14802          * @event hide
14803          * Fires after this menu is hidden
14804          * @param {Roo.menu.Menu} this
14805          */
14806         hide : true,
14807         /**
14808          * @event click
14809          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14810          * @param {Roo.menu.Menu} this
14811          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14812          * @param {Roo.EventObject} e
14813          */
14814         click : true,
14815         /**
14816          * @event mouseover
14817          * Fires when the mouse is hovering over this menu
14818          * @param {Roo.menu.Menu} this
14819          * @param {Roo.EventObject} e
14820          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14821          */
14822         mouseover : true,
14823         /**
14824          * @event mouseout
14825          * Fires when the mouse exits this menu
14826          * @param {Roo.menu.Menu} this
14827          * @param {Roo.EventObject} e
14828          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14829          */
14830         mouseout : true,
14831         /**
14832          * @event itemclick
14833          * Fires when a menu item contained in this menu is clicked
14834          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14835          * @param {Roo.EventObject} e
14836          */
14837         itemclick: true
14838     });
14839     if (this.registerMenu) {
14840         Roo.menu.MenuMgr.register(this);
14841     }
14842     
14843     var mis = this.items;
14844     this.items = new Roo.util.MixedCollection();
14845     if(mis){
14846         this.add.apply(this, mis);
14847     }
14848 };
14849
14850 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14851     /**
14852      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14853      */
14854     minWidth : 120,
14855     /**
14856      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14857      * for bottom-right shadow (defaults to "sides")
14858      */
14859     shadow : "sides",
14860     /**
14861      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14862      * this menu (defaults to "tl-tr?")
14863      */
14864     subMenuAlign : "tl-tr?",
14865     /**
14866      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14867      * relative to its element of origin (defaults to "tl-bl?")
14868      */
14869     defaultAlign : "tl-bl?",
14870     /**
14871      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14872      */
14873     allowOtherMenus : false,
14874     /**
14875      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14876      */
14877     registerMenu : true,
14878
14879     hidden:true,
14880
14881     // private
14882     render : function(){
14883         if(this.el){
14884             return;
14885         }
14886         var el = this.el = new Roo.Layer({
14887             cls: "x-menu",
14888             shadow:this.shadow,
14889             constrain: false,
14890             parentEl: this.parentEl || document.body,
14891             zindex:15000
14892         });
14893
14894         this.keyNav = new Roo.menu.MenuNav(this);
14895
14896         if(this.plain){
14897             el.addClass("x-menu-plain");
14898         }
14899         if(this.cls){
14900             el.addClass(this.cls);
14901         }
14902         // generic focus element
14903         this.focusEl = el.createChild({
14904             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14905         });
14906         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14907         //disabling touch- as it's causing issues ..
14908         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14909         ul.on('click'   , this.onClick, this);
14910         
14911         
14912         ul.on("mouseover", this.onMouseOver, this);
14913         ul.on("mouseout", this.onMouseOut, this);
14914         this.items.each(function(item){
14915             if (item.hidden) {
14916                 return;
14917             }
14918             
14919             var li = document.createElement("li");
14920             li.className = "x-menu-list-item";
14921             ul.dom.appendChild(li);
14922             item.render(li, this);
14923         }, this);
14924         this.ul = ul;
14925         this.autoWidth();
14926     },
14927
14928     // private
14929     autoWidth : function(){
14930         var el = this.el, ul = this.ul;
14931         if(!el){
14932             return;
14933         }
14934         var w = this.width;
14935         if(w){
14936             el.setWidth(w);
14937         }else if(Roo.isIE){
14938             el.setWidth(this.minWidth);
14939             var t = el.dom.offsetWidth; // force recalc
14940             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14941         }
14942     },
14943
14944     // private
14945     delayAutoWidth : function(){
14946         if(this.rendered){
14947             if(!this.awTask){
14948                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14949             }
14950             this.awTask.delay(20);
14951         }
14952     },
14953
14954     // private
14955     findTargetItem : function(e){
14956         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14957         if(t && t.menuItemId){
14958             return this.items.get(t.menuItemId);
14959         }
14960     },
14961
14962     // private
14963     onClick : function(e){
14964         Roo.log("menu.onClick");
14965         var t = this.findTargetItem(e);
14966         if(!t){
14967             return;
14968         }
14969         Roo.log(e);
14970         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14971             if(t == this.activeItem && t.shouldDeactivate(e)){
14972                 this.activeItem.deactivate();
14973                 delete this.activeItem;
14974                 return;
14975             }
14976             if(t.canActivate){
14977                 this.setActiveItem(t, true);
14978             }
14979             return;
14980             
14981             
14982         }
14983         
14984         t.onClick(e);
14985         this.fireEvent("click", this, t, e);
14986     },
14987
14988     // private
14989     setActiveItem : function(item, autoExpand){
14990         if(item != this.activeItem){
14991             if(this.activeItem){
14992                 this.activeItem.deactivate();
14993             }
14994             this.activeItem = item;
14995             item.activate(autoExpand);
14996         }else if(autoExpand){
14997             item.expandMenu();
14998         }
14999     },
15000
15001     // private
15002     tryActivate : function(start, step){
15003         var items = this.items;
15004         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15005             var item = items.get(i);
15006             if(!item.disabled && item.canActivate){
15007                 this.setActiveItem(item, false);
15008                 return item;
15009             }
15010         }
15011         return false;
15012     },
15013
15014     // private
15015     onMouseOver : function(e){
15016         var t;
15017         if(t = this.findTargetItem(e)){
15018             if(t.canActivate && !t.disabled){
15019                 this.setActiveItem(t, true);
15020             }
15021         }
15022         this.fireEvent("mouseover", this, e, t);
15023     },
15024
15025     // private
15026     onMouseOut : function(e){
15027         var t;
15028         if(t = this.findTargetItem(e)){
15029             if(t == this.activeItem && t.shouldDeactivate(e)){
15030                 this.activeItem.deactivate();
15031                 delete this.activeItem;
15032             }
15033         }
15034         this.fireEvent("mouseout", this, e, t);
15035     },
15036
15037     /**
15038      * Read-only.  Returns true if the menu is currently displayed, else false.
15039      * @type Boolean
15040      */
15041     isVisible : function(){
15042         return this.el && !this.hidden;
15043     },
15044
15045     /**
15046      * Displays this menu relative to another element
15047      * @param {String/HTMLElement/Roo.Element} element The element to align to
15048      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15049      * the element (defaults to this.defaultAlign)
15050      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15051      */
15052     show : function(el, pos, parentMenu){
15053         this.parentMenu = parentMenu;
15054         if(!this.el){
15055             this.render();
15056         }
15057         this.fireEvent("beforeshow", this);
15058         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15059     },
15060
15061     /**
15062      * Displays this menu at a specific xy position
15063      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15064      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15065      */
15066     showAt : function(xy, parentMenu, /* private: */_e){
15067         this.parentMenu = parentMenu;
15068         if(!this.el){
15069             this.render();
15070         }
15071         if(_e !== false){
15072             this.fireEvent("beforeshow", this);
15073             xy = this.el.adjustForConstraints(xy);
15074         }
15075         this.el.setXY(xy);
15076         this.el.show();
15077         this.hidden = false;
15078         this.focus();
15079         this.fireEvent("show", this);
15080     },
15081
15082     focus : function(){
15083         if(!this.hidden){
15084             this.doFocus.defer(50, this);
15085         }
15086     },
15087
15088     doFocus : function(){
15089         if(!this.hidden){
15090             this.focusEl.focus();
15091         }
15092     },
15093
15094     /**
15095      * Hides this menu and optionally all parent menus
15096      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15097      */
15098     hide : function(deep){
15099         if(this.el && this.isVisible()){
15100             this.fireEvent("beforehide", this);
15101             if(this.activeItem){
15102                 this.activeItem.deactivate();
15103                 this.activeItem = null;
15104             }
15105             this.el.hide();
15106             this.hidden = true;
15107             this.fireEvent("hide", this);
15108         }
15109         if(deep === true && this.parentMenu){
15110             this.parentMenu.hide(true);
15111         }
15112     },
15113
15114     /**
15115      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15116      * Any of the following are valid:
15117      * <ul>
15118      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15119      * <li>An HTMLElement object which will be converted to a menu item</li>
15120      * <li>A menu item config object that will be created as a new menu item</li>
15121      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15122      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15123      * </ul>
15124      * Usage:
15125      * <pre><code>
15126 // Create the menu
15127 var menu = new Roo.menu.Menu();
15128
15129 // Create a menu item to add by reference
15130 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15131
15132 // Add a bunch of items at once using different methods.
15133 // Only the last item added will be returned.
15134 var item = menu.add(
15135     menuItem,                // add existing item by ref
15136     'Dynamic Item',          // new TextItem
15137     '-',                     // new separator
15138     { text: 'Config Item' }  // new item by config
15139 );
15140 </code></pre>
15141      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15142      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15143      */
15144     add : function(){
15145         var a = arguments, l = a.length, item;
15146         for(var i = 0; i < l; i++){
15147             var el = a[i];
15148             if ((typeof(el) == "object") && el.xtype && el.xns) {
15149                 el = Roo.factory(el, Roo.menu);
15150             }
15151             
15152             if(el.render){ // some kind of Item
15153                 item = this.addItem(el);
15154             }else if(typeof el == "string"){ // string
15155                 if(el == "separator" || el == "-"){
15156                     item = this.addSeparator();
15157                 }else{
15158                     item = this.addText(el);
15159                 }
15160             }else if(el.tagName || el.el){ // element
15161                 item = this.addElement(el);
15162             }else if(typeof el == "object"){ // must be menu item config?
15163                 item = this.addMenuItem(el);
15164             }
15165         }
15166         return item;
15167     },
15168
15169     /**
15170      * Returns this menu's underlying {@link Roo.Element} object
15171      * @return {Roo.Element} The element
15172      */
15173     getEl : function(){
15174         if(!this.el){
15175             this.render();
15176         }
15177         return this.el;
15178     },
15179
15180     /**
15181      * Adds a separator bar to the menu
15182      * @return {Roo.menu.Item} The menu item that was added
15183      */
15184     addSeparator : function(){
15185         return this.addItem(new Roo.menu.Separator());
15186     },
15187
15188     /**
15189      * Adds an {@link Roo.Element} object to the menu
15190      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15191      * @return {Roo.menu.Item} The menu item that was added
15192      */
15193     addElement : function(el){
15194         return this.addItem(new Roo.menu.BaseItem(el));
15195     },
15196
15197     /**
15198      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15199      * @param {Roo.menu.Item} item The menu item to add
15200      * @return {Roo.menu.Item} The menu item that was added
15201      */
15202     addItem : function(item){
15203         this.items.add(item);
15204         if(this.ul){
15205             var li = document.createElement("li");
15206             li.className = "x-menu-list-item";
15207             this.ul.dom.appendChild(li);
15208             item.render(li, this);
15209             this.delayAutoWidth();
15210         }
15211         return item;
15212     },
15213
15214     /**
15215      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15216      * @param {Object} config A MenuItem config object
15217      * @return {Roo.menu.Item} The menu item that was added
15218      */
15219     addMenuItem : function(config){
15220         if(!(config instanceof Roo.menu.Item)){
15221             if(typeof config.checked == "boolean"){ // must be check menu item config?
15222                 config = new Roo.menu.CheckItem(config);
15223             }else{
15224                 config = new Roo.menu.Item(config);
15225             }
15226         }
15227         return this.addItem(config);
15228     },
15229
15230     /**
15231      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15232      * @param {String} text The text to display in the menu item
15233      * @return {Roo.menu.Item} The menu item that was added
15234      */
15235     addText : function(text){
15236         return this.addItem(new Roo.menu.TextItem({ text : text }));
15237     },
15238
15239     /**
15240      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15241      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15242      * @param {Roo.menu.Item} item The menu item to add
15243      * @return {Roo.menu.Item} The menu item that was added
15244      */
15245     insert : function(index, item){
15246         this.items.insert(index, item);
15247         if(this.ul){
15248             var li = document.createElement("li");
15249             li.className = "x-menu-list-item";
15250             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15251             item.render(li, this);
15252             this.delayAutoWidth();
15253         }
15254         return item;
15255     },
15256
15257     /**
15258      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15259      * @param {Roo.menu.Item} item The menu item to remove
15260      */
15261     remove : function(item){
15262         this.items.removeKey(item.id);
15263         item.destroy();
15264     },
15265
15266     /**
15267      * Removes and destroys all items in the menu
15268      */
15269     removeAll : function(){
15270         var f;
15271         while(f = this.items.first()){
15272             this.remove(f);
15273         }
15274     }
15275 });
15276
15277 // MenuNav is a private utility class used internally by the Menu
15278 Roo.menu.MenuNav = function(menu){
15279     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15280     this.scope = this.menu = menu;
15281 };
15282
15283 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15284     doRelay : function(e, h){
15285         var k = e.getKey();
15286         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15287             this.menu.tryActivate(0, 1);
15288             return false;
15289         }
15290         return h.call(this.scope || this, e, this.menu);
15291     },
15292
15293     up : function(e, m){
15294         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15295             m.tryActivate(m.items.length-1, -1);
15296         }
15297     },
15298
15299     down : function(e, m){
15300         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15301             m.tryActivate(0, 1);
15302         }
15303     },
15304
15305     right : function(e, m){
15306         if(m.activeItem){
15307             m.activeItem.expandMenu(true);
15308         }
15309     },
15310
15311     left : function(e, m){
15312         m.hide();
15313         if(m.parentMenu && m.parentMenu.activeItem){
15314             m.parentMenu.activeItem.activate();
15315         }
15316     },
15317
15318     enter : function(e, m){
15319         if(m.activeItem){
15320             e.stopPropagation();
15321             m.activeItem.onClick(e);
15322             m.fireEvent("click", this, m.activeItem);
15323             return true;
15324         }
15325     }
15326 });/*
15327  * Based on:
15328  * Ext JS Library 1.1.1
15329  * Copyright(c) 2006-2007, Ext JS, LLC.
15330  *
15331  * Originally Released Under LGPL - original licence link has changed is not relivant.
15332  *
15333  * Fork - LGPL
15334  * <script type="text/javascript">
15335  */
15336  
15337 /**
15338  * @class Roo.menu.MenuMgr
15339  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15340  * @singleton
15341  */
15342 Roo.menu.MenuMgr = function(){
15343    var menus, active, groups = {}, attached = false, lastShow = new Date();
15344
15345    // private - called when first menu is created
15346    function init(){
15347        menus = {};
15348        active = new Roo.util.MixedCollection();
15349        Roo.get(document).addKeyListener(27, function(){
15350            if(active.length > 0){
15351                hideAll();
15352            }
15353        });
15354    }
15355
15356    // private
15357    function hideAll(){
15358        if(active && active.length > 0){
15359            var c = active.clone();
15360            c.each(function(m){
15361                m.hide();
15362            });
15363        }
15364    }
15365
15366    // private
15367    function onHide(m){
15368        active.remove(m);
15369        if(active.length < 1){
15370            Roo.get(document).un("mousedown", onMouseDown);
15371            attached = false;
15372        }
15373    }
15374
15375    // private
15376    function onShow(m){
15377        var last = active.last();
15378        lastShow = new Date();
15379        active.add(m);
15380        if(!attached){
15381            Roo.get(document).on("mousedown", onMouseDown);
15382            attached = true;
15383        }
15384        if(m.parentMenu){
15385           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15386           m.parentMenu.activeChild = m;
15387        }else if(last && last.isVisible()){
15388           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15389        }
15390    }
15391
15392    // private
15393    function onBeforeHide(m){
15394        if(m.activeChild){
15395            m.activeChild.hide();
15396        }
15397        if(m.autoHideTimer){
15398            clearTimeout(m.autoHideTimer);
15399            delete m.autoHideTimer;
15400        }
15401    }
15402
15403    // private
15404    function onBeforeShow(m){
15405        var pm = m.parentMenu;
15406        if(!pm && !m.allowOtherMenus){
15407            hideAll();
15408        }else if(pm && pm.activeChild && active != m){
15409            pm.activeChild.hide();
15410        }
15411    }
15412
15413    // private
15414    function onMouseDown(e){
15415        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15416            hideAll();
15417        }
15418    }
15419
15420    // private
15421    function onBeforeCheck(mi, state){
15422        if(state){
15423            var g = groups[mi.group];
15424            for(var i = 0, l = g.length; i < l; i++){
15425                if(g[i] != mi){
15426                    g[i].setChecked(false);
15427                }
15428            }
15429        }
15430    }
15431
15432    return {
15433
15434        /**
15435         * Hides all menus that are currently visible
15436         */
15437        hideAll : function(){
15438             hideAll();  
15439        },
15440
15441        // private
15442        register : function(menu){
15443            if(!menus){
15444                init();
15445            }
15446            menus[menu.id] = menu;
15447            menu.on("beforehide", onBeforeHide);
15448            menu.on("hide", onHide);
15449            menu.on("beforeshow", onBeforeShow);
15450            menu.on("show", onShow);
15451            var g = menu.group;
15452            if(g && menu.events["checkchange"]){
15453                if(!groups[g]){
15454                    groups[g] = [];
15455                }
15456                groups[g].push(menu);
15457                menu.on("checkchange", onCheck);
15458            }
15459        },
15460
15461         /**
15462          * Returns a {@link Roo.menu.Menu} object
15463          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15464          * be used to generate and return a new Menu instance.
15465          */
15466        get : function(menu){
15467            if(typeof menu == "string"){ // menu id
15468                return menus[menu];
15469            }else if(menu.events){  // menu instance
15470                return menu;
15471            }else if(typeof menu.length == 'number'){ // array of menu items?
15472                return new Roo.menu.Menu({items:menu});
15473            }else{ // otherwise, must be a config
15474                return new Roo.menu.Menu(menu);
15475            }
15476        },
15477
15478        // private
15479        unregister : function(menu){
15480            delete menus[menu.id];
15481            menu.un("beforehide", onBeforeHide);
15482            menu.un("hide", onHide);
15483            menu.un("beforeshow", onBeforeShow);
15484            menu.un("show", onShow);
15485            var g = menu.group;
15486            if(g && menu.events["checkchange"]){
15487                groups[g].remove(menu);
15488                menu.un("checkchange", onCheck);
15489            }
15490        },
15491
15492        // private
15493        registerCheckable : function(menuItem){
15494            var g = menuItem.group;
15495            if(g){
15496                if(!groups[g]){
15497                    groups[g] = [];
15498                }
15499                groups[g].push(menuItem);
15500                menuItem.on("beforecheckchange", onBeforeCheck);
15501            }
15502        },
15503
15504        // private
15505        unregisterCheckable : function(menuItem){
15506            var g = menuItem.group;
15507            if(g){
15508                groups[g].remove(menuItem);
15509                menuItem.un("beforecheckchange", onBeforeCheck);
15510            }
15511        }
15512    };
15513 }();/*
15514  * Based on:
15515  * Ext JS Library 1.1.1
15516  * Copyright(c) 2006-2007, Ext JS, LLC.
15517  *
15518  * Originally Released Under LGPL - original licence link has changed is not relivant.
15519  *
15520  * Fork - LGPL
15521  * <script type="text/javascript">
15522  */
15523  
15524
15525 /**
15526  * @class Roo.menu.BaseItem
15527  * @extends Roo.Component
15528  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15529  * management and base configuration options shared by all menu components.
15530  * @constructor
15531  * Creates a new BaseItem
15532  * @param {Object} config Configuration options
15533  */
15534 Roo.menu.BaseItem = function(config){
15535     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15536
15537     this.addEvents({
15538         /**
15539          * @event click
15540          * Fires when this item is clicked
15541          * @param {Roo.menu.BaseItem} this
15542          * @param {Roo.EventObject} e
15543          */
15544         click: true,
15545         /**
15546          * @event activate
15547          * Fires when this item is activated
15548          * @param {Roo.menu.BaseItem} this
15549          */
15550         activate : true,
15551         /**
15552          * @event deactivate
15553          * Fires when this item is deactivated
15554          * @param {Roo.menu.BaseItem} this
15555          */
15556         deactivate : true
15557     });
15558
15559     if(this.handler){
15560         this.on("click", this.handler, this.scope, true);
15561     }
15562 };
15563
15564 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15565     /**
15566      * @cfg {Function} handler
15567      * A function that will handle the click event of this menu item (defaults to undefined)
15568      */
15569     /**
15570      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15571      */
15572     canActivate : false,
15573     
15574      /**
15575      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15576      */
15577     hidden: false,
15578     
15579     /**
15580      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15581      */
15582     activeClass : "x-menu-item-active",
15583     /**
15584      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15585      */
15586     hideOnClick : true,
15587     /**
15588      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15589      */
15590     hideDelay : 100,
15591
15592     // private
15593     ctype: "Roo.menu.BaseItem",
15594
15595     // private
15596     actionMode : "container",
15597
15598     // private
15599     render : function(container, parentMenu){
15600         this.parentMenu = parentMenu;
15601         Roo.menu.BaseItem.superclass.render.call(this, container);
15602         this.container.menuItemId = this.id;
15603     },
15604
15605     // private
15606     onRender : function(container, position){
15607         this.el = Roo.get(this.el);
15608         container.dom.appendChild(this.el.dom);
15609     },
15610
15611     // private
15612     onClick : function(e){
15613         if(!this.disabled && this.fireEvent("click", this, e) !== false
15614                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15615             this.handleClick(e);
15616         }else{
15617             e.stopEvent();
15618         }
15619     },
15620
15621     // private
15622     activate : function(){
15623         if(this.disabled){
15624             return false;
15625         }
15626         var li = this.container;
15627         li.addClass(this.activeClass);
15628         this.region = li.getRegion().adjust(2, 2, -2, -2);
15629         this.fireEvent("activate", this);
15630         return true;
15631     },
15632
15633     // private
15634     deactivate : function(){
15635         this.container.removeClass(this.activeClass);
15636         this.fireEvent("deactivate", this);
15637     },
15638
15639     // private
15640     shouldDeactivate : function(e){
15641         return !this.region || !this.region.contains(e.getPoint());
15642     },
15643
15644     // private
15645     handleClick : function(e){
15646         if(this.hideOnClick){
15647             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15648         }
15649     },
15650
15651     // private
15652     expandMenu : function(autoActivate){
15653         // do nothing
15654     },
15655
15656     // private
15657     hideMenu : function(){
15658         // do nothing
15659     }
15660 });/*
15661  * Based on:
15662  * Ext JS Library 1.1.1
15663  * Copyright(c) 2006-2007, Ext JS, LLC.
15664  *
15665  * Originally Released Under LGPL - original licence link has changed is not relivant.
15666  *
15667  * Fork - LGPL
15668  * <script type="text/javascript">
15669  */
15670  
15671 /**
15672  * @class Roo.menu.Adapter
15673  * @extends Roo.menu.BaseItem
15674  * 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.
15675  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15676  * @constructor
15677  * Creates a new Adapter
15678  * @param {Object} config Configuration options
15679  */
15680 Roo.menu.Adapter = function(component, config){
15681     Roo.menu.Adapter.superclass.constructor.call(this, config);
15682     this.component = component;
15683 };
15684 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15685     // private
15686     canActivate : true,
15687
15688     // private
15689     onRender : function(container, position){
15690         this.component.render(container);
15691         this.el = this.component.getEl();
15692     },
15693
15694     // private
15695     activate : function(){
15696         if(this.disabled){
15697             return false;
15698         }
15699         this.component.focus();
15700         this.fireEvent("activate", this);
15701         return true;
15702     },
15703
15704     // private
15705     deactivate : function(){
15706         this.fireEvent("deactivate", this);
15707     },
15708
15709     // private
15710     disable : function(){
15711         this.component.disable();
15712         Roo.menu.Adapter.superclass.disable.call(this);
15713     },
15714
15715     // private
15716     enable : function(){
15717         this.component.enable();
15718         Roo.menu.Adapter.superclass.enable.call(this);
15719     }
15720 });/*
15721  * Based on:
15722  * Ext JS Library 1.1.1
15723  * Copyright(c) 2006-2007, Ext JS, LLC.
15724  *
15725  * Originally Released Under LGPL - original licence link has changed is not relivant.
15726  *
15727  * Fork - LGPL
15728  * <script type="text/javascript">
15729  */
15730
15731 /**
15732  * @class Roo.menu.TextItem
15733  * @extends Roo.menu.BaseItem
15734  * Adds a static text string to a menu, usually used as either a heading or group separator.
15735  * Note: old style constructor with text is still supported.
15736  * 
15737  * @constructor
15738  * Creates a new TextItem
15739  * @param {Object} cfg Configuration
15740  */
15741 Roo.menu.TextItem = function(cfg){
15742     if (typeof(cfg) == 'string') {
15743         this.text = cfg;
15744     } else {
15745         Roo.apply(this,cfg);
15746     }
15747     
15748     Roo.menu.TextItem.superclass.constructor.call(this);
15749 };
15750
15751 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15752     /**
15753      * @cfg {Boolean} text Text to show on item.
15754      */
15755     text : '',
15756     
15757     /**
15758      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15759      */
15760     hideOnClick : false,
15761     /**
15762      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15763      */
15764     itemCls : "x-menu-text",
15765
15766     // private
15767     onRender : function(){
15768         var s = document.createElement("span");
15769         s.className = this.itemCls;
15770         s.innerHTML = this.text;
15771         this.el = s;
15772         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15773     }
15774 });/*
15775  * Based on:
15776  * Ext JS Library 1.1.1
15777  * Copyright(c) 2006-2007, Ext JS, LLC.
15778  *
15779  * Originally Released Under LGPL - original licence link has changed is not relivant.
15780  *
15781  * Fork - LGPL
15782  * <script type="text/javascript">
15783  */
15784
15785 /**
15786  * @class Roo.menu.Separator
15787  * @extends Roo.menu.BaseItem
15788  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15789  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15790  * @constructor
15791  * @param {Object} config Configuration options
15792  */
15793 Roo.menu.Separator = function(config){
15794     Roo.menu.Separator.superclass.constructor.call(this, config);
15795 };
15796
15797 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15798     /**
15799      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15800      */
15801     itemCls : "x-menu-sep",
15802     /**
15803      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15804      */
15805     hideOnClick : false,
15806
15807     // private
15808     onRender : function(li){
15809         var s = document.createElement("span");
15810         s.className = this.itemCls;
15811         s.innerHTML = "&#160;";
15812         this.el = s;
15813         li.addClass("x-menu-sep-li");
15814         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15815     }
15816 });/*
15817  * Based on:
15818  * Ext JS Library 1.1.1
15819  * Copyright(c) 2006-2007, Ext JS, LLC.
15820  *
15821  * Originally Released Under LGPL - original licence link has changed is not relivant.
15822  *
15823  * Fork - LGPL
15824  * <script type="text/javascript">
15825  */
15826 /**
15827  * @class Roo.menu.Item
15828  * @extends Roo.menu.BaseItem
15829  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15830  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15831  * activation and click handling.
15832  * @constructor
15833  * Creates a new Item
15834  * @param {Object} config Configuration options
15835  */
15836 Roo.menu.Item = function(config){
15837     Roo.menu.Item.superclass.constructor.call(this, config);
15838     if(this.menu){
15839         this.menu = Roo.menu.MenuMgr.get(this.menu);
15840     }
15841 };
15842 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15843     
15844     /**
15845      * @cfg {String} text
15846      * The text to show on the menu item.
15847      */
15848     text: '',
15849      /**
15850      * @cfg {String} HTML to render in menu
15851      * The text to show on the menu item (HTML version).
15852      */
15853     html: '',
15854     /**
15855      * @cfg {String} icon
15856      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15857      */
15858     icon: undefined,
15859     /**
15860      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15861      */
15862     itemCls : "x-menu-item",
15863     /**
15864      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15865      */
15866     canActivate : true,
15867     /**
15868      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15869      */
15870     showDelay: 200,
15871     // doc'd in BaseItem
15872     hideDelay: 200,
15873
15874     // private
15875     ctype: "Roo.menu.Item",
15876     
15877     // private
15878     onRender : function(container, position){
15879         var el = document.createElement("a");
15880         el.hideFocus = true;
15881         el.unselectable = "on";
15882         el.href = this.href || "#";
15883         if(this.hrefTarget){
15884             el.target = this.hrefTarget;
15885         }
15886         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15887         
15888         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15889         
15890         el.innerHTML = String.format(
15891                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15892                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15893         this.el = el;
15894         Roo.menu.Item.superclass.onRender.call(this, container, position);
15895     },
15896
15897     /**
15898      * Sets the text to display in this menu item
15899      * @param {String} text The text to display
15900      * @param {Boolean} isHTML true to indicate text is pure html.
15901      */
15902     setText : function(text, isHTML){
15903         if (isHTML) {
15904             this.html = text;
15905         } else {
15906             this.text = text;
15907             this.html = '';
15908         }
15909         if(this.rendered){
15910             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15911      
15912             this.el.update(String.format(
15913                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15914                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15915             this.parentMenu.autoWidth();
15916         }
15917     },
15918
15919     // private
15920     handleClick : function(e){
15921         if(!this.href){ // if no link defined, stop the event automatically
15922             e.stopEvent();
15923         }
15924         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15925     },
15926
15927     // private
15928     activate : function(autoExpand){
15929         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15930             this.focus();
15931             if(autoExpand){
15932                 this.expandMenu();
15933             }
15934         }
15935         return true;
15936     },
15937
15938     // private
15939     shouldDeactivate : function(e){
15940         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15941             if(this.menu && this.menu.isVisible()){
15942                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15943             }
15944             return true;
15945         }
15946         return false;
15947     },
15948
15949     // private
15950     deactivate : function(){
15951         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15952         this.hideMenu();
15953     },
15954
15955     // private
15956     expandMenu : function(autoActivate){
15957         if(!this.disabled && this.menu){
15958             clearTimeout(this.hideTimer);
15959             delete this.hideTimer;
15960             if(!this.menu.isVisible() && !this.showTimer){
15961                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15962             }else if (this.menu.isVisible() && autoActivate){
15963                 this.menu.tryActivate(0, 1);
15964             }
15965         }
15966     },
15967
15968     // private
15969     deferExpand : function(autoActivate){
15970         delete this.showTimer;
15971         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15972         if(autoActivate){
15973             this.menu.tryActivate(0, 1);
15974         }
15975     },
15976
15977     // private
15978     hideMenu : function(){
15979         clearTimeout(this.showTimer);
15980         delete this.showTimer;
15981         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15982             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15983         }
15984     },
15985
15986     // private
15987     deferHide : function(){
15988         delete this.hideTimer;
15989         this.menu.hide();
15990     }
15991 });/*
15992  * Based on:
15993  * Ext JS Library 1.1.1
15994  * Copyright(c) 2006-2007, Ext JS, LLC.
15995  *
15996  * Originally Released Under LGPL - original licence link has changed is not relivant.
15997  *
15998  * Fork - LGPL
15999  * <script type="text/javascript">
16000  */
16001  
16002 /**
16003  * @class Roo.menu.CheckItem
16004  * @extends Roo.menu.Item
16005  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16006  * @constructor
16007  * Creates a new CheckItem
16008  * @param {Object} config Configuration options
16009  */
16010 Roo.menu.CheckItem = function(config){
16011     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16012     this.addEvents({
16013         /**
16014          * @event beforecheckchange
16015          * Fires before the checked value is set, providing an opportunity to cancel if needed
16016          * @param {Roo.menu.CheckItem} this
16017          * @param {Boolean} checked The new checked value that will be set
16018          */
16019         "beforecheckchange" : true,
16020         /**
16021          * @event checkchange
16022          * Fires after the checked value has been set
16023          * @param {Roo.menu.CheckItem} this
16024          * @param {Boolean} checked The checked value that was set
16025          */
16026         "checkchange" : true
16027     });
16028     if(this.checkHandler){
16029         this.on('checkchange', this.checkHandler, this.scope);
16030     }
16031 };
16032 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16033     /**
16034      * @cfg {String} group
16035      * All check items with the same group name will automatically be grouped into a single-select
16036      * radio button group (defaults to '')
16037      */
16038     /**
16039      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16040      */
16041     itemCls : "x-menu-item x-menu-check-item",
16042     /**
16043      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16044      */
16045     groupClass : "x-menu-group-item",
16046
16047     /**
16048      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16049      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16050      * initialized with checked = true will be rendered as checked.
16051      */
16052     checked: false,
16053
16054     // private
16055     ctype: "Roo.menu.CheckItem",
16056
16057     // private
16058     onRender : function(c){
16059         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16060         if(this.group){
16061             this.el.addClass(this.groupClass);
16062         }
16063         Roo.menu.MenuMgr.registerCheckable(this);
16064         if(this.checked){
16065             this.checked = false;
16066             this.setChecked(true, true);
16067         }
16068     },
16069
16070     // private
16071     destroy : function(){
16072         if(this.rendered){
16073             Roo.menu.MenuMgr.unregisterCheckable(this);
16074         }
16075         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16076     },
16077
16078     /**
16079      * Set the checked state of this item
16080      * @param {Boolean} checked The new checked value
16081      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16082      */
16083     setChecked : function(state, suppressEvent){
16084         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16085             if(this.container){
16086                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16087             }
16088             this.checked = state;
16089             if(suppressEvent !== true){
16090                 this.fireEvent("checkchange", this, state);
16091             }
16092         }
16093     },
16094
16095     // private
16096     handleClick : function(e){
16097        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16098            this.setChecked(!this.checked);
16099        }
16100        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16101     }
16102 });/*
16103  * Based on:
16104  * Ext JS Library 1.1.1
16105  * Copyright(c) 2006-2007, Ext JS, LLC.
16106  *
16107  * Originally Released Under LGPL - original licence link has changed is not relivant.
16108  *
16109  * Fork - LGPL
16110  * <script type="text/javascript">
16111  */
16112  
16113 /**
16114  * @class Roo.menu.DateItem
16115  * @extends Roo.menu.Adapter
16116  * A menu item that wraps the {@link Roo.DatPicker} component.
16117  * @constructor
16118  * Creates a new DateItem
16119  * @param {Object} config Configuration options
16120  */
16121 Roo.menu.DateItem = function(config){
16122     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16123     /** The Roo.DatePicker object @type Roo.DatePicker */
16124     this.picker = this.component;
16125     this.addEvents({select: true});
16126     
16127     this.picker.on("render", function(picker){
16128         picker.getEl().swallowEvent("click");
16129         picker.container.addClass("x-menu-date-item");
16130     });
16131
16132     this.picker.on("select", this.onSelect, this);
16133 };
16134
16135 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16136     // private
16137     onSelect : function(picker, date){
16138         this.fireEvent("select", this, date, picker);
16139         Roo.menu.DateItem.superclass.handleClick.call(this);
16140     }
16141 });/*
16142  * Based on:
16143  * Ext JS Library 1.1.1
16144  * Copyright(c) 2006-2007, Ext JS, LLC.
16145  *
16146  * Originally Released Under LGPL - original licence link has changed is not relivant.
16147  *
16148  * Fork - LGPL
16149  * <script type="text/javascript">
16150  */
16151  
16152 /**
16153  * @class Roo.menu.ColorItem
16154  * @extends Roo.menu.Adapter
16155  * A menu item that wraps the {@link Roo.ColorPalette} component.
16156  * @constructor
16157  * Creates a new ColorItem
16158  * @param {Object} config Configuration options
16159  */
16160 Roo.menu.ColorItem = function(config){
16161     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16162     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16163     this.palette = this.component;
16164     this.relayEvents(this.palette, ["select"]);
16165     if(this.selectHandler){
16166         this.on('select', this.selectHandler, this.scope);
16167     }
16168 };
16169 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16170  * Based on:
16171  * Ext JS Library 1.1.1
16172  * Copyright(c) 2006-2007, Ext JS, LLC.
16173  *
16174  * Originally Released Under LGPL - original licence link has changed is not relivant.
16175  *
16176  * Fork - LGPL
16177  * <script type="text/javascript">
16178  */
16179  
16180
16181 /**
16182  * @class Roo.menu.DateMenu
16183  * @extends Roo.menu.Menu
16184  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16185  * @constructor
16186  * Creates a new DateMenu
16187  * @param {Object} config Configuration options
16188  */
16189 Roo.menu.DateMenu = function(config){
16190     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16191     this.plain = true;
16192     var di = new Roo.menu.DateItem(config);
16193     this.add(di);
16194     /**
16195      * The {@link Roo.DatePicker} instance for this DateMenu
16196      * @type DatePicker
16197      */
16198     this.picker = di.picker;
16199     /**
16200      * @event select
16201      * @param {DatePicker} picker
16202      * @param {Date} date
16203      */
16204     this.relayEvents(di, ["select"]);
16205     this.on('beforeshow', function(){
16206         if(this.picker){
16207             this.picker.hideMonthPicker(false);
16208         }
16209     }, this);
16210 };
16211 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16212     cls:'x-date-menu'
16213 });/*
16214  * Based on:
16215  * Ext JS Library 1.1.1
16216  * Copyright(c) 2006-2007, Ext JS, LLC.
16217  *
16218  * Originally Released Under LGPL - original licence link has changed is not relivant.
16219  *
16220  * Fork - LGPL
16221  * <script type="text/javascript">
16222  */
16223  
16224
16225 /**
16226  * @class Roo.menu.ColorMenu
16227  * @extends Roo.menu.Menu
16228  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16229  * @constructor
16230  * Creates a new ColorMenu
16231  * @param {Object} config Configuration options
16232  */
16233 Roo.menu.ColorMenu = function(config){
16234     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16235     this.plain = true;
16236     var ci = new Roo.menu.ColorItem(config);
16237     this.add(ci);
16238     /**
16239      * The {@link Roo.ColorPalette} instance for this ColorMenu
16240      * @type ColorPalette
16241      */
16242     this.palette = ci.palette;
16243     /**
16244      * @event select
16245      * @param {ColorPalette} palette
16246      * @param {String} color
16247      */
16248     this.relayEvents(ci, ["select"]);
16249 };
16250 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16251  * Based on:
16252  * Ext JS Library 1.1.1
16253  * Copyright(c) 2006-2007, Ext JS, LLC.
16254  *
16255  * Originally Released Under LGPL - original licence link has changed is not relivant.
16256  *
16257  * Fork - LGPL
16258  * <script type="text/javascript">
16259  */
16260  
16261 /**
16262  * @class Roo.form.TextItem
16263  * @extends Roo.BoxComponent
16264  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16265  * @constructor
16266  * Creates a new TextItem
16267  * @param {Object} config Configuration options
16268  */
16269 Roo.form.TextItem = function(config){
16270     Roo.form.TextItem.superclass.constructor.call(this, config);
16271 };
16272
16273 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16274     
16275     /**
16276      * @cfg {String} tag the tag for this item (default div)
16277      */
16278     tag : 'div',
16279     /**
16280      * @cfg {String} html the content for this item
16281      */
16282     html : '',
16283     
16284     getAutoCreate : function()
16285     {
16286         var cfg = {
16287             id: this.id,
16288             tag: this.tag,
16289             html: this.html,
16290             cls: 'x-form-item'
16291         };
16292         
16293         return cfg;
16294         
16295     },
16296     
16297     onRender : function(ct, position)
16298     {
16299         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16300         
16301         if(!this.el){
16302             var cfg = this.getAutoCreate();
16303             if(!cfg.name){
16304                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16305             }
16306             if (!cfg.name.length) {
16307                 delete cfg.name;
16308             }
16309             this.el = ct.createChild(cfg, position);
16310         }
16311     }
16312     
16313 });/*
16314  * Based on:
16315  * Ext JS Library 1.1.1
16316  * Copyright(c) 2006-2007, Ext JS, LLC.
16317  *
16318  * Originally Released Under LGPL - original licence link has changed is not relivant.
16319  *
16320  * Fork - LGPL
16321  * <script type="text/javascript">
16322  */
16323  
16324 /**
16325  * @class Roo.form.Field
16326  * @extends Roo.BoxComponent
16327  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16328  * @constructor
16329  * Creates a new Field
16330  * @param {Object} config Configuration options
16331  */
16332 Roo.form.Field = function(config){
16333     Roo.form.Field.superclass.constructor.call(this, config);
16334 };
16335
16336 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16337     /**
16338      * @cfg {String} fieldLabel Label to use when rendering a form.
16339      */
16340        /**
16341      * @cfg {String} qtip Mouse over tip
16342      */
16343      
16344     /**
16345      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16346      */
16347     invalidClass : "x-form-invalid",
16348     /**
16349      * @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")
16350      */
16351     invalidText : "The value in this field is invalid",
16352     /**
16353      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16354      */
16355     focusClass : "x-form-focus",
16356     /**
16357      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16358       automatic validation (defaults to "keyup").
16359      */
16360     validationEvent : "keyup",
16361     /**
16362      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16363      */
16364     validateOnBlur : true,
16365     /**
16366      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16367      */
16368     validationDelay : 250,
16369     /**
16370      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16371      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16372      */
16373     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16374     /**
16375      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16376      */
16377     fieldClass : "x-form-field",
16378     /**
16379      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16380      *<pre>
16381 Value         Description
16382 -----------   ----------------------------------------------------------------------
16383 qtip          Display a quick tip when the user hovers over the field
16384 title         Display a default browser title attribute popup
16385 under         Add a block div beneath the field containing the error text
16386 side          Add an error icon to the right of the field with a popup on hover
16387 [element id]  Add the error text directly to the innerHTML of the specified element
16388 </pre>
16389      */
16390     msgTarget : 'qtip',
16391     /**
16392      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16393      */
16394     msgFx : 'normal',
16395
16396     /**
16397      * @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.
16398      */
16399     readOnly : false,
16400
16401     /**
16402      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16403      */
16404     disabled : false,
16405
16406     /**
16407      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16408      */
16409     inputType : undefined,
16410     
16411     /**
16412      * @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).
16413          */
16414         tabIndex : undefined,
16415         
16416     // private
16417     isFormField : true,
16418
16419     // private
16420     hasFocus : false,
16421     /**
16422      * @property {Roo.Element} fieldEl
16423      * Element Containing the rendered Field (with label etc.)
16424      */
16425     /**
16426      * @cfg {Mixed} value A value to initialize this field with.
16427      */
16428     value : undefined,
16429
16430     /**
16431      * @cfg {String} name The field's HTML name attribute.
16432      */
16433     /**
16434      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16435      */
16436     // private
16437     loadedValue : false,
16438      
16439      
16440         // private ??
16441         initComponent : function(){
16442         Roo.form.Field.superclass.initComponent.call(this);
16443         this.addEvents({
16444             /**
16445              * @event focus
16446              * Fires when this field receives input focus.
16447              * @param {Roo.form.Field} this
16448              */
16449             focus : true,
16450             /**
16451              * @event blur
16452              * Fires when this field loses input focus.
16453              * @param {Roo.form.Field} this
16454              */
16455             blur : true,
16456             /**
16457              * @event specialkey
16458              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16459              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16460              * @param {Roo.form.Field} this
16461              * @param {Roo.EventObject} e The event object
16462              */
16463             specialkey : true,
16464             /**
16465              * @event change
16466              * Fires just before the field blurs if the field value has changed.
16467              * @param {Roo.form.Field} this
16468              * @param {Mixed} newValue The new value
16469              * @param {Mixed} oldValue The original value
16470              */
16471             change : true,
16472             /**
16473              * @event invalid
16474              * Fires after the field has been marked as invalid.
16475              * @param {Roo.form.Field} this
16476              * @param {String} msg The validation message
16477              */
16478             invalid : true,
16479             /**
16480              * @event valid
16481              * Fires after the field has been validated with no errors.
16482              * @param {Roo.form.Field} this
16483              */
16484             valid : true,
16485              /**
16486              * @event keyup
16487              * Fires after the key up
16488              * @param {Roo.form.Field} this
16489              * @param {Roo.EventObject}  e The event Object
16490              */
16491             keyup : true
16492         });
16493     },
16494
16495     /**
16496      * Returns the name attribute of the field if available
16497      * @return {String} name The field name
16498      */
16499     getName: function(){
16500          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16501     },
16502
16503     // private
16504     onRender : function(ct, position){
16505         Roo.form.Field.superclass.onRender.call(this, ct, position);
16506         if(!this.el){
16507             var cfg = this.getAutoCreate();
16508             if(!cfg.name){
16509                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16510             }
16511             if (!cfg.name.length) {
16512                 delete cfg.name;
16513             }
16514             if(this.inputType){
16515                 cfg.type = this.inputType;
16516             }
16517             this.el = ct.createChild(cfg, position);
16518         }
16519         var type = this.el.dom.type;
16520         if(type){
16521             if(type == 'password'){
16522                 type = 'text';
16523             }
16524             this.el.addClass('x-form-'+type);
16525         }
16526         if(this.readOnly){
16527             this.el.dom.readOnly = true;
16528         }
16529         if(this.tabIndex !== undefined){
16530             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16531         }
16532
16533         this.el.addClass([this.fieldClass, this.cls]);
16534         this.initValue();
16535     },
16536
16537     /**
16538      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16539      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16540      * @return {Roo.form.Field} this
16541      */
16542     applyTo : function(target){
16543         this.allowDomMove = false;
16544         this.el = Roo.get(target);
16545         this.render(this.el.dom.parentNode);
16546         return this;
16547     },
16548
16549     // private
16550     initValue : function(){
16551         if(this.value !== undefined){
16552             this.setValue(this.value);
16553         }else if(this.el.dom.value.length > 0){
16554             this.setValue(this.el.dom.value);
16555         }
16556     },
16557
16558     /**
16559      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16560      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16561      */
16562     isDirty : function() {
16563         if(this.disabled) {
16564             return false;
16565         }
16566         return String(this.getValue()) !== String(this.originalValue);
16567     },
16568
16569     /**
16570      * stores the current value in loadedValue
16571      */
16572     resetHasChanged : function()
16573     {
16574         this.loadedValue = String(this.getValue());
16575     },
16576     /**
16577      * checks the current value against the 'loaded' value.
16578      * Note - will return false if 'resetHasChanged' has not been called first.
16579      */
16580     hasChanged : function()
16581     {
16582         if(this.disabled || this.readOnly) {
16583             return false;
16584         }
16585         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16586     },
16587     
16588     
16589     
16590     // private
16591     afterRender : function(){
16592         Roo.form.Field.superclass.afterRender.call(this);
16593         this.initEvents();
16594     },
16595
16596     // private
16597     fireKey : function(e){
16598         //Roo.log('field ' + e.getKey());
16599         if(e.isNavKeyPress()){
16600             this.fireEvent("specialkey", this, e);
16601         }
16602     },
16603
16604     /**
16605      * Resets the current field value to the originally loaded value and clears any validation messages
16606      */
16607     reset : function(){
16608         this.setValue(this.resetValue);
16609         this.originalValue = this.getValue();
16610         this.clearInvalid();
16611     },
16612
16613     // private
16614     initEvents : function(){
16615         // safari killled keypress - so keydown is now used..
16616         this.el.on("keydown" , this.fireKey,  this);
16617         this.el.on("focus", this.onFocus,  this);
16618         this.el.on("blur", this.onBlur,  this);
16619         this.el.relayEvent('keyup', this);
16620
16621         // reference to original value for reset
16622         this.originalValue = this.getValue();
16623         this.resetValue =  this.getValue();
16624     },
16625
16626     // private
16627     onFocus : function(){
16628         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16629             this.el.addClass(this.focusClass);
16630         }
16631         if(!this.hasFocus){
16632             this.hasFocus = true;
16633             this.startValue = this.getValue();
16634             this.fireEvent("focus", this);
16635         }
16636     },
16637
16638     beforeBlur : Roo.emptyFn,
16639
16640     // private
16641     onBlur : function(){
16642         this.beforeBlur();
16643         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16644             this.el.removeClass(this.focusClass);
16645         }
16646         this.hasFocus = false;
16647         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16648             this.validate();
16649         }
16650         var v = this.getValue();
16651         if(String(v) !== String(this.startValue)){
16652             this.fireEvent('change', this, v, this.startValue);
16653         }
16654         this.fireEvent("blur", this);
16655     },
16656
16657     /**
16658      * Returns whether or not the field value is currently valid
16659      * @param {Boolean} preventMark True to disable marking the field invalid
16660      * @return {Boolean} True if the value is valid, else false
16661      */
16662     isValid : function(preventMark){
16663         if(this.disabled){
16664             return true;
16665         }
16666         var restore = this.preventMark;
16667         this.preventMark = preventMark === true;
16668         var v = this.validateValue(this.processValue(this.getRawValue()));
16669         this.preventMark = restore;
16670         return v;
16671     },
16672
16673     /**
16674      * Validates the field value
16675      * @return {Boolean} True if the value is valid, else false
16676      */
16677     validate : function(){
16678         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16679             this.clearInvalid();
16680             return true;
16681         }
16682         return false;
16683     },
16684
16685     processValue : function(value){
16686         return value;
16687     },
16688
16689     // private
16690     // Subclasses should provide the validation implementation by overriding this
16691     validateValue : function(value){
16692         return true;
16693     },
16694
16695     /**
16696      * Mark this field as invalid
16697      * @param {String} msg The validation message
16698      */
16699     markInvalid : function(msg){
16700         if(!this.rendered || this.preventMark){ // not rendered
16701             return;
16702         }
16703         
16704         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16705         
16706         obj.el.addClass(this.invalidClass);
16707         msg = msg || this.invalidText;
16708         switch(this.msgTarget){
16709             case 'qtip':
16710                 obj.el.dom.qtip = msg;
16711                 obj.el.dom.qclass = 'x-form-invalid-tip';
16712                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16713                     Roo.QuickTips.enable();
16714                 }
16715                 break;
16716             case 'title':
16717                 this.el.dom.title = msg;
16718                 break;
16719             case 'under':
16720                 if(!this.errorEl){
16721                     var elp = this.el.findParent('.x-form-element', 5, true);
16722                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16723                     this.errorEl.setWidth(elp.getWidth(true)-20);
16724                 }
16725                 this.errorEl.update(msg);
16726                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16727                 break;
16728             case 'side':
16729                 if(!this.errorIcon){
16730                     var elp = this.el.findParent('.x-form-element', 5, true);
16731                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16732                 }
16733                 this.alignErrorIcon();
16734                 this.errorIcon.dom.qtip = msg;
16735                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16736                 this.errorIcon.show();
16737                 this.on('resize', this.alignErrorIcon, this);
16738                 break;
16739             default:
16740                 var t = Roo.getDom(this.msgTarget);
16741                 t.innerHTML = msg;
16742                 t.style.display = this.msgDisplay;
16743                 break;
16744         }
16745         this.fireEvent('invalid', this, msg);
16746     },
16747
16748     // private
16749     alignErrorIcon : function(){
16750         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16751     },
16752
16753     /**
16754      * Clear any invalid styles/messages for this field
16755      */
16756     clearInvalid : function(){
16757         if(!this.rendered || this.preventMark){ // not rendered
16758             return;
16759         }
16760         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16761         
16762         obj.el.removeClass(this.invalidClass);
16763         switch(this.msgTarget){
16764             case 'qtip':
16765                 obj.el.dom.qtip = '';
16766                 break;
16767             case 'title':
16768                 this.el.dom.title = '';
16769                 break;
16770             case 'under':
16771                 if(this.errorEl){
16772                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16773                 }
16774                 break;
16775             case 'side':
16776                 if(this.errorIcon){
16777                     this.errorIcon.dom.qtip = '';
16778                     this.errorIcon.hide();
16779                     this.un('resize', this.alignErrorIcon, this);
16780                 }
16781                 break;
16782             default:
16783                 var t = Roo.getDom(this.msgTarget);
16784                 t.innerHTML = '';
16785                 t.style.display = 'none';
16786                 break;
16787         }
16788         this.fireEvent('valid', this);
16789     },
16790
16791     /**
16792      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16793      * @return {Mixed} value The field value
16794      */
16795     getRawValue : function(){
16796         var v = this.el.getValue();
16797         
16798         return v;
16799     },
16800
16801     /**
16802      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16803      * @return {Mixed} value The field value
16804      */
16805     getValue : function(){
16806         var v = this.el.getValue();
16807          
16808         return v;
16809     },
16810
16811     /**
16812      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16813      * @param {Mixed} value The value to set
16814      */
16815     setRawValue : function(v){
16816         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16817     },
16818
16819     /**
16820      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16821      * @param {Mixed} value The value to set
16822      */
16823     setValue : function(v){
16824         this.value = v;
16825         if(this.rendered){
16826             this.el.dom.value = (v === null || v === undefined ? '' : v);
16827              this.validate();
16828         }
16829     },
16830
16831     adjustSize : function(w, h){
16832         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16833         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16834         return s;
16835     },
16836
16837     adjustWidth : function(tag, w){
16838         tag = tag.toLowerCase();
16839         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16840             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16841                 if(tag == 'input'){
16842                     return w + 2;
16843                 }
16844                 if(tag == 'textarea'){
16845                     return w-2;
16846                 }
16847             }else if(Roo.isOpera){
16848                 if(tag == 'input'){
16849                     return w + 2;
16850                 }
16851                 if(tag == 'textarea'){
16852                     return w-2;
16853                 }
16854             }
16855         }
16856         return w;
16857     }
16858 });
16859
16860
16861 // anything other than normal should be considered experimental
16862 Roo.form.Field.msgFx = {
16863     normal : {
16864         show: function(msgEl, f){
16865             msgEl.setDisplayed('block');
16866         },
16867
16868         hide : function(msgEl, f){
16869             msgEl.setDisplayed(false).update('');
16870         }
16871     },
16872
16873     slide : {
16874         show: function(msgEl, f){
16875             msgEl.slideIn('t', {stopFx:true});
16876         },
16877
16878         hide : function(msgEl, f){
16879             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16880         }
16881     },
16882
16883     slideRight : {
16884         show: function(msgEl, f){
16885             msgEl.fixDisplay();
16886             msgEl.alignTo(f.el, 'tl-tr');
16887             msgEl.slideIn('l', {stopFx:true});
16888         },
16889
16890         hide : function(msgEl, f){
16891             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16892         }
16893     }
16894 };/*
16895  * Based on:
16896  * Ext JS Library 1.1.1
16897  * Copyright(c) 2006-2007, Ext JS, LLC.
16898  *
16899  * Originally Released Under LGPL - original licence link has changed is not relivant.
16900  *
16901  * Fork - LGPL
16902  * <script type="text/javascript">
16903  */
16904  
16905
16906 /**
16907  * @class Roo.form.TextField
16908  * @extends Roo.form.Field
16909  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16910  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16911  * @constructor
16912  * Creates a new TextField
16913  * @param {Object} config Configuration options
16914  */
16915 Roo.form.TextField = function(config){
16916     Roo.form.TextField.superclass.constructor.call(this, config);
16917     this.addEvents({
16918         /**
16919          * @event autosize
16920          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16921          * according to the default logic, but this event provides a hook for the developer to apply additional
16922          * logic at runtime to resize the field if needed.
16923              * @param {Roo.form.Field} this This text field
16924              * @param {Number} width The new field width
16925              */
16926         autosize : true
16927     });
16928 };
16929
16930 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16931     /**
16932      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16933      */
16934     grow : false,
16935     /**
16936      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16937      */
16938     growMin : 30,
16939     /**
16940      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16941      */
16942     growMax : 800,
16943     /**
16944      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16945      */
16946     vtype : null,
16947     /**
16948      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16949      */
16950     maskRe : null,
16951     /**
16952      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16953      */
16954     disableKeyFilter : false,
16955     /**
16956      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16957      */
16958     allowBlank : true,
16959     /**
16960      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16961      */
16962     minLength : 0,
16963     /**
16964      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16965      */
16966     maxLength : Number.MAX_VALUE,
16967     /**
16968      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16969      */
16970     minLengthText : "The minimum length for this field is {0}",
16971     /**
16972      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16973      */
16974     maxLengthText : "The maximum length for this field is {0}",
16975     /**
16976      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16977      */
16978     selectOnFocus : false,
16979     /**
16980      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16981      */
16982     blankText : "This field is required",
16983     /**
16984      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16985      * If available, this function will be called only after the basic validators all return true, and will be passed the
16986      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16987      */
16988     validator : null,
16989     /**
16990      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16991      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16992      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16993      */
16994     regex : null,
16995     /**
16996      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16997      */
16998     regexText : "",
16999     /**
17000      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17001      */
17002     emptyText : null,
17003    
17004
17005     // private
17006     initEvents : function()
17007     {
17008         if (this.emptyText) {
17009             this.el.attr('placeholder', this.emptyText);
17010         }
17011         
17012         Roo.form.TextField.superclass.initEvents.call(this);
17013         if(this.validationEvent == 'keyup'){
17014             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17015             this.el.on('keyup', this.filterValidation, this);
17016         }
17017         else if(this.validationEvent !== false){
17018             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17019         }
17020         
17021         if(this.selectOnFocus){
17022             this.on("focus", this.preFocus, this);
17023             
17024         }
17025         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17026             this.el.on("keypress", this.filterKeys, this);
17027         }
17028         if(this.grow){
17029             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17030             this.el.on("click", this.autoSize,  this);
17031         }
17032         if(this.el.is('input[type=password]') && Roo.isSafari){
17033             this.el.on('keydown', this.SafariOnKeyDown, this);
17034         }
17035     },
17036
17037     processValue : function(value){
17038         if(this.stripCharsRe){
17039             var newValue = value.replace(this.stripCharsRe, '');
17040             if(newValue !== value){
17041                 this.setRawValue(newValue);
17042                 return newValue;
17043             }
17044         }
17045         return value;
17046     },
17047
17048     filterValidation : function(e){
17049         if(!e.isNavKeyPress()){
17050             this.validationTask.delay(this.validationDelay);
17051         }
17052     },
17053
17054     // private
17055     onKeyUp : function(e){
17056         if(!e.isNavKeyPress()){
17057             this.autoSize();
17058         }
17059     },
17060
17061     /**
17062      * Resets the current field value to the originally-loaded value and clears any validation messages.
17063      *  
17064      */
17065     reset : function(){
17066         Roo.form.TextField.superclass.reset.call(this);
17067        
17068     },
17069
17070     
17071     // private
17072     preFocus : function(){
17073         
17074         if(this.selectOnFocus){
17075             this.el.dom.select();
17076         }
17077     },
17078
17079     
17080     // private
17081     filterKeys : function(e){
17082         var k = e.getKey();
17083         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17084             return;
17085         }
17086         var c = e.getCharCode(), cc = String.fromCharCode(c);
17087         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17088             return;
17089         }
17090         if(!this.maskRe.test(cc)){
17091             e.stopEvent();
17092         }
17093     },
17094
17095     setValue : function(v){
17096         
17097         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17098         
17099         this.autoSize();
17100     },
17101
17102     /**
17103      * Validates a value according to the field's validation rules and marks the field as invalid
17104      * if the validation fails
17105      * @param {Mixed} value The value to validate
17106      * @return {Boolean} True if the value is valid, else false
17107      */
17108     validateValue : function(value){
17109         if(value.length < 1)  { // if it's blank
17110              if(this.allowBlank){
17111                 this.clearInvalid();
17112                 return true;
17113              }else{
17114                 this.markInvalid(this.blankText);
17115                 return false;
17116              }
17117         }
17118         if(value.length < this.minLength){
17119             this.markInvalid(String.format(this.minLengthText, this.minLength));
17120             return false;
17121         }
17122         if(value.length > this.maxLength){
17123             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17124             return false;
17125         }
17126         if(this.vtype){
17127             var vt = Roo.form.VTypes;
17128             if(!vt[this.vtype](value, this)){
17129                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17130                 return false;
17131             }
17132         }
17133         if(typeof this.validator == "function"){
17134             var msg = this.validator(value);
17135             if(msg !== true){
17136                 this.markInvalid(msg);
17137                 return false;
17138             }
17139         }
17140         if(this.regex && !this.regex.test(value)){
17141             this.markInvalid(this.regexText);
17142             return false;
17143         }
17144         return true;
17145     },
17146
17147     /**
17148      * Selects text in this field
17149      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17150      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17151      */
17152     selectText : function(start, end){
17153         var v = this.getRawValue();
17154         if(v.length > 0){
17155             start = start === undefined ? 0 : start;
17156             end = end === undefined ? v.length : end;
17157             var d = this.el.dom;
17158             if(d.setSelectionRange){
17159                 d.setSelectionRange(start, end);
17160             }else if(d.createTextRange){
17161                 var range = d.createTextRange();
17162                 range.moveStart("character", start);
17163                 range.moveEnd("character", v.length-end);
17164                 range.select();
17165             }
17166         }
17167     },
17168
17169     /**
17170      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17171      * This only takes effect if grow = true, and fires the autosize event.
17172      */
17173     autoSize : function(){
17174         if(!this.grow || !this.rendered){
17175             return;
17176         }
17177         if(!this.metrics){
17178             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17179         }
17180         var el = this.el;
17181         var v = el.dom.value;
17182         var d = document.createElement('div');
17183         d.appendChild(document.createTextNode(v));
17184         v = d.innerHTML;
17185         d = null;
17186         v += "&#160;";
17187         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17188         this.el.setWidth(w);
17189         this.fireEvent("autosize", this, w);
17190     },
17191     
17192     // private
17193     SafariOnKeyDown : function(event)
17194     {
17195         // this is a workaround for a password hang bug on chrome/ webkit.
17196         
17197         var isSelectAll = false;
17198         
17199         if(this.el.dom.selectionEnd > 0){
17200             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17201         }
17202         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17203             event.preventDefault();
17204             this.setValue('');
17205             return;
17206         }
17207         
17208         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17209             
17210             event.preventDefault();
17211             // this is very hacky as keydown always get's upper case.
17212             
17213             var cc = String.fromCharCode(event.getCharCode());
17214             
17215             
17216             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17217             
17218         }
17219         
17220         
17221     }
17222 });/*
17223  * Based on:
17224  * Ext JS Library 1.1.1
17225  * Copyright(c) 2006-2007, Ext JS, LLC.
17226  *
17227  * Originally Released Under LGPL - original licence link has changed is not relivant.
17228  *
17229  * Fork - LGPL
17230  * <script type="text/javascript">
17231  */
17232  
17233 /**
17234  * @class Roo.form.Hidden
17235  * @extends Roo.form.TextField
17236  * Simple Hidden element used on forms 
17237  * 
17238  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17239  * 
17240  * @constructor
17241  * Creates a new Hidden form element.
17242  * @param {Object} config Configuration options
17243  */
17244
17245
17246
17247 // easy hidden field...
17248 Roo.form.Hidden = function(config){
17249     Roo.form.Hidden.superclass.constructor.call(this, config);
17250 };
17251   
17252 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17253     fieldLabel:      '',
17254     inputType:      'hidden',
17255     width:          50,
17256     allowBlank:     true,
17257     labelSeparator: '',
17258     hidden:         true,
17259     itemCls :       'x-form-item-display-none'
17260
17261
17262 });
17263
17264
17265 /*
17266  * Based on:
17267  * Ext JS Library 1.1.1
17268  * Copyright(c) 2006-2007, Ext JS, LLC.
17269  *
17270  * Originally Released Under LGPL - original licence link has changed is not relivant.
17271  *
17272  * Fork - LGPL
17273  * <script type="text/javascript">
17274  */
17275  
17276 /**
17277  * @class Roo.form.TriggerField
17278  * @extends Roo.form.TextField
17279  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17280  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17281  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17282  * for which you can provide a custom implementation.  For example:
17283  * <pre><code>
17284 var trigger = new Roo.form.TriggerField();
17285 trigger.onTriggerClick = myTriggerFn;
17286 trigger.applyTo('my-field');
17287 </code></pre>
17288  *
17289  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17290  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17291  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17292  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17293  * @constructor
17294  * Create a new TriggerField.
17295  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17296  * to the base TextField)
17297  */
17298 Roo.form.TriggerField = function(config){
17299     this.mimicing = false;
17300     Roo.form.TriggerField.superclass.constructor.call(this, config);
17301 };
17302
17303 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17304     /**
17305      * @cfg {String} triggerClass A CSS class to apply to the trigger
17306      */
17307     /**
17308      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17309      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17310      */
17311     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17312     /**
17313      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17314      */
17315     hideTrigger:false,
17316
17317     /** @cfg {Boolean} grow @hide */
17318     /** @cfg {Number} growMin @hide */
17319     /** @cfg {Number} growMax @hide */
17320
17321     /**
17322      * @hide 
17323      * @method
17324      */
17325     autoSize: Roo.emptyFn,
17326     // private
17327     monitorTab : true,
17328     // private
17329     deferHeight : true,
17330
17331     
17332     actionMode : 'wrap',
17333     // private
17334     onResize : function(w, h){
17335         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17336         if(typeof w == 'number'){
17337             var x = w - this.trigger.getWidth();
17338             this.el.setWidth(this.adjustWidth('input', x));
17339             this.trigger.setStyle('left', x+'px');
17340         }
17341     },
17342
17343     // private
17344     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17345
17346     // private
17347     getResizeEl : function(){
17348         return this.wrap;
17349     },
17350
17351     // private
17352     getPositionEl : function(){
17353         return this.wrap;
17354     },
17355
17356     // private
17357     alignErrorIcon : function(){
17358         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17359     },
17360
17361     // private
17362     onRender : function(ct, position){
17363         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17364         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17365         this.trigger = this.wrap.createChild(this.triggerConfig ||
17366                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17367         if(this.hideTrigger){
17368             this.trigger.setDisplayed(false);
17369         }
17370         this.initTrigger();
17371         if(!this.width){
17372             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17373         }
17374     },
17375
17376     // private
17377     initTrigger : function(){
17378         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17379         this.trigger.addClassOnOver('x-form-trigger-over');
17380         this.trigger.addClassOnClick('x-form-trigger-click');
17381     },
17382
17383     // private
17384     onDestroy : function(){
17385         if(this.trigger){
17386             this.trigger.removeAllListeners();
17387             this.trigger.remove();
17388         }
17389         if(this.wrap){
17390             this.wrap.remove();
17391         }
17392         Roo.form.TriggerField.superclass.onDestroy.call(this);
17393     },
17394
17395     // private
17396     onFocus : function(){
17397         Roo.form.TriggerField.superclass.onFocus.call(this);
17398         if(!this.mimicing){
17399             this.wrap.addClass('x-trigger-wrap-focus');
17400             this.mimicing = true;
17401             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17402             if(this.monitorTab){
17403                 this.el.on("keydown", this.checkTab, this);
17404             }
17405         }
17406     },
17407
17408     // private
17409     checkTab : function(e){
17410         if(e.getKey() == e.TAB){
17411             this.triggerBlur();
17412         }
17413     },
17414
17415     // private
17416     onBlur : function(){
17417         // do nothing
17418     },
17419
17420     // private
17421     mimicBlur : function(e, t){
17422         if(!this.wrap.contains(t) && this.validateBlur()){
17423             this.triggerBlur();
17424         }
17425     },
17426
17427     // private
17428     triggerBlur : function(){
17429         this.mimicing = false;
17430         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17431         if(this.monitorTab){
17432             this.el.un("keydown", this.checkTab, this);
17433         }
17434         this.wrap.removeClass('x-trigger-wrap-focus');
17435         Roo.form.TriggerField.superclass.onBlur.call(this);
17436     },
17437
17438     // private
17439     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17440     validateBlur : function(e, t){
17441         return true;
17442     },
17443
17444     // private
17445     onDisable : function(){
17446         Roo.form.TriggerField.superclass.onDisable.call(this);
17447         if(this.wrap){
17448             this.wrap.addClass('x-item-disabled');
17449         }
17450     },
17451
17452     // private
17453     onEnable : function(){
17454         Roo.form.TriggerField.superclass.onEnable.call(this);
17455         if(this.wrap){
17456             this.wrap.removeClass('x-item-disabled');
17457         }
17458     },
17459
17460     // private
17461     onShow : function(){
17462         var ae = this.getActionEl();
17463         
17464         if(ae){
17465             ae.dom.style.display = '';
17466             ae.dom.style.visibility = 'visible';
17467         }
17468     },
17469
17470     // private
17471     
17472     onHide : function(){
17473         var ae = this.getActionEl();
17474         ae.dom.style.display = 'none';
17475     },
17476
17477     /**
17478      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17479      * by an implementing function.
17480      * @method
17481      * @param {EventObject} e
17482      */
17483     onTriggerClick : Roo.emptyFn
17484 });
17485
17486 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17487 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17488 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17489 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17490     initComponent : function(){
17491         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17492
17493         this.triggerConfig = {
17494             tag:'span', cls:'x-form-twin-triggers', cn:[
17495             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17496             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17497         ]};
17498     },
17499
17500     getTrigger : function(index){
17501         return this.triggers[index];
17502     },
17503
17504     initTrigger : function(){
17505         var ts = this.trigger.select('.x-form-trigger', true);
17506         this.wrap.setStyle('overflow', 'hidden');
17507         var triggerField = this;
17508         ts.each(function(t, all, index){
17509             t.hide = function(){
17510                 var w = triggerField.wrap.getWidth();
17511                 this.dom.style.display = 'none';
17512                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17513             };
17514             t.show = function(){
17515                 var w = triggerField.wrap.getWidth();
17516                 this.dom.style.display = '';
17517                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17518             };
17519             var triggerIndex = 'Trigger'+(index+1);
17520
17521             if(this['hide'+triggerIndex]){
17522                 t.dom.style.display = 'none';
17523             }
17524             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17525             t.addClassOnOver('x-form-trigger-over');
17526             t.addClassOnClick('x-form-trigger-click');
17527         }, this);
17528         this.triggers = ts.elements;
17529     },
17530
17531     onTrigger1Click : Roo.emptyFn,
17532     onTrigger2Click : Roo.emptyFn
17533 });/*
17534  * Based on:
17535  * Ext JS Library 1.1.1
17536  * Copyright(c) 2006-2007, Ext JS, LLC.
17537  *
17538  * Originally Released Under LGPL - original licence link has changed is not relivant.
17539  *
17540  * Fork - LGPL
17541  * <script type="text/javascript">
17542  */
17543  
17544 /**
17545  * @class Roo.form.TextArea
17546  * @extends Roo.form.TextField
17547  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17548  * support for auto-sizing.
17549  * @constructor
17550  * Creates a new TextArea
17551  * @param {Object} config Configuration options
17552  */
17553 Roo.form.TextArea = function(config){
17554     Roo.form.TextArea.superclass.constructor.call(this, config);
17555     // these are provided exchanges for backwards compat
17556     // minHeight/maxHeight were replaced by growMin/growMax to be
17557     // compatible with TextField growing config values
17558     if(this.minHeight !== undefined){
17559         this.growMin = this.minHeight;
17560     }
17561     if(this.maxHeight !== undefined){
17562         this.growMax = this.maxHeight;
17563     }
17564 };
17565
17566 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17567     /**
17568      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17569      */
17570     growMin : 60,
17571     /**
17572      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17573      */
17574     growMax: 1000,
17575     /**
17576      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17577      * in the field (equivalent to setting overflow: hidden, defaults to false)
17578      */
17579     preventScrollbars: false,
17580     /**
17581      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17582      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17583      */
17584
17585     // private
17586     onRender : function(ct, position){
17587         if(!this.el){
17588             this.defaultAutoCreate = {
17589                 tag: "textarea",
17590                 style:"width:300px;height:60px;",
17591                 autocomplete: "new-password"
17592             };
17593         }
17594         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17595         if(this.grow){
17596             this.textSizeEl = Roo.DomHelper.append(document.body, {
17597                 tag: "pre", cls: "x-form-grow-sizer"
17598             });
17599             if(this.preventScrollbars){
17600                 this.el.setStyle("overflow", "hidden");
17601             }
17602             this.el.setHeight(this.growMin);
17603         }
17604     },
17605
17606     onDestroy : function(){
17607         if(this.textSizeEl){
17608             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17609         }
17610         Roo.form.TextArea.superclass.onDestroy.call(this);
17611     },
17612
17613     // private
17614     onKeyUp : function(e){
17615         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17616             this.autoSize();
17617         }
17618     },
17619
17620     /**
17621      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17622      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17623      */
17624     autoSize : function(){
17625         if(!this.grow || !this.textSizeEl){
17626             return;
17627         }
17628         var el = this.el;
17629         var v = el.dom.value;
17630         var ts = this.textSizeEl;
17631
17632         ts.innerHTML = '';
17633         ts.appendChild(document.createTextNode(v));
17634         v = ts.innerHTML;
17635
17636         Roo.fly(ts).setWidth(this.el.getWidth());
17637         if(v.length < 1){
17638             v = "&#160;&#160;";
17639         }else{
17640             if(Roo.isIE){
17641                 v = v.replace(/\n/g, '<p>&#160;</p>');
17642             }
17643             v += "&#160;\n&#160;";
17644         }
17645         ts.innerHTML = v;
17646         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17647         if(h != this.lastHeight){
17648             this.lastHeight = h;
17649             this.el.setHeight(h);
17650             this.fireEvent("autosize", this, h);
17651         }
17652     }
17653 });/*
17654  * Based on:
17655  * Ext JS Library 1.1.1
17656  * Copyright(c) 2006-2007, Ext JS, LLC.
17657  *
17658  * Originally Released Under LGPL - original licence link has changed is not relivant.
17659  *
17660  * Fork - LGPL
17661  * <script type="text/javascript">
17662  */
17663  
17664
17665 /**
17666  * @class Roo.form.NumberField
17667  * @extends Roo.form.TextField
17668  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17669  * @constructor
17670  * Creates a new NumberField
17671  * @param {Object} config Configuration options
17672  */
17673 Roo.form.NumberField = function(config){
17674     Roo.form.NumberField.superclass.constructor.call(this, config);
17675 };
17676
17677 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17678     /**
17679      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17680      */
17681     fieldClass: "x-form-field x-form-num-field",
17682     /**
17683      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17684      */
17685     allowDecimals : true,
17686     /**
17687      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17688      */
17689     decimalSeparator : ".",
17690     /**
17691      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17692      */
17693     decimalPrecision : 2,
17694     /**
17695      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17696      */
17697     allowNegative : true,
17698     /**
17699      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17700      */
17701     minValue : Number.NEGATIVE_INFINITY,
17702     /**
17703      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17704      */
17705     maxValue : Number.MAX_VALUE,
17706     /**
17707      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17708      */
17709     minText : "The minimum value for this field is {0}",
17710     /**
17711      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17712      */
17713     maxText : "The maximum value for this field is {0}",
17714     /**
17715      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17716      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17717      */
17718     nanText : "{0} is not a valid number",
17719
17720     // private
17721     initEvents : function(){
17722         Roo.form.NumberField.superclass.initEvents.call(this);
17723         var allowed = "0123456789";
17724         if(this.allowDecimals){
17725             allowed += this.decimalSeparator;
17726         }
17727         if(this.allowNegative){
17728             allowed += "-";
17729         }
17730         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17731         var keyPress = function(e){
17732             var k = e.getKey();
17733             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17734                 return;
17735             }
17736             var c = e.getCharCode();
17737             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17738                 e.stopEvent();
17739             }
17740         };
17741         this.el.on("keypress", keyPress, this);
17742     },
17743
17744     // private
17745     validateValue : function(value){
17746         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17747             return false;
17748         }
17749         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17750              return true;
17751         }
17752         var num = this.parseValue(value);
17753         if(isNaN(num)){
17754             this.markInvalid(String.format(this.nanText, value));
17755             return false;
17756         }
17757         if(num < this.minValue){
17758             this.markInvalid(String.format(this.minText, this.minValue));
17759             return false;
17760         }
17761         if(num > this.maxValue){
17762             this.markInvalid(String.format(this.maxText, this.maxValue));
17763             return false;
17764         }
17765         return true;
17766     },
17767
17768     getValue : function(){
17769         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17770     },
17771
17772     // private
17773     parseValue : function(value){
17774         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17775         return isNaN(value) ? '' : value;
17776     },
17777
17778     // private
17779     fixPrecision : function(value){
17780         var nan = isNaN(value);
17781         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17782             return nan ? '' : value;
17783         }
17784         return parseFloat(value).toFixed(this.decimalPrecision);
17785     },
17786
17787     setValue : function(v){
17788         v = this.fixPrecision(v);
17789         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17790     },
17791
17792     // private
17793     decimalPrecisionFcn : function(v){
17794         return Math.floor(v);
17795     },
17796
17797     beforeBlur : function(){
17798         var v = this.parseValue(this.getRawValue());
17799         if(v){
17800             this.setValue(v);
17801         }
17802     }
17803 });/*
17804  * Based on:
17805  * Ext JS Library 1.1.1
17806  * Copyright(c) 2006-2007, Ext JS, LLC.
17807  *
17808  * Originally Released Under LGPL - original licence link has changed is not relivant.
17809  *
17810  * Fork - LGPL
17811  * <script type="text/javascript">
17812  */
17813  
17814 /**
17815  * @class Roo.form.DateField
17816  * @extends Roo.form.TriggerField
17817  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17818 * @constructor
17819 * Create a new DateField
17820 * @param {Object} config
17821  */
17822 Roo.form.DateField = function(config){
17823     Roo.form.DateField.superclass.constructor.call(this, config);
17824     
17825       this.addEvents({
17826          
17827         /**
17828          * @event select
17829          * Fires when a date is selected
17830              * @param {Roo.form.DateField} combo This combo box
17831              * @param {Date} date The date selected
17832              */
17833         'select' : true
17834          
17835     });
17836     
17837     
17838     if(typeof this.minValue == "string") {
17839         this.minValue = this.parseDate(this.minValue);
17840     }
17841     if(typeof this.maxValue == "string") {
17842         this.maxValue = this.parseDate(this.maxValue);
17843     }
17844     this.ddMatch = null;
17845     if(this.disabledDates){
17846         var dd = this.disabledDates;
17847         var re = "(?:";
17848         for(var i = 0; i < dd.length; i++){
17849             re += dd[i];
17850             if(i != dd.length-1) {
17851                 re += "|";
17852             }
17853         }
17854         this.ddMatch = new RegExp(re + ")");
17855     }
17856 };
17857
17858 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17859     /**
17860      * @cfg {String} format
17861      * The default date format string which can be overriden for localization support.  The format must be
17862      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17863      */
17864     format : "m/d/y",
17865     /**
17866      * @cfg {String} altFormats
17867      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17868      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17869      */
17870     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17871     /**
17872      * @cfg {Array} disabledDays
17873      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17874      */
17875     disabledDays : null,
17876     /**
17877      * @cfg {String} disabledDaysText
17878      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17879      */
17880     disabledDaysText : "Disabled",
17881     /**
17882      * @cfg {Array} disabledDates
17883      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17884      * expression so they are very powerful. Some examples:
17885      * <ul>
17886      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17887      * <li>["03/08", "09/16"] would disable those days for every year</li>
17888      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17889      * <li>["03/../2006"] would disable every day in March 2006</li>
17890      * <li>["^03"] would disable every day in every March</li>
17891      * </ul>
17892      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17893      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17894      */
17895     disabledDates : null,
17896     /**
17897      * @cfg {String} disabledDatesText
17898      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17899      */
17900     disabledDatesText : "Disabled",
17901     /**
17902      * @cfg {Date/String} minValue
17903      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17904      * valid format (defaults to null).
17905      */
17906     minValue : null,
17907     /**
17908      * @cfg {Date/String} maxValue
17909      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17910      * valid format (defaults to null).
17911      */
17912     maxValue : null,
17913     /**
17914      * @cfg {String} minText
17915      * The error text to display when the date in the cell is before minValue (defaults to
17916      * 'The date in this field must be after {minValue}').
17917      */
17918     minText : "The date in this field must be equal to or after {0}",
17919     /**
17920      * @cfg {String} maxText
17921      * The error text to display when the date in the cell is after maxValue (defaults to
17922      * 'The date in this field must be before {maxValue}').
17923      */
17924     maxText : "The date in this field must be equal to or before {0}",
17925     /**
17926      * @cfg {String} invalidText
17927      * The error text to display when the date in the field is invalid (defaults to
17928      * '{value} is not a valid date - it must be in the format {format}').
17929      */
17930     invalidText : "{0} is not a valid date - it must be in the format {1}",
17931     /**
17932      * @cfg {String} triggerClass
17933      * An additional CSS class used to style the trigger button.  The trigger will always get the
17934      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17935      * which displays a calendar icon).
17936      */
17937     triggerClass : 'x-form-date-trigger',
17938     
17939
17940     /**
17941      * @cfg {Boolean} useIso
17942      * if enabled, then the date field will use a hidden field to store the 
17943      * real value as iso formated date. default (false)
17944      */ 
17945     useIso : false,
17946     /**
17947      * @cfg {String/Object} autoCreate
17948      * A DomHelper element spec, or true for a default element spec (defaults to
17949      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17950      */ 
17951     // private
17952     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17953     
17954     // private
17955     hiddenField: false,
17956     
17957     onRender : function(ct, position)
17958     {
17959         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17960         if (this.useIso) {
17961             //this.el.dom.removeAttribute('name'); 
17962             Roo.log("Changing name?");
17963             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17964             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17965                     'before', true);
17966             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17967             // prevent input submission
17968             this.hiddenName = this.name;
17969         }
17970             
17971             
17972     },
17973     
17974     // private
17975     validateValue : function(value)
17976     {
17977         value = this.formatDate(value);
17978         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17979             Roo.log('super failed');
17980             return false;
17981         }
17982         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17983              return true;
17984         }
17985         var svalue = value;
17986         value = this.parseDate(value);
17987         if(!value){
17988             Roo.log('parse date failed' + svalue);
17989             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17990             return false;
17991         }
17992         var time = value.getTime();
17993         if(this.minValue && time < this.minValue.getTime()){
17994             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17995             return false;
17996         }
17997         if(this.maxValue && time > this.maxValue.getTime()){
17998             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17999             return false;
18000         }
18001         if(this.disabledDays){
18002             var day = value.getDay();
18003             for(var i = 0; i < this.disabledDays.length; i++) {
18004                 if(day === this.disabledDays[i]){
18005                     this.markInvalid(this.disabledDaysText);
18006                     return false;
18007                 }
18008             }
18009         }
18010         var fvalue = this.formatDate(value);
18011         if(this.ddMatch && this.ddMatch.test(fvalue)){
18012             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18013             return false;
18014         }
18015         return true;
18016     },
18017
18018     // private
18019     // Provides logic to override the default TriggerField.validateBlur which just returns true
18020     validateBlur : function(){
18021         return !this.menu || !this.menu.isVisible();
18022     },
18023     
18024     getName: function()
18025     {
18026         // returns hidden if it's set..
18027         if (!this.rendered) {return ''};
18028         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18029         
18030     },
18031
18032     /**
18033      * Returns the current date value of the date field.
18034      * @return {Date} The date value
18035      */
18036     getValue : function(){
18037         
18038         return  this.hiddenField ?
18039                 this.hiddenField.value :
18040                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18041     },
18042
18043     /**
18044      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18045      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18046      * (the default format used is "m/d/y").
18047      * <br />Usage:
18048      * <pre><code>
18049 //All of these calls set the same date value (May 4, 2006)
18050
18051 //Pass a date object:
18052 var dt = new Date('5/4/06');
18053 dateField.setValue(dt);
18054
18055 //Pass a date string (default format):
18056 dateField.setValue('5/4/06');
18057
18058 //Pass a date string (custom format):
18059 dateField.format = 'Y-m-d';
18060 dateField.setValue('2006-5-4');
18061 </code></pre>
18062      * @param {String/Date} date The date or valid date string
18063      */
18064     setValue : function(date){
18065         if (this.hiddenField) {
18066             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18067         }
18068         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18069         // make sure the value field is always stored as a date..
18070         this.value = this.parseDate(date);
18071         
18072         
18073     },
18074
18075     // private
18076     parseDate : function(value){
18077         if(!value || value instanceof Date){
18078             return value;
18079         }
18080         var v = Date.parseDate(value, this.format);
18081          if (!v && this.useIso) {
18082             v = Date.parseDate(value, 'Y-m-d');
18083         }
18084         if(!v && this.altFormats){
18085             if(!this.altFormatsArray){
18086                 this.altFormatsArray = this.altFormats.split("|");
18087             }
18088             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18089                 v = Date.parseDate(value, this.altFormatsArray[i]);
18090             }
18091         }
18092         return v;
18093     },
18094
18095     // private
18096     formatDate : function(date, fmt){
18097         return (!date || !(date instanceof Date)) ?
18098                date : date.dateFormat(fmt || this.format);
18099     },
18100
18101     // private
18102     menuListeners : {
18103         select: function(m, d){
18104             
18105             this.setValue(d);
18106             this.fireEvent('select', this, d);
18107         },
18108         show : function(){ // retain focus styling
18109             this.onFocus();
18110         },
18111         hide : function(){
18112             this.focus.defer(10, this);
18113             var ml = this.menuListeners;
18114             this.menu.un("select", ml.select,  this);
18115             this.menu.un("show", ml.show,  this);
18116             this.menu.un("hide", ml.hide,  this);
18117         }
18118     },
18119
18120     // private
18121     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18122     onTriggerClick : function(){
18123         if(this.disabled){
18124             return;
18125         }
18126         if(this.menu == null){
18127             this.menu = new Roo.menu.DateMenu();
18128         }
18129         Roo.apply(this.menu.picker,  {
18130             showClear: this.allowBlank,
18131             minDate : this.minValue,
18132             maxDate : this.maxValue,
18133             disabledDatesRE : this.ddMatch,
18134             disabledDatesText : this.disabledDatesText,
18135             disabledDays : this.disabledDays,
18136             disabledDaysText : this.disabledDaysText,
18137             format : this.useIso ? 'Y-m-d' : this.format,
18138             minText : String.format(this.minText, this.formatDate(this.minValue)),
18139             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18140         });
18141         this.menu.on(Roo.apply({}, this.menuListeners, {
18142             scope:this
18143         }));
18144         this.menu.picker.setValue(this.getValue() || new Date());
18145         this.menu.show(this.el, "tl-bl?");
18146     },
18147
18148     beforeBlur : function(){
18149         var v = this.parseDate(this.getRawValue());
18150         if(v){
18151             this.setValue(v);
18152         }
18153     },
18154
18155     /*@
18156      * overide
18157      * 
18158      */
18159     isDirty : function() {
18160         if(this.disabled) {
18161             return false;
18162         }
18163         
18164         if(typeof(this.startValue) === 'undefined'){
18165             return false;
18166         }
18167         
18168         return String(this.getValue()) !== String(this.startValue);
18169         
18170     }
18171 });/*
18172  * Based on:
18173  * Ext JS Library 1.1.1
18174  * Copyright(c) 2006-2007, Ext JS, LLC.
18175  *
18176  * Originally Released Under LGPL - original licence link has changed is not relivant.
18177  *
18178  * Fork - LGPL
18179  * <script type="text/javascript">
18180  */
18181  
18182 /**
18183  * @class Roo.form.MonthField
18184  * @extends Roo.form.TriggerField
18185  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18186 * @constructor
18187 * Create a new MonthField
18188 * @param {Object} config
18189  */
18190 Roo.form.MonthField = function(config){
18191     
18192     Roo.form.MonthField.superclass.constructor.call(this, config);
18193     
18194       this.addEvents({
18195          
18196         /**
18197          * @event select
18198          * Fires when a date is selected
18199              * @param {Roo.form.MonthFieeld} combo This combo box
18200              * @param {Date} date The date selected
18201              */
18202         'select' : true
18203          
18204     });
18205     
18206     
18207     if(typeof this.minValue == "string") {
18208         this.minValue = this.parseDate(this.minValue);
18209     }
18210     if(typeof this.maxValue == "string") {
18211         this.maxValue = this.parseDate(this.maxValue);
18212     }
18213     this.ddMatch = null;
18214     if(this.disabledDates){
18215         var dd = this.disabledDates;
18216         var re = "(?:";
18217         for(var i = 0; i < dd.length; i++){
18218             re += dd[i];
18219             if(i != dd.length-1) {
18220                 re += "|";
18221             }
18222         }
18223         this.ddMatch = new RegExp(re + ")");
18224     }
18225 };
18226
18227 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18228     /**
18229      * @cfg {String} format
18230      * The default date format string which can be overriden for localization support.  The format must be
18231      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18232      */
18233     format : "M Y",
18234     /**
18235      * @cfg {String} altFormats
18236      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18237      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18238      */
18239     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18240     /**
18241      * @cfg {Array} disabledDays
18242      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18243      */
18244     disabledDays : [0,1,2,3,4,5,6],
18245     /**
18246      * @cfg {String} disabledDaysText
18247      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18248      */
18249     disabledDaysText : "Disabled",
18250     /**
18251      * @cfg {Array} disabledDates
18252      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18253      * expression so they are very powerful. Some examples:
18254      * <ul>
18255      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18256      * <li>["03/08", "09/16"] would disable those days for every year</li>
18257      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18258      * <li>["03/../2006"] would disable every day in March 2006</li>
18259      * <li>["^03"] would disable every day in every March</li>
18260      * </ul>
18261      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18262      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18263      */
18264     disabledDates : null,
18265     /**
18266      * @cfg {String} disabledDatesText
18267      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18268      */
18269     disabledDatesText : "Disabled",
18270     /**
18271      * @cfg {Date/String} minValue
18272      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18273      * valid format (defaults to null).
18274      */
18275     minValue : null,
18276     /**
18277      * @cfg {Date/String} maxValue
18278      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18279      * valid format (defaults to null).
18280      */
18281     maxValue : null,
18282     /**
18283      * @cfg {String} minText
18284      * The error text to display when the date in the cell is before minValue (defaults to
18285      * 'The date in this field must be after {minValue}').
18286      */
18287     minText : "The date in this field must be equal to or after {0}",
18288     /**
18289      * @cfg {String} maxTextf
18290      * The error text to display when the date in the cell is after maxValue (defaults to
18291      * 'The date in this field must be before {maxValue}').
18292      */
18293     maxText : "The date in this field must be equal to or before {0}",
18294     /**
18295      * @cfg {String} invalidText
18296      * The error text to display when the date in the field is invalid (defaults to
18297      * '{value} is not a valid date - it must be in the format {format}').
18298      */
18299     invalidText : "{0} is not a valid date - it must be in the format {1}",
18300     /**
18301      * @cfg {String} triggerClass
18302      * An additional CSS class used to style the trigger button.  The trigger will always get the
18303      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18304      * which displays a calendar icon).
18305      */
18306     triggerClass : 'x-form-date-trigger',
18307     
18308
18309     /**
18310      * @cfg {Boolean} useIso
18311      * if enabled, then the date field will use a hidden field to store the 
18312      * real value as iso formated date. default (true)
18313      */ 
18314     useIso : true,
18315     /**
18316      * @cfg {String/Object} autoCreate
18317      * A DomHelper element spec, or true for a default element spec (defaults to
18318      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18319      */ 
18320     // private
18321     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18322     
18323     // private
18324     hiddenField: false,
18325     
18326     hideMonthPicker : false,
18327     
18328     onRender : function(ct, position)
18329     {
18330         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18331         if (this.useIso) {
18332             this.el.dom.removeAttribute('name'); 
18333             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18334                     'before', true);
18335             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18336             // prevent input submission
18337             this.hiddenName = this.name;
18338         }
18339             
18340             
18341     },
18342     
18343     // private
18344     validateValue : function(value)
18345     {
18346         value = this.formatDate(value);
18347         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18348             return false;
18349         }
18350         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18351              return true;
18352         }
18353         var svalue = value;
18354         value = this.parseDate(value);
18355         if(!value){
18356             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18357             return false;
18358         }
18359         var time = value.getTime();
18360         if(this.minValue && time < this.minValue.getTime()){
18361             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18362             return false;
18363         }
18364         if(this.maxValue && time > this.maxValue.getTime()){
18365             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18366             return false;
18367         }
18368         /*if(this.disabledDays){
18369             var day = value.getDay();
18370             for(var i = 0; i < this.disabledDays.length; i++) {
18371                 if(day === this.disabledDays[i]){
18372                     this.markInvalid(this.disabledDaysText);
18373                     return false;
18374                 }
18375             }
18376         }
18377         */
18378         var fvalue = this.formatDate(value);
18379         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18380             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18381             return false;
18382         }
18383         */
18384         return true;
18385     },
18386
18387     // private
18388     // Provides logic to override the default TriggerField.validateBlur which just returns true
18389     validateBlur : function(){
18390         return !this.menu || !this.menu.isVisible();
18391     },
18392
18393     /**
18394      * Returns the current date value of the date field.
18395      * @return {Date} The date value
18396      */
18397     getValue : function(){
18398         
18399         
18400         
18401         return  this.hiddenField ?
18402                 this.hiddenField.value :
18403                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18404     },
18405
18406     /**
18407      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18408      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18409      * (the default format used is "m/d/y").
18410      * <br />Usage:
18411      * <pre><code>
18412 //All of these calls set the same date value (May 4, 2006)
18413
18414 //Pass a date object:
18415 var dt = new Date('5/4/06');
18416 monthField.setValue(dt);
18417
18418 //Pass a date string (default format):
18419 monthField.setValue('5/4/06');
18420
18421 //Pass a date string (custom format):
18422 monthField.format = 'Y-m-d';
18423 monthField.setValue('2006-5-4');
18424 </code></pre>
18425      * @param {String/Date} date The date or valid date string
18426      */
18427     setValue : function(date){
18428         Roo.log('month setValue' + date);
18429         // can only be first of month..
18430         
18431         var val = this.parseDate(date);
18432         
18433         if (this.hiddenField) {
18434             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18435         }
18436         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18437         this.value = this.parseDate(date);
18438     },
18439
18440     // private
18441     parseDate : function(value){
18442         if(!value || value instanceof Date){
18443             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18444             return value;
18445         }
18446         var v = Date.parseDate(value, this.format);
18447         if (!v && this.useIso) {
18448             v = Date.parseDate(value, 'Y-m-d');
18449         }
18450         if (v) {
18451             // 
18452             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18453         }
18454         
18455         
18456         if(!v && this.altFormats){
18457             if(!this.altFormatsArray){
18458                 this.altFormatsArray = this.altFormats.split("|");
18459             }
18460             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18461                 v = Date.parseDate(value, this.altFormatsArray[i]);
18462             }
18463         }
18464         return v;
18465     },
18466
18467     // private
18468     formatDate : function(date, fmt){
18469         return (!date || !(date instanceof Date)) ?
18470                date : date.dateFormat(fmt || this.format);
18471     },
18472
18473     // private
18474     menuListeners : {
18475         select: function(m, d){
18476             this.setValue(d);
18477             this.fireEvent('select', this, d);
18478         },
18479         show : function(){ // retain focus styling
18480             this.onFocus();
18481         },
18482         hide : function(){
18483             this.focus.defer(10, this);
18484             var ml = this.menuListeners;
18485             this.menu.un("select", ml.select,  this);
18486             this.menu.un("show", ml.show,  this);
18487             this.menu.un("hide", ml.hide,  this);
18488         }
18489     },
18490     // private
18491     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18492     onTriggerClick : function(){
18493         if(this.disabled){
18494             return;
18495         }
18496         if(this.menu == null){
18497             this.menu = new Roo.menu.DateMenu();
18498            
18499         }
18500         
18501         Roo.apply(this.menu.picker,  {
18502             
18503             showClear: this.allowBlank,
18504             minDate : this.minValue,
18505             maxDate : this.maxValue,
18506             disabledDatesRE : this.ddMatch,
18507             disabledDatesText : this.disabledDatesText,
18508             
18509             format : this.useIso ? 'Y-m-d' : this.format,
18510             minText : String.format(this.minText, this.formatDate(this.minValue)),
18511             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18512             
18513         });
18514          this.menu.on(Roo.apply({}, this.menuListeners, {
18515             scope:this
18516         }));
18517        
18518         
18519         var m = this.menu;
18520         var p = m.picker;
18521         
18522         // hide month picker get's called when we called by 'before hide';
18523         
18524         var ignorehide = true;
18525         p.hideMonthPicker  = function(disableAnim){
18526             if (ignorehide) {
18527                 return;
18528             }
18529              if(this.monthPicker){
18530                 Roo.log("hideMonthPicker called");
18531                 if(disableAnim === true){
18532                     this.monthPicker.hide();
18533                 }else{
18534                     this.monthPicker.slideOut('t', {duration:.2});
18535                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18536                     p.fireEvent("select", this, this.value);
18537                     m.hide();
18538                 }
18539             }
18540         }
18541         
18542         Roo.log('picker set value');
18543         Roo.log(this.getValue());
18544         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18545         m.show(this.el, 'tl-bl?');
18546         ignorehide  = false;
18547         // this will trigger hideMonthPicker..
18548         
18549         
18550         // hidden the day picker
18551         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18552         
18553         
18554         
18555       
18556         
18557         p.showMonthPicker.defer(100, p);
18558     
18559         
18560        
18561     },
18562
18563     beforeBlur : function(){
18564         var v = this.parseDate(this.getRawValue());
18565         if(v){
18566             this.setValue(v);
18567         }
18568     }
18569
18570     /** @cfg {Boolean} grow @hide */
18571     /** @cfg {Number} growMin @hide */
18572     /** @cfg {Number} growMax @hide */
18573     /**
18574      * @hide
18575      * @method autoSize
18576      */
18577 });/*
18578  * Based on:
18579  * Ext JS Library 1.1.1
18580  * Copyright(c) 2006-2007, Ext JS, LLC.
18581  *
18582  * Originally Released Under LGPL - original licence link has changed is not relivant.
18583  *
18584  * Fork - LGPL
18585  * <script type="text/javascript">
18586  */
18587  
18588
18589 /**
18590  * @class Roo.form.ComboBox
18591  * @extends Roo.form.TriggerField
18592  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18593  * @constructor
18594  * Create a new ComboBox.
18595  * @param {Object} config Configuration options
18596  */
18597 Roo.form.ComboBox = function(config){
18598     Roo.form.ComboBox.superclass.constructor.call(this, config);
18599     this.addEvents({
18600         /**
18601          * @event expand
18602          * Fires when the dropdown list is expanded
18603              * @param {Roo.form.ComboBox} combo This combo box
18604              */
18605         'expand' : true,
18606         /**
18607          * @event collapse
18608          * Fires when the dropdown list is collapsed
18609              * @param {Roo.form.ComboBox} combo This combo box
18610              */
18611         'collapse' : true,
18612         /**
18613          * @event beforeselect
18614          * Fires before a list item is selected. Return false to cancel the selection.
18615              * @param {Roo.form.ComboBox} combo This combo box
18616              * @param {Roo.data.Record} record The data record returned from the underlying store
18617              * @param {Number} index The index of the selected item in the dropdown list
18618              */
18619         'beforeselect' : true,
18620         /**
18621          * @event select
18622          * Fires when a list item is selected
18623              * @param {Roo.form.ComboBox} combo This combo box
18624              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18625              * @param {Number} index The index of the selected item in the dropdown list
18626              */
18627         'select' : true,
18628         /**
18629          * @event beforequery
18630          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18631          * The event object passed has these properties:
18632              * @param {Roo.form.ComboBox} combo This combo box
18633              * @param {String} query The query
18634              * @param {Boolean} forceAll true to force "all" query
18635              * @param {Boolean} cancel true to cancel the query
18636              * @param {Object} e The query event object
18637              */
18638         'beforequery': true,
18639          /**
18640          * @event add
18641          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18642              * @param {Roo.form.ComboBox} combo This combo box
18643              */
18644         'add' : true,
18645         /**
18646          * @event edit
18647          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18648              * @param {Roo.form.ComboBox} combo This combo box
18649              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18650              */
18651         'edit' : true
18652         
18653         
18654     });
18655     if(this.transform){
18656         this.allowDomMove = false;
18657         var s = Roo.getDom(this.transform);
18658         if(!this.hiddenName){
18659             this.hiddenName = s.name;
18660         }
18661         if(!this.store){
18662             this.mode = 'local';
18663             var d = [], opts = s.options;
18664             for(var i = 0, len = opts.length;i < len; i++){
18665                 var o = opts[i];
18666                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18667                 if(o.selected) {
18668                     this.value = value;
18669                 }
18670                 d.push([value, o.text]);
18671             }
18672             this.store = new Roo.data.SimpleStore({
18673                 'id': 0,
18674                 fields: ['value', 'text'],
18675                 data : d
18676             });
18677             this.valueField = 'value';
18678             this.displayField = 'text';
18679         }
18680         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18681         if(!this.lazyRender){
18682             this.target = true;
18683             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18684             s.parentNode.removeChild(s); // remove it
18685             this.render(this.el.parentNode);
18686         }else{
18687             s.parentNode.removeChild(s); // remove it
18688         }
18689
18690     }
18691     if (this.store) {
18692         this.store = Roo.factory(this.store, Roo.data);
18693     }
18694     
18695     this.selectedIndex = -1;
18696     if(this.mode == 'local'){
18697         if(config.queryDelay === undefined){
18698             this.queryDelay = 10;
18699         }
18700         if(config.minChars === undefined){
18701             this.minChars = 0;
18702         }
18703     }
18704 };
18705
18706 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18707     /**
18708      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18709      */
18710     /**
18711      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18712      * rendering into an Roo.Editor, defaults to false)
18713      */
18714     /**
18715      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18716      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18717      */
18718     /**
18719      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18720      */
18721     /**
18722      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18723      * the dropdown list (defaults to undefined, with no header element)
18724      */
18725
18726      /**
18727      * @cfg {String/Roo.Template} tpl The template to use to render the output
18728      */
18729      
18730     // private
18731     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18732     /**
18733      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18734      */
18735     listWidth: undefined,
18736     /**
18737      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18738      * mode = 'remote' or 'text' if mode = 'local')
18739      */
18740     displayField: undefined,
18741     /**
18742      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18743      * mode = 'remote' or 'value' if mode = 'local'). 
18744      * Note: use of a valueField requires the user make a selection
18745      * in order for a value to be mapped.
18746      */
18747     valueField: undefined,
18748     
18749     
18750     /**
18751      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18752      * field's data value (defaults to the underlying DOM element's name)
18753      */
18754     hiddenName: undefined,
18755     /**
18756      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18757      */
18758     listClass: '',
18759     /**
18760      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18761      */
18762     selectedClass: 'x-combo-selected',
18763     /**
18764      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18765      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18766      * which displays a downward arrow icon).
18767      */
18768     triggerClass : 'x-form-arrow-trigger',
18769     /**
18770      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18771      */
18772     shadow:'sides',
18773     /**
18774      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18775      * anchor positions (defaults to 'tl-bl')
18776      */
18777     listAlign: 'tl-bl?',
18778     /**
18779      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18780      */
18781     maxHeight: 300,
18782     /**
18783      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18784      * query specified by the allQuery config option (defaults to 'query')
18785      */
18786     triggerAction: 'query',
18787     /**
18788      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18789      * (defaults to 4, does not apply if editable = false)
18790      */
18791     minChars : 4,
18792     /**
18793      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18794      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18795      */
18796     typeAhead: false,
18797     /**
18798      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18799      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18800      */
18801     queryDelay: 500,
18802     /**
18803      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18804      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18805      */
18806     pageSize: 0,
18807     /**
18808      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18809      * when editable = true (defaults to false)
18810      */
18811     selectOnFocus:false,
18812     /**
18813      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18814      */
18815     queryParam: 'query',
18816     /**
18817      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18818      * when mode = 'remote' (defaults to 'Loading...')
18819      */
18820     loadingText: 'Loading...',
18821     /**
18822      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18823      */
18824     resizable: false,
18825     /**
18826      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18827      */
18828     handleHeight : 8,
18829     /**
18830      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18831      * traditional select (defaults to true)
18832      */
18833     editable: true,
18834     /**
18835      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18836      */
18837     allQuery: '',
18838     /**
18839      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18840      */
18841     mode: 'remote',
18842     /**
18843      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18844      * listWidth has a higher value)
18845      */
18846     minListWidth : 70,
18847     /**
18848      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18849      * allow the user to set arbitrary text into the field (defaults to false)
18850      */
18851     forceSelection:false,
18852     /**
18853      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18854      * if typeAhead = true (defaults to 250)
18855      */
18856     typeAheadDelay : 250,
18857     /**
18858      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18859      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18860      */
18861     valueNotFoundText : undefined,
18862     /**
18863      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18864      */
18865     blockFocus : false,
18866     
18867     /**
18868      * @cfg {Boolean} disableClear Disable showing of clear button.
18869      */
18870     disableClear : false,
18871     /**
18872      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18873      */
18874     alwaysQuery : false,
18875     
18876     //private
18877     addicon : false,
18878     editicon: false,
18879     
18880     // element that contains real text value.. (when hidden is used..)
18881      
18882     // private
18883     onRender : function(ct, position){
18884         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18885         if(this.hiddenName){
18886             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18887                     'before', true);
18888             this.hiddenField.value =
18889                 this.hiddenValue !== undefined ? this.hiddenValue :
18890                 this.value !== undefined ? this.value : '';
18891
18892             // prevent input submission
18893             this.el.dom.removeAttribute('name');
18894              
18895              
18896         }
18897         if(Roo.isGecko){
18898             this.el.dom.setAttribute('autocomplete', 'off');
18899         }
18900
18901         var cls = 'x-combo-list';
18902
18903         this.list = new Roo.Layer({
18904             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18905         });
18906
18907         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18908         this.list.setWidth(lw);
18909         this.list.swallowEvent('mousewheel');
18910         this.assetHeight = 0;
18911
18912         if(this.title){
18913             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18914             this.assetHeight += this.header.getHeight();
18915         }
18916
18917         this.innerList = this.list.createChild({cls:cls+'-inner'});
18918         this.innerList.on('mouseover', this.onViewOver, this);
18919         this.innerList.on('mousemove', this.onViewMove, this);
18920         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18921         
18922         if(this.allowBlank && !this.pageSize && !this.disableClear){
18923             this.footer = this.list.createChild({cls:cls+'-ft'});
18924             this.pageTb = new Roo.Toolbar(this.footer);
18925            
18926         }
18927         if(this.pageSize){
18928             this.footer = this.list.createChild({cls:cls+'-ft'});
18929             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18930                     {pageSize: this.pageSize});
18931             
18932         }
18933         
18934         if (this.pageTb && this.allowBlank && !this.disableClear) {
18935             var _this = this;
18936             this.pageTb.add(new Roo.Toolbar.Fill(), {
18937                 cls: 'x-btn-icon x-btn-clear',
18938                 text: '&#160;',
18939                 handler: function()
18940                 {
18941                     _this.collapse();
18942                     _this.clearValue();
18943                     _this.onSelect(false, -1);
18944                 }
18945             });
18946         }
18947         if (this.footer) {
18948             this.assetHeight += this.footer.getHeight();
18949         }
18950         
18951
18952         if(!this.tpl){
18953             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18954         }
18955
18956         this.view = new Roo.View(this.innerList, this.tpl, {
18957             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18958         });
18959
18960         this.view.on('click', this.onViewClick, this);
18961
18962         this.store.on('beforeload', this.onBeforeLoad, this);
18963         this.store.on('load', this.onLoad, this);
18964         this.store.on('loadexception', this.onLoadException, this);
18965
18966         if(this.resizable){
18967             this.resizer = new Roo.Resizable(this.list,  {
18968                pinned:true, handles:'se'
18969             });
18970             this.resizer.on('resize', function(r, w, h){
18971                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18972                 this.listWidth = w;
18973                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18974                 this.restrictHeight();
18975             }, this);
18976             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18977         }
18978         if(!this.editable){
18979             this.editable = true;
18980             this.setEditable(false);
18981         }  
18982         
18983         
18984         if (typeof(this.events.add.listeners) != 'undefined') {
18985             
18986             this.addicon = this.wrap.createChild(
18987                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18988        
18989             this.addicon.on('click', function(e) {
18990                 this.fireEvent('add', this);
18991             }, this);
18992         }
18993         if (typeof(this.events.edit.listeners) != 'undefined') {
18994             
18995             this.editicon = this.wrap.createChild(
18996                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18997             if (this.addicon) {
18998                 this.editicon.setStyle('margin-left', '40px');
18999             }
19000             this.editicon.on('click', function(e) {
19001                 
19002                 // we fire even  if inothing is selected..
19003                 this.fireEvent('edit', this, this.lastData );
19004                 
19005             }, this);
19006         }
19007         
19008         
19009         
19010     },
19011
19012     // private
19013     initEvents : function(){
19014         Roo.form.ComboBox.superclass.initEvents.call(this);
19015
19016         this.keyNav = new Roo.KeyNav(this.el, {
19017             "up" : function(e){
19018                 this.inKeyMode = true;
19019                 this.selectPrev();
19020             },
19021
19022             "down" : function(e){
19023                 if(!this.isExpanded()){
19024                     this.onTriggerClick();
19025                 }else{
19026                     this.inKeyMode = true;
19027                     this.selectNext();
19028                 }
19029             },
19030
19031             "enter" : function(e){
19032                 this.onViewClick();
19033                 //return true;
19034             },
19035
19036             "esc" : function(e){
19037                 this.collapse();
19038             },
19039
19040             "tab" : function(e){
19041                 this.onViewClick(false);
19042                 this.fireEvent("specialkey", this, e);
19043                 return true;
19044             },
19045
19046             scope : this,
19047
19048             doRelay : function(foo, bar, hname){
19049                 if(hname == 'down' || this.scope.isExpanded()){
19050                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19051                 }
19052                 return true;
19053             },
19054
19055             forceKeyDown: true
19056         });
19057         this.queryDelay = Math.max(this.queryDelay || 10,
19058                 this.mode == 'local' ? 10 : 250);
19059         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19060         if(this.typeAhead){
19061             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19062         }
19063         if(this.editable !== false){
19064             this.el.on("keyup", this.onKeyUp, this);
19065         }
19066         if(this.forceSelection){
19067             this.on('blur', this.doForce, this);
19068         }
19069     },
19070
19071     onDestroy : function(){
19072         if(this.view){
19073             this.view.setStore(null);
19074             this.view.el.removeAllListeners();
19075             this.view.el.remove();
19076             this.view.purgeListeners();
19077         }
19078         if(this.list){
19079             this.list.destroy();
19080         }
19081         if(this.store){
19082             this.store.un('beforeload', this.onBeforeLoad, this);
19083             this.store.un('load', this.onLoad, this);
19084             this.store.un('loadexception', this.onLoadException, this);
19085         }
19086         Roo.form.ComboBox.superclass.onDestroy.call(this);
19087     },
19088
19089     // private
19090     fireKey : function(e){
19091         if(e.isNavKeyPress() && !this.list.isVisible()){
19092             this.fireEvent("specialkey", this, e);
19093         }
19094     },
19095
19096     // private
19097     onResize: function(w, h){
19098         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19099         
19100         if(typeof w != 'number'){
19101             // we do not handle it!?!?
19102             return;
19103         }
19104         var tw = this.trigger.getWidth();
19105         tw += this.addicon ? this.addicon.getWidth() : 0;
19106         tw += this.editicon ? this.editicon.getWidth() : 0;
19107         var x = w - tw;
19108         this.el.setWidth( this.adjustWidth('input', x));
19109             
19110         this.trigger.setStyle('left', x+'px');
19111         
19112         if(this.list && this.listWidth === undefined){
19113             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19114             this.list.setWidth(lw);
19115             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19116         }
19117         
19118     
19119         
19120     },
19121
19122     /**
19123      * Allow or prevent the user from directly editing the field text.  If false is passed,
19124      * the user will only be able to select from the items defined in the dropdown list.  This method
19125      * is the runtime equivalent of setting the 'editable' config option at config time.
19126      * @param {Boolean} value True to allow the user to directly edit the field text
19127      */
19128     setEditable : function(value){
19129         if(value == this.editable){
19130             return;
19131         }
19132         this.editable = value;
19133         if(!value){
19134             this.el.dom.setAttribute('readOnly', true);
19135             this.el.on('mousedown', this.onTriggerClick,  this);
19136             this.el.addClass('x-combo-noedit');
19137         }else{
19138             this.el.dom.setAttribute('readOnly', false);
19139             this.el.un('mousedown', this.onTriggerClick,  this);
19140             this.el.removeClass('x-combo-noedit');
19141         }
19142     },
19143
19144     // private
19145     onBeforeLoad : function(){
19146         if(!this.hasFocus){
19147             return;
19148         }
19149         this.innerList.update(this.loadingText ?
19150                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19151         this.restrictHeight();
19152         this.selectedIndex = -1;
19153     },
19154
19155     // private
19156     onLoad : function(){
19157         if(!this.hasFocus){
19158             return;
19159         }
19160         if(this.store.getCount() > 0){
19161             this.expand();
19162             this.restrictHeight();
19163             if(this.lastQuery == this.allQuery){
19164                 if(this.editable){
19165                     this.el.dom.select();
19166                 }
19167                 if(!this.selectByValue(this.value, true)){
19168                     this.select(0, true);
19169                 }
19170             }else{
19171                 this.selectNext();
19172                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19173                     this.taTask.delay(this.typeAheadDelay);
19174                 }
19175             }
19176         }else{
19177             this.onEmptyResults();
19178         }
19179         //this.el.focus();
19180     },
19181     // private
19182     onLoadException : function()
19183     {
19184         this.collapse();
19185         Roo.log(this.store.reader.jsonData);
19186         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19187             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19188         }
19189         
19190         
19191     },
19192     // private
19193     onTypeAhead : function(){
19194         if(this.store.getCount() > 0){
19195             var r = this.store.getAt(0);
19196             var newValue = r.data[this.displayField];
19197             var len = newValue.length;
19198             var selStart = this.getRawValue().length;
19199             if(selStart != len){
19200                 this.setRawValue(newValue);
19201                 this.selectText(selStart, newValue.length);
19202             }
19203         }
19204     },
19205
19206     // private
19207     onSelect : function(record, index){
19208         if(this.fireEvent('beforeselect', this, record, index) !== false){
19209             this.setFromData(index > -1 ? record.data : false);
19210             this.collapse();
19211             this.fireEvent('select', this, record, index);
19212         }
19213     },
19214
19215     /**
19216      * Returns the currently selected field value or empty string if no value is set.
19217      * @return {String} value The selected value
19218      */
19219     getValue : function(){
19220         if(this.valueField){
19221             return typeof this.value != 'undefined' ? this.value : '';
19222         }
19223         return Roo.form.ComboBox.superclass.getValue.call(this);
19224     },
19225
19226     /**
19227      * Clears any text/value currently set in the field
19228      */
19229     clearValue : function(){
19230         if(this.hiddenField){
19231             this.hiddenField.value = '';
19232         }
19233         this.value = '';
19234         this.setRawValue('');
19235         this.lastSelectionText = '';
19236         
19237     },
19238
19239     /**
19240      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19241      * will be displayed in the field.  If the value does not match the data value of an existing item,
19242      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19243      * Otherwise the field will be blank (although the value will still be set).
19244      * @param {String} value The value to match
19245      */
19246     setValue : function(v){
19247         var text = v;
19248         if(this.valueField){
19249             var r = this.findRecord(this.valueField, v);
19250             if(r){
19251                 text = r.data[this.displayField];
19252             }else if(this.valueNotFoundText !== undefined){
19253                 text = this.valueNotFoundText;
19254             }
19255         }
19256         this.lastSelectionText = text;
19257         if(this.hiddenField){
19258             this.hiddenField.value = v;
19259         }
19260         Roo.form.ComboBox.superclass.setValue.call(this, text);
19261         this.value = v;
19262     },
19263     /**
19264      * @property {Object} the last set data for the element
19265      */
19266     
19267     lastData : false,
19268     /**
19269      * Sets the value of the field based on a object which is related to the record format for the store.
19270      * @param {Object} value the value to set as. or false on reset?
19271      */
19272     setFromData : function(o){
19273         var dv = ''; // display value
19274         var vv = ''; // value value..
19275         this.lastData = o;
19276         if (this.displayField) {
19277             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19278         } else {
19279             // this is an error condition!!!
19280             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19281         }
19282         
19283         if(this.valueField){
19284             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19285         }
19286         if(this.hiddenField){
19287             this.hiddenField.value = vv;
19288             
19289             this.lastSelectionText = dv;
19290             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19291             this.value = vv;
19292             return;
19293         }
19294         // no hidden field.. - we store the value in 'value', but still display
19295         // display field!!!!
19296         this.lastSelectionText = dv;
19297         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19298         this.value = vv;
19299         
19300         
19301     },
19302     // private
19303     reset : function(){
19304         // overridden so that last data is reset..
19305         this.setValue(this.resetValue);
19306         this.originalValue = this.getValue();
19307         this.clearInvalid();
19308         this.lastData = false;
19309         if (this.view) {
19310             this.view.clearSelections();
19311         }
19312     },
19313     // private
19314     findRecord : function(prop, value){
19315         var record;
19316         if(this.store.getCount() > 0){
19317             this.store.each(function(r){
19318                 if(r.data[prop] == value){
19319                     record = r;
19320                     return false;
19321                 }
19322                 return true;
19323             });
19324         }
19325         return record;
19326     },
19327     
19328     getName: function()
19329     {
19330         // returns hidden if it's set..
19331         if (!this.rendered) {return ''};
19332         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19333         
19334     },
19335     // private
19336     onViewMove : function(e, t){
19337         this.inKeyMode = false;
19338     },
19339
19340     // private
19341     onViewOver : function(e, t){
19342         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19343             return;
19344         }
19345         var item = this.view.findItemFromChild(t);
19346         if(item){
19347             var index = this.view.indexOf(item);
19348             this.select(index, false);
19349         }
19350     },
19351
19352     // private
19353     onViewClick : function(doFocus)
19354     {
19355         var index = this.view.getSelectedIndexes()[0];
19356         var r = this.store.getAt(index);
19357         if(r){
19358             this.onSelect(r, index);
19359         }
19360         if(doFocus !== false && !this.blockFocus){
19361             this.el.focus();
19362         }
19363     },
19364
19365     // private
19366     restrictHeight : function(){
19367         this.innerList.dom.style.height = '';
19368         var inner = this.innerList.dom;
19369         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19370         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19371         this.list.beginUpdate();
19372         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19373         this.list.alignTo(this.el, this.listAlign);
19374         this.list.endUpdate();
19375     },
19376
19377     // private
19378     onEmptyResults : function(){
19379         this.collapse();
19380     },
19381
19382     /**
19383      * Returns true if the dropdown list is expanded, else false.
19384      */
19385     isExpanded : function(){
19386         return this.list.isVisible();
19387     },
19388
19389     /**
19390      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19391      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19392      * @param {String} value The data value of the item to select
19393      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19394      * selected item if it is not currently in view (defaults to true)
19395      * @return {Boolean} True if the value matched an item in the list, else false
19396      */
19397     selectByValue : function(v, scrollIntoView){
19398         if(v !== undefined && v !== null){
19399             var r = this.findRecord(this.valueField || this.displayField, v);
19400             if(r){
19401                 this.select(this.store.indexOf(r), scrollIntoView);
19402                 return true;
19403             }
19404         }
19405         return false;
19406     },
19407
19408     /**
19409      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19410      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19411      * @param {Number} index The zero-based index of the list item to select
19412      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19413      * selected item if it is not currently in view (defaults to true)
19414      */
19415     select : function(index, scrollIntoView){
19416         this.selectedIndex = index;
19417         this.view.select(index);
19418         if(scrollIntoView !== false){
19419             var el = this.view.getNode(index);
19420             if(el){
19421                 this.innerList.scrollChildIntoView(el, false);
19422             }
19423         }
19424     },
19425
19426     // private
19427     selectNext : function(){
19428         var ct = this.store.getCount();
19429         if(ct > 0){
19430             if(this.selectedIndex == -1){
19431                 this.select(0);
19432             }else if(this.selectedIndex < ct-1){
19433                 this.select(this.selectedIndex+1);
19434             }
19435         }
19436     },
19437
19438     // private
19439     selectPrev : function(){
19440         var ct = this.store.getCount();
19441         if(ct > 0){
19442             if(this.selectedIndex == -1){
19443                 this.select(0);
19444             }else if(this.selectedIndex != 0){
19445                 this.select(this.selectedIndex-1);
19446             }
19447         }
19448     },
19449
19450     // private
19451     onKeyUp : function(e){
19452         if(this.editable !== false && !e.isSpecialKey()){
19453             this.lastKey = e.getKey();
19454             this.dqTask.delay(this.queryDelay);
19455         }
19456     },
19457
19458     // private
19459     validateBlur : function(){
19460         return !this.list || !this.list.isVisible();   
19461     },
19462
19463     // private
19464     initQuery : function(){
19465         this.doQuery(this.getRawValue());
19466     },
19467
19468     // private
19469     doForce : function(){
19470         if(this.el.dom.value.length > 0){
19471             this.el.dom.value =
19472                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19473              
19474         }
19475     },
19476
19477     /**
19478      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19479      * query allowing the query action to be canceled if needed.
19480      * @param {String} query The SQL query to execute
19481      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19482      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19483      * saved in the current store (defaults to false)
19484      */
19485     doQuery : function(q, forceAll){
19486         if(q === undefined || q === null){
19487             q = '';
19488         }
19489         var qe = {
19490             query: q,
19491             forceAll: forceAll,
19492             combo: this,
19493             cancel:false
19494         };
19495         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19496             return false;
19497         }
19498         q = qe.query;
19499         forceAll = qe.forceAll;
19500         if(forceAll === true || (q.length >= this.minChars)){
19501             if(this.lastQuery != q || this.alwaysQuery){
19502                 this.lastQuery = q;
19503                 if(this.mode == 'local'){
19504                     this.selectedIndex = -1;
19505                     if(forceAll){
19506                         this.store.clearFilter();
19507                     }else{
19508                         this.store.filter(this.displayField, q);
19509                     }
19510                     this.onLoad();
19511                 }else{
19512                     this.store.baseParams[this.queryParam] = q;
19513                     this.store.load({
19514                         params: this.getParams(q)
19515                     });
19516                     this.expand();
19517                 }
19518             }else{
19519                 this.selectedIndex = -1;
19520                 this.onLoad();   
19521             }
19522         }
19523     },
19524
19525     // private
19526     getParams : function(q){
19527         var p = {};
19528         //p[this.queryParam] = q;
19529         if(this.pageSize){
19530             p.start = 0;
19531             p.limit = this.pageSize;
19532         }
19533         return p;
19534     },
19535
19536     /**
19537      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19538      */
19539     collapse : function(){
19540         if(!this.isExpanded()){
19541             return;
19542         }
19543         this.list.hide();
19544         Roo.get(document).un('mousedown', this.collapseIf, this);
19545         Roo.get(document).un('mousewheel', this.collapseIf, this);
19546         if (!this.editable) {
19547             Roo.get(document).un('keydown', this.listKeyPress, this);
19548         }
19549         this.fireEvent('collapse', this);
19550     },
19551
19552     // private
19553     collapseIf : function(e){
19554         if(!e.within(this.wrap) && !e.within(this.list)){
19555             this.collapse();
19556         }
19557     },
19558
19559     /**
19560      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19561      */
19562     expand : function(){
19563         if(this.isExpanded() || !this.hasFocus){
19564             return;
19565         }
19566         this.list.alignTo(this.el, this.listAlign);
19567         this.list.show();
19568         Roo.get(document).on('mousedown', this.collapseIf, this);
19569         Roo.get(document).on('mousewheel', this.collapseIf, this);
19570         if (!this.editable) {
19571             Roo.get(document).on('keydown', this.listKeyPress, this);
19572         }
19573         
19574         this.fireEvent('expand', this);
19575     },
19576
19577     // private
19578     // Implements the default empty TriggerField.onTriggerClick function
19579     onTriggerClick : function(){
19580         if(this.disabled){
19581             return;
19582         }
19583         if(this.isExpanded()){
19584             this.collapse();
19585             if (!this.blockFocus) {
19586                 this.el.focus();
19587             }
19588             
19589         }else {
19590             this.hasFocus = true;
19591             if(this.triggerAction == 'all') {
19592                 this.doQuery(this.allQuery, true);
19593             } else {
19594                 this.doQuery(this.getRawValue());
19595             }
19596             if (!this.blockFocus) {
19597                 this.el.focus();
19598             }
19599         }
19600     },
19601     listKeyPress : function(e)
19602     {
19603         //Roo.log('listkeypress');
19604         // scroll to first matching element based on key pres..
19605         if (e.isSpecialKey()) {
19606             return false;
19607         }
19608         var k = String.fromCharCode(e.getKey()).toUpperCase();
19609         //Roo.log(k);
19610         var match  = false;
19611         var csel = this.view.getSelectedNodes();
19612         var cselitem = false;
19613         if (csel.length) {
19614             var ix = this.view.indexOf(csel[0]);
19615             cselitem  = this.store.getAt(ix);
19616             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19617                 cselitem = false;
19618             }
19619             
19620         }
19621         
19622         this.store.each(function(v) { 
19623             if (cselitem) {
19624                 // start at existing selection.
19625                 if (cselitem.id == v.id) {
19626                     cselitem = false;
19627                 }
19628                 return;
19629             }
19630                 
19631             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19632                 match = this.store.indexOf(v);
19633                 return false;
19634             }
19635         }, this);
19636         
19637         if (match === false) {
19638             return true; // no more action?
19639         }
19640         // scroll to?
19641         this.view.select(match);
19642         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19643         sn.scrollIntoView(sn.dom.parentNode, false);
19644     }
19645
19646     /** 
19647     * @cfg {Boolean} grow 
19648     * @hide 
19649     */
19650     /** 
19651     * @cfg {Number} growMin 
19652     * @hide 
19653     */
19654     /** 
19655     * @cfg {Number} growMax 
19656     * @hide 
19657     */
19658     /**
19659      * @hide
19660      * @method autoSize
19661      */
19662 });/*
19663  * Copyright(c) 2010-2012, Roo J Solutions Limited
19664  *
19665  * Licence LGPL
19666  *
19667  */
19668
19669 /**
19670  * @class Roo.form.ComboBoxArray
19671  * @extends Roo.form.TextField
19672  * A facebook style adder... for lists of email / people / countries  etc...
19673  * pick multiple items from a combo box, and shows each one.
19674  *
19675  *  Fred [x]  Brian [x]  [Pick another |v]
19676  *
19677  *
19678  *  For this to work: it needs various extra information
19679  *    - normal combo problay has
19680  *      name, hiddenName
19681  *    + displayField, valueField
19682  *
19683  *    For our purpose...
19684  *
19685  *
19686  *   If we change from 'extends' to wrapping...
19687  *   
19688  *  
19689  *
19690  
19691  
19692  * @constructor
19693  * Create a new ComboBoxArray.
19694  * @param {Object} config Configuration options
19695  */
19696  
19697
19698 Roo.form.ComboBoxArray = function(config)
19699 {
19700     this.addEvents({
19701         /**
19702          * @event beforeremove
19703          * Fires before remove the value from the list
19704              * @param {Roo.form.ComboBoxArray} _self This combo box array
19705              * @param {Roo.form.ComboBoxArray.Item} item removed item
19706              */
19707         'beforeremove' : true,
19708         /**
19709          * @event remove
19710          * Fires when remove the value from the list
19711              * @param {Roo.form.ComboBoxArray} _self This combo box array
19712              * @param {Roo.form.ComboBoxArray.Item} item removed item
19713              */
19714         'remove' : true
19715         
19716         
19717     });
19718     
19719     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19720     
19721     this.items = new Roo.util.MixedCollection(false);
19722     
19723     // construct the child combo...
19724     
19725     
19726     
19727     
19728    
19729     
19730 }
19731
19732  
19733 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19734
19735     /**
19736      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19737      */
19738     
19739     lastData : false,
19740     
19741     // behavies liek a hiddne field
19742     inputType:      'hidden',
19743     /**
19744      * @cfg {Number} width The width of the box that displays the selected element
19745      */ 
19746     width:          300,
19747
19748     
19749     
19750     /**
19751      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19752      */
19753     name : false,
19754     /**
19755      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19756      */
19757     hiddenName : false,
19758     
19759     
19760     // private the array of items that are displayed..
19761     items  : false,
19762     // private - the hidden field el.
19763     hiddenEl : false,
19764     // private - the filed el..
19765     el : false,
19766     
19767     //validateValue : function() { return true; }, // all values are ok!
19768     //onAddClick: function() { },
19769     
19770     onRender : function(ct, position) 
19771     {
19772         
19773         // create the standard hidden element
19774         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19775         
19776         
19777         // give fake names to child combo;
19778         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19779         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19780         
19781         this.combo = Roo.factory(this.combo, Roo.form);
19782         this.combo.onRender(ct, position);
19783         if (typeof(this.combo.width) != 'undefined') {
19784             this.combo.onResize(this.combo.width,0);
19785         }
19786         
19787         this.combo.initEvents();
19788         
19789         // assigned so form know we need to do this..
19790         this.store          = this.combo.store;
19791         this.valueField     = this.combo.valueField;
19792         this.displayField   = this.combo.displayField ;
19793         
19794         
19795         this.combo.wrap.addClass('x-cbarray-grp');
19796         
19797         var cbwrap = this.combo.wrap.createChild(
19798             {tag: 'div', cls: 'x-cbarray-cb'},
19799             this.combo.el.dom
19800         );
19801         
19802              
19803         this.hiddenEl = this.combo.wrap.createChild({
19804             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19805         });
19806         this.el = this.combo.wrap.createChild({
19807             tag: 'input',  type:'hidden' , name: this.name, value : ''
19808         });
19809          //   this.el.dom.removeAttribute("name");
19810         
19811         
19812         this.outerWrap = this.combo.wrap;
19813         this.wrap = cbwrap;
19814         
19815         this.outerWrap.setWidth(this.width);
19816         this.outerWrap.dom.removeChild(this.el.dom);
19817         
19818         this.wrap.dom.appendChild(this.el.dom);
19819         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19820         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19821         
19822         this.combo.trigger.setStyle('position','relative');
19823         this.combo.trigger.setStyle('left', '0px');
19824         this.combo.trigger.setStyle('top', '2px');
19825         
19826         this.combo.el.setStyle('vertical-align', 'text-bottom');
19827         
19828         //this.trigger.setStyle('vertical-align', 'top');
19829         
19830         // this should use the code from combo really... on('add' ....)
19831         if (this.adder) {
19832             
19833         
19834             this.adder = this.outerWrap.createChild(
19835                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19836             var _t = this;
19837             this.adder.on('click', function(e) {
19838                 _t.fireEvent('adderclick', this, e);
19839             }, _t);
19840         }
19841         //var _t = this;
19842         //this.adder.on('click', this.onAddClick, _t);
19843         
19844         
19845         this.combo.on('select', function(cb, rec, ix) {
19846             this.addItem(rec.data);
19847             
19848             cb.setValue('');
19849             cb.el.dom.value = '';
19850             //cb.lastData = rec.data;
19851             // add to list
19852             
19853         }, this);
19854         
19855         
19856     },
19857     
19858     
19859     getName: function()
19860     {
19861         // returns hidden if it's set..
19862         if (!this.rendered) {return ''};
19863         return  this.hiddenName ? this.hiddenName : this.name;
19864         
19865     },
19866     
19867     
19868     onResize: function(w, h){
19869         
19870         return;
19871         // not sure if this is needed..
19872         //this.combo.onResize(w,h);
19873         
19874         if(typeof w != 'number'){
19875             // we do not handle it!?!?
19876             return;
19877         }
19878         var tw = this.combo.trigger.getWidth();
19879         tw += this.addicon ? this.addicon.getWidth() : 0;
19880         tw += this.editicon ? this.editicon.getWidth() : 0;
19881         var x = w - tw;
19882         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19883             
19884         this.combo.trigger.setStyle('left', '0px');
19885         
19886         if(this.list && this.listWidth === undefined){
19887             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19888             this.list.setWidth(lw);
19889             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19890         }
19891         
19892     
19893         
19894     },
19895     
19896     addItem: function(rec)
19897     {
19898         var valueField = this.combo.valueField;
19899         var displayField = this.combo.displayField;
19900         if (this.items.indexOfKey(rec[valueField]) > -1) {
19901             //console.log("GOT " + rec.data.id);
19902             return;
19903         }
19904         
19905         var x = new Roo.form.ComboBoxArray.Item({
19906             //id : rec[this.idField],
19907             data : rec,
19908             displayField : displayField ,
19909             tipField : displayField ,
19910             cb : this
19911         });
19912         // use the 
19913         this.items.add(rec[valueField],x);
19914         // add it before the element..
19915         this.updateHiddenEl();
19916         x.render(this.outerWrap, this.wrap.dom);
19917         // add the image handler..
19918     },
19919     
19920     updateHiddenEl : function()
19921     {
19922         this.validate();
19923         if (!this.hiddenEl) {
19924             return;
19925         }
19926         var ar = [];
19927         var idField = this.combo.valueField;
19928         
19929         this.items.each(function(f) {
19930             ar.push(f.data[idField]);
19931            
19932         });
19933         this.hiddenEl.dom.value = ar.join(',');
19934         this.validate();
19935     },
19936     
19937     reset : function()
19938     {
19939         this.items.clear();
19940         
19941         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19942            el.remove();
19943         });
19944         
19945         this.el.dom.value = '';
19946         if (this.hiddenEl) {
19947             this.hiddenEl.dom.value = '';
19948         }
19949         
19950     },
19951     getValue: function()
19952     {
19953         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19954     },
19955     setValue: function(v) // not a valid action - must use addItems..
19956     {
19957          
19958         this.reset();
19959         
19960         
19961         
19962         if (this.store.isLocal && (typeof(v) == 'string')) {
19963             // then we can use the store to find the values..
19964             // comma seperated at present.. this needs to allow JSON based encoding..
19965             this.hiddenEl.value  = v;
19966             var v_ar = [];
19967             Roo.each(v.split(','), function(k) {
19968                 Roo.log("CHECK " + this.valueField + ',' + k);
19969                 var li = this.store.query(this.valueField, k);
19970                 if (!li.length) {
19971                     return;
19972                 }
19973                 var add = {};
19974                 add[this.valueField] = k;
19975                 add[this.displayField] = li.item(0).data[this.displayField];
19976                 
19977                 this.addItem(add);
19978             }, this) 
19979              
19980         }
19981         if (typeof(v) == 'object' ) {
19982             // then let's assume it's an array of objects..
19983             Roo.each(v, function(l) {
19984                 this.addItem(l);
19985             }, this);
19986              
19987         }
19988         
19989         
19990     },
19991     setFromData: function(v)
19992     {
19993         // this recieves an object, if setValues is called.
19994         this.reset();
19995         this.el.dom.value = v[this.displayField];
19996         this.hiddenEl.dom.value = v[this.valueField];
19997         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19998             return;
19999         }
20000         var kv = v[this.valueField];
20001         var dv = v[this.displayField];
20002         kv = typeof(kv) != 'string' ? '' : kv;
20003         dv = typeof(dv) != 'string' ? '' : dv;
20004         
20005         
20006         var keys = kv.split(',');
20007         var display = dv.split(',');
20008         for (var i = 0 ; i < keys.length; i++) {
20009             
20010             add = {};
20011             add[this.valueField] = keys[i];
20012             add[this.displayField] = display[i];
20013             this.addItem(add);
20014         }
20015       
20016         
20017     },
20018     
20019     /**
20020      * Validates the combox array value
20021      * @return {Boolean} True if the value is valid, else false
20022      */
20023     validate : function(){
20024         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20025             this.clearInvalid();
20026             return true;
20027         }
20028         return false;
20029     },
20030     
20031     validateValue : function(value){
20032         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20033         
20034     },
20035     
20036     /*@
20037      * overide
20038      * 
20039      */
20040     isDirty : function() {
20041         if(this.disabled) {
20042             return false;
20043         }
20044         
20045         try {
20046             var d = Roo.decode(String(this.originalValue));
20047         } catch (e) {
20048             return String(this.getValue()) !== String(this.originalValue);
20049         }
20050         
20051         var originalValue = [];
20052         
20053         for (var i = 0; i < d.length; i++){
20054             originalValue.push(d[i][this.valueField]);
20055         }
20056         
20057         return String(this.getValue()) !== String(originalValue.join(','));
20058         
20059     }
20060     
20061 });
20062
20063
20064
20065 /**
20066  * @class Roo.form.ComboBoxArray.Item
20067  * @extends Roo.BoxComponent
20068  * A selected item in the list
20069  *  Fred [x]  Brian [x]  [Pick another |v]
20070  * 
20071  * @constructor
20072  * Create a new item.
20073  * @param {Object} config Configuration options
20074  */
20075  
20076 Roo.form.ComboBoxArray.Item = function(config) {
20077     config.id = Roo.id();
20078     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20079 }
20080
20081 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20082     data : {},
20083     cb: false,
20084     displayField : false,
20085     tipField : false,
20086     
20087     
20088     defaultAutoCreate : {
20089         tag: 'div',
20090         cls: 'x-cbarray-item',
20091         cn : [ 
20092             { tag: 'div' },
20093             {
20094                 tag: 'img',
20095                 width:16,
20096                 height : 16,
20097                 src : Roo.BLANK_IMAGE_URL ,
20098                 align: 'center'
20099             }
20100         ]
20101         
20102     },
20103     
20104  
20105     onRender : function(ct, position)
20106     {
20107         Roo.form.Field.superclass.onRender.call(this, ct, position);
20108         
20109         if(!this.el){
20110             var cfg = this.getAutoCreate();
20111             this.el = ct.createChild(cfg, position);
20112         }
20113         
20114         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20115         
20116         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20117             this.cb.renderer(this.data) :
20118             String.format('{0}',this.data[this.displayField]);
20119         
20120             
20121         this.el.child('div').dom.setAttribute('qtip',
20122                         String.format('{0}',this.data[this.tipField])
20123         );
20124         
20125         this.el.child('img').on('click', this.remove, this);
20126         
20127     },
20128    
20129     remove : function()
20130     {
20131         if(this.cb.disabled){
20132             return;
20133         }
20134         
20135         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20136             this.cb.items.remove(this);
20137             this.el.child('img').un('click', this.remove, this);
20138             this.el.remove();
20139             this.cb.updateHiddenEl();
20140
20141             this.cb.fireEvent('remove', this.cb, this);
20142         }
20143         
20144     }
20145 });/*
20146  * Based on:
20147  * Ext JS Library 1.1.1
20148  * Copyright(c) 2006-2007, Ext JS, LLC.
20149  *
20150  * Originally Released Under LGPL - original licence link has changed is not relivant.
20151  *
20152  * Fork - LGPL
20153  * <script type="text/javascript">
20154  */
20155 /**
20156  * @class Roo.form.Checkbox
20157  * @extends Roo.form.Field
20158  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20159  * @constructor
20160  * Creates a new Checkbox
20161  * @param {Object} config Configuration options
20162  */
20163 Roo.form.Checkbox = function(config){
20164     Roo.form.Checkbox.superclass.constructor.call(this, config);
20165     this.addEvents({
20166         /**
20167          * @event check
20168          * Fires when the checkbox is checked or unchecked.
20169              * @param {Roo.form.Checkbox} this This checkbox
20170              * @param {Boolean} checked The new checked value
20171              */
20172         check : true
20173     });
20174 };
20175
20176 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20177     /**
20178      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20179      */
20180     focusClass : undefined,
20181     /**
20182      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20183      */
20184     fieldClass: "x-form-field",
20185     /**
20186      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20187      */
20188     checked: false,
20189     /**
20190      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20191      * {tag: "input", type: "checkbox", autocomplete: "off"})
20192      */
20193     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20194     /**
20195      * @cfg {String} boxLabel The text that appears beside the checkbox
20196      */
20197     boxLabel : "",
20198     /**
20199      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20200      */  
20201     inputValue : '1',
20202     /**
20203      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20204      */
20205      valueOff: '0', // value when not checked..
20206
20207     actionMode : 'viewEl', 
20208     //
20209     // private
20210     itemCls : 'x-menu-check-item x-form-item',
20211     groupClass : 'x-menu-group-item',
20212     inputType : 'hidden',
20213     
20214     
20215     inSetChecked: false, // check that we are not calling self...
20216     
20217     inputElement: false, // real input element?
20218     basedOn: false, // ????
20219     
20220     isFormField: true, // not sure where this is needed!!!!
20221
20222     onResize : function(){
20223         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20224         if(!this.boxLabel){
20225             this.el.alignTo(this.wrap, 'c-c');
20226         }
20227     },
20228
20229     initEvents : function(){
20230         Roo.form.Checkbox.superclass.initEvents.call(this);
20231         this.el.on("click", this.onClick,  this);
20232         this.el.on("change", this.onClick,  this);
20233     },
20234
20235
20236     getResizeEl : function(){
20237         return this.wrap;
20238     },
20239
20240     getPositionEl : function(){
20241         return this.wrap;
20242     },
20243
20244     // private
20245     onRender : function(ct, position){
20246         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20247         /*
20248         if(this.inputValue !== undefined){
20249             this.el.dom.value = this.inputValue;
20250         }
20251         */
20252         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20253         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20254         var viewEl = this.wrap.createChild({ 
20255             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20256         this.viewEl = viewEl;   
20257         this.wrap.on('click', this.onClick,  this); 
20258         
20259         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20260         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20261         
20262         
20263         
20264         if(this.boxLabel){
20265             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20266         //    viewEl.on('click', this.onClick,  this); 
20267         }
20268         //if(this.checked){
20269             this.setChecked(this.checked);
20270         //}else{
20271             //this.checked = this.el.dom;
20272         //}
20273
20274     },
20275
20276     // private
20277     initValue : Roo.emptyFn,
20278
20279     /**
20280      * Returns the checked state of the checkbox.
20281      * @return {Boolean} True if checked, else false
20282      */
20283     getValue : function(){
20284         if(this.el){
20285             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20286         }
20287         return this.valueOff;
20288         
20289     },
20290
20291         // private
20292     onClick : function(){ 
20293         if (this.disabled) {
20294             return;
20295         }
20296         this.setChecked(!this.checked);
20297
20298         //if(this.el.dom.checked != this.checked){
20299         //    this.setValue(this.el.dom.checked);
20300        // }
20301     },
20302
20303     /**
20304      * Sets the checked state of the checkbox.
20305      * On is always based on a string comparison between inputValue and the param.
20306      * @param {Boolean/String} value - the value to set 
20307      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20308      */
20309     setValue : function(v,suppressEvent){
20310         
20311         
20312         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20313         //if(this.el && this.el.dom){
20314         //    this.el.dom.checked = this.checked;
20315         //    this.el.dom.defaultChecked = this.checked;
20316         //}
20317         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20318         //this.fireEvent("check", this, this.checked);
20319     },
20320     // private..
20321     setChecked : function(state,suppressEvent)
20322     {
20323         if (this.inSetChecked) {
20324             this.checked = state;
20325             return;
20326         }
20327         
20328     
20329         if(this.wrap){
20330             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20331         }
20332         this.checked = state;
20333         if(suppressEvent !== true){
20334             this.fireEvent('check', this, state);
20335         }
20336         this.inSetChecked = true;
20337         this.el.dom.value = state ? this.inputValue : this.valueOff;
20338         this.inSetChecked = false;
20339         
20340     },
20341     // handle setting of hidden value by some other method!!?!?
20342     setFromHidden: function()
20343     {
20344         if(!this.el){
20345             return;
20346         }
20347         //console.log("SET FROM HIDDEN");
20348         //alert('setFrom hidden');
20349         this.setValue(this.el.dom.value);
20350     },
20351     
20352     onDestroy : function()
20353     {
20354         if(this.viewEl){
20355             Roo.get(this.viewEl).remove();
20356         }
20357          
20358         Roo.form.Checkbox.superclass.onDestroy.call(this);
20359     },
20360     
20361     setBoxLabel : function(str)
20362     {
20363         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20364     }
20365
20366 });/*
20367  * Based on:
20368  * Ext JS Library 1.1.1
20369  * Copyright(c) 2006-2007, Ext JS, LLC.
20370  *
20371  * Originally Released Under LGPL - original licence link has changed is not relivant.
20372  *
20373  * Fork - LGPL
20374  * <script type="text/javascript">
20375  */
20376  
20377 /**
20378  * @class Roo.form.Radio
20379  * @extends Roo.form.Checkbox
20380  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20381  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20382  * @constructor
20383  * Creates a new Radio
20384  * @param {Object} config Configuration options
20385  */
20386 Roo.form.Radio = function(){
20387     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20388 };
20389 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20390     inputType: 'radio',
20391
20392     /**
20393      * If this radio is part of a group, it will return the selected value
20394      * @return {String}
20395      */
20396     getGroupValue : function(){
20397         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20398     },
20399     
20400     
20401     onRender : function(ct, position){
20402         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20403         
20404         if(this.inputValue !== undefined){
20405             this.el.dom.value = this.inputValue;
20406         }
20407          
20408         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20409         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20410         //var viewEl = this.wrap.createChild({ 
20411         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20412         //this.viewEl = viewEl;   
20413         //this.wrap.on('click', this.onClick,  this); 
20414         
20415         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20416         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20417         
20418         
20419         
20420         if(this.boxLabel){
20421             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20422         //    viewEl.on('click', this.onClick,  this); 
20423         }
20424          if(this.checked){
20425             this.el.dom.checked =   'checked' ;
20426         }
20427          
20428     } 
20429     
20430     
20431 });//<script type="text/javascript">
20432
20433 /*
20434  * Based  Ext JS Library 1.1.1
20435  * Copyright(c) 2006-2007, Ext JS, LLC.
20436  * LGPL
20437  *
20438  */
20439  
20440 /**
20441  * @class Roo.HtmlEditorCore
20442  * @extends Roo.Component
20443  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20444  *
20445  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20446  */
20447
20448 Roo.HtmlEditorCore = function(config){
20449     
20450     
20451     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20452     
20453     
20454     this.addEvents({
20455         /**
20456          * @event initialize
20457          * Fires when the editor is fully initialized (including the iframe)
20458          * @param {Roo.HtmlEditorCore} this
20459          */
20460         initialize: true,
20461         /**
20462          * @event activate
20463          * Fires when the editor is first receives the focus. Any insertion must wait
20464          * until after this event.
20465          * @param {Roo.HtmlEditorCore} this
20466          */
20467         activate: true,
20468          /**
20469          * @event beforesync
20470          * Fires before the textarea is updated with content from the editor iframe. Return false
20471          * to cancel the sync.
20472          * @param {Roo.HtmlEditorCore} this
20473          * @param {String} html
20474          */
20475         beforesync: true,
20476          /**
20477          * @event beforepush
20478          * Fires before the iframe editor is updated with content from the textarea. Return false
20479          * to cancel the push.
20480          * @param {Roo.HtmlEditorCore} this
20481          * @param {String} html
20482          */
20483         beforepush: true,
20484          /**
20485          * @event sync
20486          * Fires when the textarea is updated with content from the editor iframe.
20487          * @param {Roo.HtmlEditorCore} this
20488          * @param {String} html
20489          */
20490         sync: true,
20491          /**
20492          * @event push
20493          * Fires when the iframe editor is updated with content from the textarea.
20494          * @param {Roo.HtmlEditorCore} this
20495          * @param {String} html
20496          */
20497         push: true,
20498         
20499         /**
20500          * @event editorevent
20501          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20502          * @param {Roo.HtmlEditorCore} this
20503          */
20504         editorevent: true
20505         
20506     });
20507     
20508     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20509     
20510     // defaults : white / black...
20511     this.applyBlacklists();
20512     
20513     
20514     
20515 };
20516
20517
20518 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20519
20520
20521      /**
20522      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20523      */
20524     
20525     owner : false,
20526     
20527      /**
20528      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20529      *                        Roo.resizable.
20530      */
20531     resizable : false,
20532      /**
20533      * @cfg {Number} height (in pixels)
20534      */   
20535     height: 300,
20536    /**
20537      * @cfg {Number} width (in pixels)
20538      */   
20539     width: 500,
20540     
20541     /**
20542      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20543      * 
20544      */
20545     stylesheets: false,
20546     
20547     // id of frame..
20548     frameId: false,
20549     
20550     // private properties
20551     validationEvent : false,
20552     deferHeight: true,
20553     initialized : false,
20554     activated : false,
20555     sourceEditMode : false,
20556     onFocus : Roo.emptyFn,
20557     iframePad:3,
20558     hideMode:'offsets',
20559     
20560     clearUp: true,
20561     
20562     // blacklist + whitelisted elements..
20563     black: false,
20564     white: false,
20565      
20566     bodyCls : '',
20567
20568     /**
20569      * Protected method that will not generally be called directly. It
20570      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20571      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20572      */
20573     getDocMarkup : function(){
20574         // body styles..
20575         var st = '';
20576         
20577         // inherit styels from page...?? 
20578         if (this.stylesheets === false) {
20579             
20580             Roo.get(document.head).select('style').each(function(node) {
20581                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20582             });
20583             
20584             Roo.get(document.head).select('link').each(function(node) { 
20585                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20586             });
20587             
20588         } else if (!this.stylesheets.length) {
20589                 // simple..
20590                 st = '<style type="text/css">' +
20591                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20592                    '</style>';
20593         } else { 
20594             st = '<style type="text/css">' +
20595                     this.stylesheets +
20596                 '</style>';
20597         }
20598         
20599         st +=  '<style type="text/css">' +
20600             'IMG { cursor: pointer } ' +
20601         '</style>';
20602
20603         var cls = 'roo-htmleditor-body';
20604         
20605         if(this.bodyCls.length){
20606             cls += ' ' + this.bodyCls;
20607         }
20608         
20609         return '<html><head>' + st  +
20610             //<style type="text/css">' +
20611             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20612             //'</style>' +
20613             ' </head><body class="' +  cls + '"></body></html>';
20614     },
20615
20616     // private
20617     onRender : function(ct, position)
20618     {
20619         var _t = this;
20620         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20621         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20622         
20623         
20624         this.el.dom.style.border = '0 none';
20625         this.el.dom.setAttribute('tabIndex', -1);
20626         this.el.addClass('x-hidden hide');
20627         
20628         
20629         
20630         if(Roo.isIE){ // fix IE 1px bogus margin
20631             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20632         }
20633        
20634         
20635         this.frameId = Roo.id();
20636         
20637          
20638         
20639         var iframe = this.owner.wrap.createChild({
20640             tag: 'iframe',
20641             cls: 'form-control', // bootstrap..
20642             id: this.frameId,
20643             name: this.frameId,
20644             frameBorder : 'no',
20645             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20646         }, this.el
20647         );
20648         
20649         
20650         this.iframe = iframe.dom;
20651
20652          this.assignDocWin();
20653         
20654         this.doc.designMode = 'on';
20655        
20656         this.doc.open();
20657         this.doc.write(this.getDocMarkup());
20658         this.doc.close();
20659
20660         
20661         var task = { // must defer to wait for browser to be ready
20662             run : function(){
20663                 //console.log("run task?" + this.doc.readyState);
20664                 this.assignDocWin();
20665                 if(this.doc.body || this.doc.readyState == 'complete'){
20666                     try {
20667                         this.doc.designMode="on";
20668                     } catch (e) {
20669                         return;
20670                     }
20671                     Roo.TaskMgr.stop(task);
20672                     this.initEditor.defer(10, this);
20673                 }
20674             },
20675             interval : 10,
20676             duration: 10000,
20677             scope: this
20678         };
20679         Roo.TaskMgr.start(task);
20680
20681     },
20682
20683     // private
20684     onResize : function(w, h)
20685     {
20686          Roo.log('resize: ' +w + ',' + h );
20687         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20688         if(!this.iframe){
20689             return;
20690         }
20691         if(typeof w == 'number'){
20692             
20693             this.iframe.style.width = w + 'px';
20694         }
20695         if(typeof h == 'number'){
20696             
20697             this.iframe.style.height = h + 'px';
20698             if(this.doc){
20699                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20700             }
20701         }
20702         
20703     },
20704
20705     /**
20706      * Toggles the editor between standard and source edit mode.
20707      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20708      */
20709     toggleSourceEdit : function(sourceEditMode){
20710         
20711         this.sourceEditMode = sourceEditMode === true;
20712         
20713         if(this.sourceEditMode){
20714  
20715             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20716             
20717         }else{
20718             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20719             //this.iframe.className = '';
20720             this.deferFocus();
20721         }
20722         //this.setSize(this.owner.wrap.getSize());
20723         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20724     },
20725
20726     
20727   
20728
20729     /**
20730      * Protected method that will not generally be called directly. If you need/want
20731      * custom HTML cleanup, this is the method you should override.
20732      * @param {String} html The HTML to be cleaned
20733      * return {String} The cleaned HTML
20734      */
20735     cleanHtml : function(html){
20736         html = String(html);
20737         if(html.length > 5){
20738             if(Roo.isSafari){ // strip safari nonsense
20739                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20740             }
20741         }
20742         if(html == '&nbsp;'){
20743             html = '';
20744         }
20745         return html;
20746     },
20747
20748     /**
20749      * HTML Editor -> Textarea
20750      * Protected method that will not generally be called directly. Syncs the contents
20751      * of the editor iframe with the textarea.
20752      */
20753     syncValue : function(){
20754         if(this.initialized){
20755             var bd = (this.doc.body || this.doc.documentElement);
20756             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20757             var html = bd.innerHTML;
20758             if(Roo.isSafari){
20759                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20760                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20761                 if(m && m[1]){
20762                     html = '<div style="'+m[0]+'">' + html + '</div>';
20763                 }
20764             }
20765             html = this.cleanHtml(html);
20766             // fix up the special chars.. normaly like back quotes in word...
20767             // however we do not want to do this with chinese..
20768             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20769                 var cc = b.charCodeAt();
20770                 if (
20771                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20772                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20773                     (cc >= 0xf900 && cc < 0xfb00 )
20774                 ) {
20775                         return b;
20776                 }
20777                 return "&#"+cc+";" 
20778             });
20779             if(this.owner.fireEvent('beforesync', this, html) !== false){
20780                 this.el.dom.value = html;
20781                 this.owner.fireEvent('sync', this, html);
20782             }
20783         }
20784     },
20785
20786     /**
20787      * Protected method that will not generally be called directly. Pushes the value of the textarea
20788      * into the iframe editor.
20789      */
20790     pushValue : function(){
20791         if(this.initialized){
20792             var v = this.el.dom.value.trim();
20793             
20794 //            if(v.length < 1){
20795 //                v = '&#160;';
20796 //            }
20797             
20798             if(this.owner.fireEvent('beforepush', this, v) !== false){
20799                 var d = (this.doc.body || this.doc.documentElement);
20800                 d.innerHTML = v;
20801                 this.cleanUpPaste();
20802                 this.el.dom.value = d.innerHTML;
20803                 this.owner.fireEvent('push', this, v);
20804             }
20805         }
20806     },
20807
20808     // private
20809     deferFocus : function(){
20810         this.focus.defer(10, this);
20811     },
20812
20813     // doc'ed in Field
20814     focus : function(){
20815         if(this.win && !this.sourceEditMode){
20816             this.win.focus();
20817         }else{
20818             this.el.focus();
20819         }
20820     },
20821     
20822     assignDocWin: function()
20823     {
20824         var iframe = this.iframe;
20825         
20826          if(Roo.isIE){
20827             this.doc = iframe.contentWindow.document;
20828             this.win = iframe.contentWindow;
20829         } else {
20830 //            if (!Roo.get(this.frameId)) {
20831 //                return;
20832 //            }
20833 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20834 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20835             
20836             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20837                 return;
20838             }
20839             
20840             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20841             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20842         }
20843     },
20844     
20845     // private
20846     initEditor : function(){
20847         //console.log("INIT EDITOR");
20848         this.assignDocWin();
20849         
20850         
20851         
20852         this.doc.designMode="on";
20853         this.doc.open();
20854         this.doc.write(this.getDocMarkup());
20855         this.doc.close();
20856         
20857         var dbody = (this.doc.body || this.doc.documentElement);
20858         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20859         // this copies styles from the containing element into thsi one..
20860         // not sure why we need all of this..
20861         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20862         
20863         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20864         //ss['background-attachment'] = 'fixed'; // w3c
20865         dbody.bgProperties = 'fixed'; // ie
20866         //Roo.DomHelper.applyStyles(dbody, ss);
20867         Roo.EventManager.on(this.doc, {
20868             //'mousedown': this.onEditorEvent,
20869             'mouseup': this.onEditorEvent,
20870             'dblclick': this.onEditorEvent,
20871             'click': this.onEditorEvent,
20872             'keyup': this.onEditorEvent,
20873             buffer:100,
20874             scope: this
20875         });
20876         if(Roo.isGecko){
20877             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20878         }
20879         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20880             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20881         }
20882         this.initialized = true;
20883
20884         this.owner.fireEvent('initialize', this);
20885         this.pushValue();
20886     },
20887
20888     // private
20889     onDestroy : function(){
20890         
20891         
20892         
20893         if(this.rendered){
20894             
20895             //for (var i =0; i < this.toolbars.length;i++) {
20896             //    // fixme - ask toolbars for heights?
20897             //    this.toolbars[i].onDestroy();
20898            // }
20899             
20900             //this.wrap.dom.innerHTML = '';
20901             //this.wrap.remove();
20902         }
20903     },
20904
20905     // private
20906     onFirstFocus : function(){
20907         
20908         this.assignDocWin();
20909         
20910         
20911         this.activated = true;
20912          
20913     
20914         if(Roo.isGecko){ // prevent silly gecko errors
20915             this.win.focus();
20916             var s = this.win.getSelection();
20917             if(!s.focusNode || s.focusNode.nodeType != 3){
20918                 var r = s.getRangeAt(0);
20919                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20920                 r.collapse(true);
20921                 this.deferFocus();
20922             }
20923             try{
20924                 this.execCmd('useCSS', true);
20925                 this.execCmd('styleWithCSS', false);
20926             }catch(e){}
20927         }
20928         this.owner.fireEvent('activate', this);
20929     },
20930
20931     // private
20932     adjustFont: function(btn){
20933         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20934         //if(Roo.isSafari){ // safari
20935         //    adjust *= 2;
20936        // }
20937         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20938         if(Roo.isSafari){ // safari
20939             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20940             v =  (v < 10) ? 10 : v;
20941             v =  (v > 48) ? 48 : v;
20942             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20943             
20944         }
20945         
20946         
20947         v = Math.max(1, v+adjust);
20948         
20949         this.execCmd('FontSize', v  );
20950     },
20951
20952     onEditorEvent : function(e)
20953     {
20954         this.owner.fireEvent('editorevent', this, e);
20955       //  this.updateToolbar();
20956         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20957     },
20958
20959     insertTag : function(tg)
20960     {
20961         // could be a bit smarter... -> wrap the current selected tRoo..
20962         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20963             
20964             range = this.createRange(this.getSelection());
20965             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20966             wrappingNode.appendChild(range.extractContents());
20967             range.insertNode(wrappingNode);
20968
20969             return;
20970             
20971             
20972             
20973         }
20974         this.execCmd("formatblock",   tg);
20975         
20976     },
20977     
20978     insertText : function(txt)
20979     {
20980         
20981         
20982         var range = this.createRange();
20983         range.deleteContents();
20984                //alert(Sender.getAttribute('label'));
20985                
20986         range.insertNode(this.doc.createTextNode(txt));
20987     } ,
20988     
20989      
20990
20991     /**
20992      * Executes a Midas editor command on the editor document and performs necessary focus and
20993      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20994      * @param {String} cmd The Midas command
20995      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20996      */
20997     relayCmd : function(cmd, value){
20998         this.win.focus();
20999         this.execCmd(cmd, value);
21000         this.owner.fireEvent('editorevent', this);
21001         //this.updateToolbar();
21002         this.owner.deferFocus();
21003     },
21004
21005     /**
21006      * Executes a Midas editor command directly on the editor document.
21007      * For visual commands, you should use {@link #relayCmd} instead.
21008      * <b>This should only be called after the editor is initialized.</b>
21009      * @param {String} cmd The Midas command
21010      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21011      */
21012     execCmd : function(cmd, value){
21013         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21014         this.syncValue();
21015     },
21016  
21017  
21018    
21019     /**
21020      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21021      * to insert tRoo.
21022      * @param {String} text | dom node.. 
21023      */
21024     insertAtCursor : function(text)
21025     {
21026         
21027         if(!this.activated){
21028             return;
21029         }
21030         /*
21031         if(Roo.isIE){
21032             this.win.focus();
21033             var r = this.doc.selection.createRange();
21034             if(r){
21035                 r.collapse(true);
21036                 r.pasteHTML(text);
21037                 this.syncValue();
21038                 this.deferFocus();
21039             
21040             }
21041             return;
21042         }
21043         */
21044         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21045             this.win.focus();
21046             
21047             
21048             // from jquery ui (MIT licenced)
21049             var range, node;
21050             var win = this.win;
21051             
21052             if (win.getSelection && win.getSelection().getRangeAt) {
21053                 range = win.getSelection().getRangeAt(0);
21054                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21055                 range.insertNode(node);
21056             } else if (win.document.selection && win.document.selection.createRange) {
21057                 // no firefox support
21058                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21059                 win.document.selection.createRange().pasteHTML(txt);
21060             } else {
21061                 // no firefox support
21062                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21063                 this.execCmd('InsertHTML', txt);
21064             } 
21065             
21066             this.syncValue();
21067             
21068             this.deferFocus();
21069         }
21070     },
21071  // private
21072     mozKeyPress : function(e){
21073         if(e.ctrlKey){
21074             var c = e.getCharCode(), cmd;
21075           
21076             if(c > 0){
21077                 c = String.fromCharCode(c).toLowerCase();
21078                 switch(c){
21079                     case 'b':
21080                         cmd = 'bold';
21081                         break;
21082                     case 'i':
21083                         cmd = 'italic';
21084                         break;
21085                     
21086                     case 'u':
21087                         cmd = 'underline';
21088                         break;
21089                     
21090                     case 'v':
21091                         this.cleanUpPaste.defer(100, this);
21092                         return;
21093                         
21094                 }
21095                 if(cmd){
21096                     this.win.focus();
21097                     this.execCmd(cmd);
21098                     this.deferFocus();
21099                     e.preventDefault();
21100                 }
21101                 
21102             }
21103         }
21104     },
21105
21106     // private
21107     fixKeys : function(){ // load time branching for fastest keydown performance
21108         if(Roo.isIE){
21109             return function(e){
21110                 var k = e.getKey(), r;
21111                 if(k == e.TAB){
21112                     e.stopEvent();
21113                     r = this.doc.selection.createRange();
21114                     if(r){
21115                         r.collapse(true);
21116                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21117                         this.deferFocus();
21118                     }
21119                     return;
21120                 }
21121                 
21122                 if(k == e.ENTER){
21123                     r = this.doc.selection.createRange();
21124                     if(r){
21125                         var target = r.parentElement();
21126                         if(!target || target.tagName.toLowerCase() != 'li'){
21127                             e.stopEvent();
21128                             r.pasteHTML('<br />');
21129                             r.collapse(false);
21130                             r.select();
21131                         }
21132                     }
21133                 }
21134                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21135                     this.cleanUpPaste.defer(100, this);
21136                     return;
21137                 }
21138                 
21139                 
21140             };
21141         }else if(Roo.isOpera){
21142             return function(e){
21143                 var k = e.getKey();
21144                 if(k == e.TAB){
21145                     e.stopEvent();
21146                     this.win.focus();
21147                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21148                     this.deferFocus();
21149                 }
21150                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21151                     this.cleanUpPaste.defer(100, this);
21152                     return;
21153                 }
21154                 
21155             };
21156         }else if(Roo.isSafari){
21157             return function(e){
21158                 var k = e.getKey();
21159                 
21160                 if(k == e.TAB){
21161                     e.stopEvent();
21162                     this.execCmd('InsertText','\t');
21163                     this.deferFocus();
21164                     return;
21165                 }
21166                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21167                     this.cleanUpPaste.defer(100, this);
21168                     return;
21169                 }
21170                 
21171              };
21172         }
21173     }(),
21174     
21175     getAllAncestors: function()
21176     {
21177         var p = this.getSelectedNode();
21178         var a = [];
21179         if (!p) {
21180             a.push(p); // push blank onto stack..
21181             p = this.getParentElement();
21182         }
21183         
21184         
21185         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21186             a.push(p);
21187             p = p.parentNode;
21188         }
21189         a.push(this.doc.body);
21190         return a;
21191     },
21192     lastSel : false,
21193     lastSelNode : false,
21194     
21195     
21196     getSelection : function() 
21197     {
21198         this.assignDocWin();
21199         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21200     },
21201     
21202     getSelectedNode: function() 
21203     {
21204         // this may only work on Gecko!!!
21205         
21206         // should we cache this!!!!
21207         
21208         
21209         
21210          
21211         var range = this.createRange(this.getSelection()).cloneRange();
21212         
21213         if (Roo.isIE) {
21214             var parent = range.parentElement();
21215             while (true) {
21216                 var testRange = range.duplicate();
21217                 testRange.moveToElementText(parent);
21218                 if (testRange.inRange(range)) {
21219                     break;
21220                 }
21221                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21222                     break;
21223                 }
21224                 parent = parent.parentElement;
21225             }
21226             return parent;
21227         }
21228         
21229         // is ancestor a text element.
21230         var ac =  range.commonAncestorContainer;
21231         if (ac.nodeType == 3) {
21232             ac = ac.parentNode;
21233         }
21234         
21235         var ar = ac.childNodes;
21236          
21237         var nodes = [];
21238         var other_nodes = [];
21239         var has_other_nodes = false;
21240         for (var i=0;i<ar.length;i++) {
21241             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21242                 continue;
21243             }
21244             // fullly contained node.
21245             
21246             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21247                 nodes.push(ar[i]);
21248                 continue;
21249             }
21250             
21251             // probably selected..
21252             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21253                 other_nodes.push(ar[i]);
21254                 continue;
21255             }
21256             // outer..
21257             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21258                 continue;
21259             }
21260             
21261             
21262             has_other_nodes = true;
21263         }
21264         if (!nodes.length && other_nodes.length) {
21265             nodes= other_nodes;
21266         }
21267         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21268             return false;
21269         }
21270         
21271         return nodes[0];
21272     },
21273     createRange: function(sel)
21274     {
21275         // this has strange effects when using with 
21276         // top toolbar - not sure if it's a great idea.
21277         //this.editor.contentWindow.focus();
21278         if (typeof sel != "undefined") {
21279             try {
21280                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21281             } catch(e) {
21282                 return this.doc.createRange();
21283             }
21284         } else {
21285             return this.doc.createRange();
21286         }
21287     },
21288     getParentElement: function()
21289     {
21290         
21291         this.assignDocWin();
21292         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21293         
21294         var range = this.createRange(sel);
21295          
21296         try {
21297             var p = range.commonAncestorContainer;
21298             while (p.nodeType == 3) { // text node
21299                 p = p.parentNode;
21300             }
21301             return p;
21302         } catch (e) {
21303             return null;
21304         }
21305     
21306     },
21307     /***
21308      *
21309      * Range intersection.. the hard stuff...
21310      *  '-1' = before
21311      *  '0' = hits..
21312      *  '1' = after.
21313      *         [ -- selected range --- ]
21314      *   [fail]                        [fail]
21315      *
21316      *    basically..
21317      *      if end is before start or  hits it. fail.
21318      *      if start is after end or hits it fail.
21319      *
21320      *   if either hits (but other is outside. - then it's not 
21321      *   
21322      *    
21323      **/
21324     
21325     
21326     // @see http://www.thismuchiknow.co.uk/?p=64.
21327     rangeIntersectsNode : function(range, node)
21328     {
21329         var nodeRange = node.ownerDocument.createRange();
21330         try {
21331             nodeRange.selectNode(node);
21332         } catch (e) {
21333             nodeRange.selectNodeContents(node);
21334         }
21335     
21336         var rangeStartRange = range.cloneRange();
21337         rangeStartRange.collapse(true);
21338     
21339         var rangeEndRange = range.cloneRange();
21340         rangeEndRange.collapse(false);
21341     
21342         var nodeStartRange = nodeRange.cloneRange();
21343         nodeStartRange.collapse(true);
21344     
21345         var nodeEndRange = nodeRange.cloneRange();
21346         nodeEndRange.collapse(false);
21347     
21348         return rangeStartRange.compareBoundaryPoints(
21349                  Range.START_TO_START, nodeEndRange) == -1 &&
21350                rangeEndRange.compareBoundaryPoints(
21351                  Range.START_TO_START, nodeStartRange) == 1;
21352         
21353          
21354     },
21355     rangeCompareNode : function(range, node)
21356     {
21357         var nodeRange = node.ownerDocument.createRange();
21358         try {
21359             nodeRange.selectNode(node);
21360         } catch (e) {
21361             nodeRange.selectNodeContents(node);
21362         }
21363         
21364         
21365         range.collapse(true);
21366     
21367         nodeRange.collapse(true);
21368      
21369         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21370         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21371          
21372         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21373         
21374         var nodeIsBefore   =  ss == 1;
21375         var nodeIsAfter    = ee == -1;
21376         
21377         if (nodeIsBefore && nodeIsAfter) {
21378             return 0; // outer
21379         }
21380         if (!nodeIsBefore && nodeIsAfter) {
21381             return 1; //right trailed.
21382         }
21383         
21384         if (nodeIsBefore && !nodeIsAfter) {
21385             return 2;  // left trailed.
21386         }
21387         // fully contined.
21388         return 3;
21389     },
21390
21391     // private? - in a new class?
21392     cleanUpPaste :  function()
21393     {
21394         // cleans up the whole document..
21395         Roo.log('cleanuppaste');
21396         
21397         this.cleanUpChildren(this.doc.body);
21398         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21399         if (clean != this.doc.body.innerHTML) {
21400             this.doc.body.innerHTML = clean;
21401         }
21402         
21403     },
21404     
21405     cleanWordChars : function(input) {// change the chars to hex code
21406         var he = Roo.HtmlEditorCore;
21407         
21408         var output = input;
21409         Roo.each(he.swapCodes, function(sw) { 
21410             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21411             
21412             output = output.replace(swapper, sw[1]);
21413         });
21414         
21415         return output;
21416     },
21417     
21418     
21419     cleanUpChildren : function (n)
21420     {
21421         if (!n.childNodes.length) {
21422             return;
21423         }
21424         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21425            this.cleanUpChild(n.childNodes[i]);
21426         }
21427     },
21428     
21429     
21430         
21431     
21432     cleanUpChild : function (node)
21433     {
21434         var ed = this;
21435         //console.log(node);
21436         if (node.nodeName == "#text") {
21437             // clean up silly Windows -- stuff?
21438             return; 
21439         }
21440         if (node.nodeName == "#comment") {
21441             node.parentNode.removeChild(node);
21442             // clean up silly Windows -- stuff?
21443             return; 
21444         }
21445         var lcname = node.tagName.toLowerCase();
21446         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21447         // whitelist of tags..
21448         
21449         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21450             // remove node.
21451             node.parentNode.removeChild(node);
21452             return;
21453             
21454         }
21455         
21456         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21457         
21458         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21459         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21460         
21461         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21462         //    remove_keep_children = true;
21463         //}
21464         
21465         if (remove_keep_children) {
21466             this.cleanUpChildren(node);
21467             // inserts everything just before this node...
21468             while (node.childNodes.length) {
21469                 var cn = node.childNodes[0];
21470                 node.removeChild(cn);
21471                 node.parentNode.insertBefore(cn, node);
21472             }
21473             node.parentNode.removeChild(node);
21474             return;
21475         }
21476         
21477         if (!node.attributes || !node.attributes.length) {
21478             this.cleanUpChildren(node);
21479             return;
21480         }
21481         
21482         function cleanAttr(n,v)
21483         {
21484             
21485             if (v.match(/^\./) || v.match(/^\//)) {
21486                 return;
21487             }
21488             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21489                 return;
21490             }
21491             if (v.match(/^#/)) {
21492                 return;
21493             }
21494 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21495             node.removeAttribute(n);
21496             
21497         }
21498         
21499         var cwhite = this.cwhite;
21500         var cblack = this.cblack;
21501             
21502         function cleanStyle(n,v)
21503         {
21504             if (v.match(/expression/)) { //XSS?? should we even bother..
21505                 node.removeAttribute(n);
21506                 return;
21507             }
21508             
21509             var parts = v.split(/;/);
21510             var clean = [];
21511             
21512             Roo.each(parts, function(p) {
21513                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21514                 if (!p.length) {
21515                     return true;
21516                 }
21517                 var l = p.split(':').shift().replace(/\s+/g,'');
21518                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21519                 
21520                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21521 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21522                     //node.removeAttribute(n);
21523                     return true;
21524                 }
21525                 //Roo.log()
21526                 // only allow 'c whitelisted system attributes'
21527                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21528 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21529                     //node.removeAttribute(n);
21530                     return true;
21531                 }
21532                 
21533                 
21534                  
21535                 
21536                 clean.push(p);
21537                 return true;
21538             });
21539             if (clean.length) { 
21540                 node.setAttribute(n, clean.join(';'));
21541             } else {
21542                 node.removeAttribute(n);
21543             }
21544             
21545         }
21546         
21547         
21548         for (var i = node.attributes.length-1; i > -1 ; i--) {
21549             var a = node.attributes[i];
21550             //console.log(a);
21551             
21552             if (a.name.toLowerCase().substr(0,2)=='on')  {
21553                 node.removeAttribute(a.name);
21554                 continue;
21555             }
21556             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21557                 node.removeAttribute(a.name);
21558                 continue;
21559             }
21560             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21561                 cleanAttr(a.name,a.value); // fixme..
21562                 continue;
21563             }
21564             if (a.name == 'style') {
21565                 cleanStyle(a.name,a.value);
21566                 continue;
21567             }
21568             /// clean up MS crap..
21569             // tecnically this should be a list of valid class'es..
21570             
21571             
21572             if (a.name == 'class') {
21573                 if (a.value.match(/^Mso/)) {
21574                     node.className = '';
21575                 }
21576                 
21577                 if (a.value.match(/^body$/)) {
21578                     node.className = '';
21579                 }
21580                 continue;
21581             }
21582             
21583             // style cleanup!?
21584             // class cleanup?
21585             
21586         }
21587         
21588         
21589         this.cleanUpChildren(node);
21590         
21591         
21592     },
21593     
21594     /**
21595      * Clean up MS wordisms...
21596      */
21597     cleanWord : function(node)
21598     {
21599         
21600         
21601         if (!node) {
21602             this.cleanWord(this.doc.body);
21603             return;
21604         }
21605         if (node.nodeName == "#text") {
21606             // clean up silly Windows -- stuff?
21607             return; 
21608         }
21609         if (node.nodeName == "#comment") {
21610             node.parentNode.removeChild(node);
21611             // clean up silly Windows -- stuff?
21612             return; 
21613         }
21614         
21615         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21616             node.parentNode.removeChild(node);
21617             return;
21618         }
21619         
21620         // remove - but keep children..
21621         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21622             while (node.childNodes.length) {
21623                 var cn = node.childNodes[0];
21624                 node.removeChild(cn);
21625                 node.parentNode.insertBefore(cn, node);
21626             }
21627             node.parentNode.removeChild(node);
21628             this.iterateChildren(node, this.cleanWord);
21629             return;
21630         }
21631         // clean styles
21632         if (node.className.length) {
21633             
21634             var cn = node.className.split(/\W+/);
21635             var cna = [];
21636             Roo.each(cn, function(cls) {
21637                 if (cls.match(/Mso[a-zA-Z]+/)) {
21638                     return;
21639                 }
21640                 cna.push(cls);
21641             });
21642             node.className = cna.length ? cna.join(' ') : '';
21643             if (!cna.length) {
21644                 node.removeAttribute("class");
21645             }
21646         }
21647         
21648         if (node.hasAttribute("lang")) {
21649             node.removeAttribute("lang");
21650         }
21651         
21652         if (node.hasAttribute("style")) {
21653             
21654             var styles = node.getAttribute("style").split(";");
21655             var nstyle = [];
21656             Roo.each(styles, function(s) {
21657                 if (!s.match(/:/)) {
21658                     return;
21659                 }
21660                 var kv = s.split(":");
21661                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21662                     return;
21663                 }
21664                 // what ever is left... we allow.
21665                 nstyle.push(s);
21666             });
21667             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21668             if (!nstyle.length) {
21669                 node.removeAttribute('style');
21670             }
21671         }
21672         this.iterateChildren(node, this.cleanWord);
21673         
21674         
21675         
21676     },
21677     /**
21678      * iterateChildren of a Node, calling fn each time, using this as the scole..
21679      * @param {DomNode} node node to iterate children of.
21680      * @param {Function} fn method of this class to call on each item.
21681      */
21682     iterateChildren : function(node, fn)
21683     {
21684         if (!node.childNodes.length) {
21685                 return;
21686         }
21687         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21688            fn.call(this, node.childNodes[i])
21689         }
21690     },
21691     
21692     
21693     /**
21694      * cleanTableWidths.
21695      *
21696      * Quite often pasting from word etc.. results in tables with column and widths.
21697      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21698      *
21699      */
21700     cleanTableWidths : function(node)
21701     {
21702          
21703          
21704         if (!node) {
21705             this.cleanTableWidths(this.doc.body);
21706             return;
21707         }
21708         
21709         // ignore list...
21710         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21711             return; 
21712         }
21713         Roo.log(node.tagName);
21714         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21715             this.iterateChildren(node, this.cleanTableWidths);
21716             return;
21717         }
21718         if (node.hasAttribute('width')) {
21719             node.removeAttribute('width');
21720         }
21721         
21722          
21723         if (node.hasAttribute("style")) {
21724             // pretty basic...
21725             
21726             var styles = node.getAttribute("style").split(";");
21727             var nstyle = [];
21728             Roo.each(styles, function(s) {
21729                 if (!s.match(/:/)) {
21730                     return;
21731                 }
21732                 var kv = s.split(":");
21733                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21734                     return;
21735                 }
21736                 // what ever is left... we allow.
21737                 nstyle.push(s);
21738             });
21739             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21740             if (!nstyle.length) {
21741                 node.removeAttribute('style');
21742             }
21743         }
21744         
21745         this.iterateChildren(node, this.cleanTableWidths);
21746         
21747         
21748     },
21749     
21750     
21751     
21752     
21753     domToHTML : function(currentElement, depth, nopadtext) {
21754         
21755         depth = depth || 0;
21756         nopadtext = nopadtext || false;
21757     
21758         if (!currentElement) {
21759             return this.domToHTML(this.doc.body);
21760         }
21761         
21762         //Roo.log(currentElement);
21763         var j;
21764         var allText = false;
21765         var nodeName = currentElement.nodeName;
21766         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21767         
21768         if  (nodeName == '#text') {
21769             
21770             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21771         }
21772         
21773         
21774         var ret = '';
21775         if (nodeName != 'BODY') {
21776              
21777             var i = 0;
21778             // Prints the node tagName, such as <A>, <IMG>, etc
21779             if (tagName) {
21780                 var attr = [];
21781                 for(i = 0; i < currentElement.attributes.length;i++) {
21782                     // quoting?
21783                     var aname = currentElement.attributes.item(i).name;
21784                     if (!currentElement.attributes.item(i).value.length) {
21785                         continue;
21786                     }
21787                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21788                 }
21789                 
21790                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21791             } 
21792             else {
21793                 
21794                 // eack
21795             }
21796         } else {
21797             tagName = false;
21798         }
21799         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21800             return ret;
21801         }
21802         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21803             nopadtext = true;
21804         }
21805         
21806         
21807         // Traverse the tree
21808         i = 0;
21809         var currentElementChild = currentElement.childNodes.item(i);
21810         var allText = true;
21811         var innerHTML  = '';
21812         lastnode = '';
21813         while (currentElementChild) {
21814             // Formatting code (indent the tree so it looks nice on the screen)
21815             var nopad = nopadtext;
21816             if (lastnode == 'SPAN') {
21817                 nopad  = true;
21818             }
21819             // text
21820             if  (currentElementChild.nodeName == '#text') {
21821                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21822                 toadd = nopadtext ? toadd : toadd.trim();
21823                 if (!nopad && toadd.length > 80) {
21824                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21825                 }
21826                 innerHTML  += toadd;
21827                 
21828                 i++;
21829                 currentElementChild = currentElement.childNodes.item(i);
21830                 lastNode = '';
21831                 continue;
21832             }
21833             allText = false;
21834             
21835             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21836                 
21837             // Recursively traverse the tree structure of the child node
21838             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21839             lastnode = currentElementChild.nodeName;
21840             i++;
21841             currentElementChild=currentElement.childNodes.item(i);
21842         }
21843         
21844         ret += innerHTML;
21845         
21846         if (!allText) {
21847                 // The remaining code is mostly for formatting the tree
21848             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21849         }
21850         
21851         
21852         if (tagName) {
21853             ret+= "</"+tagName+">";
21854         }
21855         return ret;
21856         
21857     },
21858         
21859     applyBlacklists : function()
21860     {
21861         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21862         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21863         
21864         this.white = [];
21865         this.black = [];
21866         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21867             if (b.indexOf(tag) > -1) {
21868                 return;
21869             }
21870             this.white.push(tag);
21871             
21872         }, this);
21873         
21874         Roo.each(w, function(tag) {
21875             if (b.indexOf(tag) > -1) {
21876                 return;
21877             }
21878             if (this.white.indexOf(tag) > -1) {
21879                 return;
21880             }
21881             this.white.push(tag);
21882             
21883         }, this);
21884         
21885         
21886         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21887             if (w.indexOf(tag) > -1) {
21888                 return;
21889             }
21890             this.black.push(tag);
21891             
21892         }, this);
21893         
21894         Roo.each(b, function(tag) {
21895             if (w.indexOf(tag) > -1) {
21896                 return;
21897             }
21898             if (this.black.indexOf(tag) > -1) {
21899                 return;
21900             }
21901             this.black.push(tag);
21902             
21903         }, this);
21904         
21905         
21906         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21907         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21908         
21909         this.cwhite = [];
21910         this.cblack = [];
21911         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21912             if (b.indexOf(tag) > -1) {
21913                 return;
21914             }
21915             this.cwhite.push(tag);
21916             
21917         }, this);
21918         
21919         Roo.each(w, function(tag) {
21920             if (b.indexOf(tag) > -1) {
21921                 return;
21922             }
21923             if (this.cwhite.indexOf(tag) > -1) {
21924                 return;
21925             }
21926             this.cwhite.push(tag);
21927             
21928         }, this);
21929         
21930         
21931         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21932             if (w.indexOf(tag) > -1) {
21933                 return;
21934             }
21935             this.cblack.push(tag);
21936             
21937         }, this);
21938         
21939         Roo.each(b, function(tag) {
21940             if (w.indexOf(tag) > -1) {
21941                 return;
21942             }
21943             if (this.cblack.indexOf(tag) > -1) {
21944                 return;
21945             }
21946             this.cblack.push(tag);
21947             
21948         }, this);
21949     },
21950     
21951     setStylesheets : function(stylesheets)
21952     {
21953         if(typeof(stylesheets) == 'string'){
21954             Roo.get(this.iframe.contentDocument.head).createChild({
21955                 tag : 'link',
21956                 rel : 'stylesheet',
21957                 type : 'text/css',
21958                 href : stylesheets
21959             });
21960             
21961             return;
21962         }
21963         var _this = this;
21964      
21965         Roo.each(stylesheets, function(s) {
21966             if(!s.length){
21967                 return;
21968             }
21969             
21970             Roo.get(_this.iframe.contentDocument.head).createChild({
21971                 tag : 'link',
21972                 rel : 'stylesheet',
21973                 type : 'text/css',
21974                 href : s
21975             });
21976         });
21977
21978         
21979     },
21980     
21981     removeStylesheets : function()
21982     {
21983         var _this = this;
21984         
21985         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21986             s.remove();
21987         });
21988     },
21989     
21990     setStyle : function(style)
21991     {
21992         Roo.get(this.iframe.contentDocument.head).createChild({
21993             tag : 'style',
21994             type : 'text/css',
21995             html : style
21996         });
21997
21998         return;
21999     }
22000     
22001     // hide stuff that is not compatible
22002     /**
22003      * @event blur
22004      * @hide
22005      */
22006     /**
22007      * @event change
22008      * @hide
22009      */
22010     /**
22011      * @event focus
22012      * @hide
22013      */
22014     /**
22015      * @event specialkey
22016      * @hide
22017      */
22018     /**
22019      * @cfg {String} fieldClass @hide
22020      */
22021     /**
22022      * @cfg {String} focusClass @hide
22023      */
22024     /**
22025      * @cfg {String} autoCreate @hide
22026      */
22027     /**
22028      * @cfg {String} inputType @hide
22029      */
22030     /**
22031      * @cfg {String} invalidClass @hide
22032      */
22033     /**
22034      * @cfg {String} invalidText @hide
22035      */
22036     /**
22037      * @cfg {String} msgFx @hide
22038      */
22039     /**
22040      * @cfg {String} validateOnBlur @hide
22041      */
22042 });
22043
22044 Roo.HtmlEditorCore.white = [
22045         'area', 'br', 'img', 'input', 'hr', 'wbr',
22046         
22047        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22048        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22049        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22050        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22051        'table',   'ul',         'xmp', 
22052        
22053        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22054       'thead',   'tr', 
22055      
22056       'dir', 'menu', 'ol', 'ul', 'dl',
22057        
22058       'embed',  'object'
22059 ];
22060
22061
22062 Roo.HtmlEditorCore.black = [
22063     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22064         'applet', // 
22065         'base',   'basefont', 'bgsound', 'blink',  'body', 
22066         'frame',  'frameset', 'head',    'html',   'ilayer', 
22067         'iframe', 'layer',  'link',     'meta',    'object',   
22068         'script', 'style' ,'title',  'xml' // clean later..
22069 ];
22070 Roo.HtmlEditorCore.clean = [
22071     'script', 'style', 'title', 'xml'
22072 ];
22073 Roo.HtmlEditorCore.remove = [
22074     'font'
22075 ];
22076 // attributes..
22077
22078 Roo.HtmlEditorCore.ablack = [
22079     'on'
22080 ];
22081     
22082 Roo.HtmlEditorCore.aclean = [ 
22083     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22084 ];
22085
22086 // protocols..
22087 Roo.HtmlEditorCore.pwhite= [
22088         'http',  'https',  'mailto'
22089 ];
22090
22091 // white listed style attributes.
22092 Roo.HtmlEditorCore.cwhite= [
22093       //  'text-align', /// default is to allow most things..
22094       
22095          
22096 //        'font-size'//??
22097 ];
22098
22099 // black listed style attributes.
22100 Roo.HtmlEditorCore.cblack= [
22101       //  'font-size' -- this can be set by the project 
22102 ];
22103
22104
22105 Roo.HtmlEditorCore.swapCodes   =[ 
22106     [    8211, "--" ], 
22107     [    8212, "--" ], 
22108     [    8216,  "'" ],  
22109     [    8217, "'" ],  
22110     [    8220, '"' ],  
22111     [    8221, '"' ],  
22112     [    8226, "*" ],  
22113     [    8230, "..." ]
22114 ]; 
22115
22116     //<script type="text/javascript">
22117
22118 /*
22119  * Ext JS Library 1.1.1
22120  * Copyright(c) 2006-2007, Ext JS, LLC.
22121  * Licence LGPL
22122  * 
22123  */
22124  
22125  
22126 Roo.form.HtmlEditor = function(config){
22127     
22128     
22129     
22130     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22131     
22132     if (!this.toolbars) {
22133         this.toolbars = [];
22134     }
22135     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22136     
22137     
22138 };
22139
22140 /**
22141  * @class Roo.form.HtmlEditor
22142  * @extends Roo.form.Field
22143  * Provides a lightweight HTML Editor component.
22144  *
22145  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22146  * 
22147  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22148  * supported by this editor.</b><br/><br/>
22149  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22150  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22151  */
22152 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22153     /**
22154      * @cfg {Boolean} clearUp
22155      */
22156     clearUp : true,
22157       /**
22158      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22159      */
22160     toolbars : false,
22161    
22162      /**
22163      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22164      *                        Roo.resizable.
22165      */
22166     resizable : false,
22167      /**
22168      * @cfg {Number} height (in pixels)
22169      */   
22170     height: 300,
22171    /**
22172      * @cfg {Number} width (in pixels)
22173      */   
22174     width: 500,
22175     
22176     /**
22177      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22178      * 
22179      */
22180     stylesheets: false,
22181     
22182     
22183      /**
22184      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22185      * 
22186      */
22187     cblack: false,
22188     /**
22189      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22190      * 
22191      */
22192     cwhite: false,
22193     
22194      /**
22195      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22196      * 
22197      */
22198     black: false,
22199     /**
22200      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22201      * 
22202      */
22203     white: false,
22204     
22205     // id of frame..
22206     frameId: false,
22207     
22208     // private properties
22209     validationEvent : false,
22210     deferHeight: true,
22211     initialized : false,
22212     activated : false,
22213     
22214     onFocus : Roo.emptyFn,
22215     iframePad:3,
22216     hideMode:'offsets',
22217     
22218     actionMode : 'container', // defaults to hiding it...
22219     
22220     defaultAutoCreate : { // modified by initCompnoent..
22221         tag: "textarea",
22222         style:"width:500px;height:300px;",
22223         autocomplete: "new-password"
22224     },
22225
22226     // private
22227     initComponent : function(){
22228         this.addEvents({
22229             /**
22230              * @event initialize
22231              * Fires when the editor is fully initialized (including the iframe)
22232              * @param {HtmlEditor} this
22233              */
22234             initialize: true,
22235             /**
22236              * @event activate
22237              * Fires when the editor is first receives the focus. Any insertion must wait
22238              * until after this event.
22239              * @param {HtmlEditor} this
22240              */
22241             activate: true,
22242              /**
22243              * @event beforesync
22244              * Fires before the textarea is updated with content from the editor iframe. Return false
22245              * to cancel the sync.
22246              * @param {HtmlEditor} this
22247              * @param {String} html
22248              */
22249             beforesync: true,
22250              /**
22251              * @event beforepush
22252              * Fires before the iframe editor is updated with content from the textarea. Return false
22253              * to cancel the push.
22254              * @param {HtmlEditor} this
22255              * @param {String} html
22256              */
22257             beforepush: true,
22258              /**
22259              * @event sync
22260              * Fires when the textarea is updated with content from the editor iframe.
22261              * @param {HtmlEditor} this
22262              * @param {String} html
22263              */
22264             sync: true,
22265              /**
22266              * @event push
22267              * Fires when the iframe editor is updated with content from the textarea.
22268              * @param {HtmlEditor} this
22269              * @param {String} html
22270              */
22271             push: true,
22272              /**
22273              * @event editmodechange
22274              * Fires when the editor switches edit modes
22275              * @param {HtmlEditor} this
22276              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22277              */
22278             editmodechange: true,
22279             /**
22280              * @event editorevent
22281              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22282              * @param {HtmlEditor} this
22283              */
22284             editorevent: true,
22285             /**
22286              * @event firstfocus
22287              * Fires when on first focus - needed by toolbars..
22288              * @param {HtmlEditor} this
22289              */
22290             firstfocus: true,
22291             /**
22292              * @event autosave
22293              * Auto save the htmlEditor value as a file into Events
22294              * @param {HtmlEditor} this
22295              */
22296             autosave: true,
22297             /**
22298              * @event savedpreview
22299              * preview the saved version of htmlEditor
22300              * @param {HtmlEditor} this
22301              */
22302             savedpreview: true,
22303             
22304             /**
22305             * @event stylesheetsclick
22306             * Fires when press the Sytlesheets button
22307             * @param {Roo.HtmlEditorCore} this
22308             */
22309             stylesheetsclick: true
22310         });
22311         this.defaultAutoCreate =  {
22312             tag: "textarea",
22313             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22314             autocomplete: "new-password"
22315         };
22316     },
22317
22318     /**
22319      * Protected method that will not generally be called directly. It
22320      * is called when the editor creates its toolbar. Override this method if you need to
22321      * add custom toolbar buttons.
22322      * @param {HtmlEditor} editor
22323      */
22324     createToolbar : function(editor){
22325         Roo.log("create toolbars");
22326         if (!editor.toolbars || !editor.toolbars.length) {
22327             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22328         }
22329         
22330         for (var i =0 ; i < editor.toolbars.length;i++) {
22331             editor.toolbars[i] = Roo.factory(
22332                     typeof(editor.toolbars[i]) == 'string' ?
22333                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22334                 Roo.form.HtmlEditor);
22335             editor.toolbars[i].init(editor);
22336         }
22337          
22338         
22339     },
22340
22341      
22342     // private
22343     onRender : function(ct, position)
22344     {
22345         var _t = this;
22346         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22347         
22348         this.wrap = this.el.wrap({
22349             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22350         });
22351         
22352         this.editorcore.onRender(ct, position);
22353          
22354         if (this.resizable) {
22355             this.resizeEl = new Roo.Resizable(this.wrap, {
22356                 pinned : true,
22357                 wrap: true,
22358                 dynamic : true,
22359                 minHeight : this.height,
22360                 height: this.height,
22361                 handles : this.resizable,
22362                 width: this.width,
22363                 listeners : {
22364                     resize : function(r, w, h) {
22365                         _t.onResize(w,h); // -something
22366                     }
22367                 }
22368             });
22369             
22370         }
22371         this.createToolbar(this);
22372        
22373         
22374         if(!this.width){
22375             this.setSize(this.wrap.getSize());
22376         }
22377         if (this.resizeEl) {
22378             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22379             // should trigger onReize..
22380         }
22381         
22382         this.keyNav = new Roo.KeyNav(this.el, {
22383             
22384             "tab" : function(e){
22385                 e.preventDefault();
22386                 
22387                 var value = this.getValue();
22388                 
22389                 var start = this.el.dom.selectionStart;
22390                 var end = this.el.dom.selectionEnd;
22391                 
22392                 if(!e.shiftKey){
22393                     
22394                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22395                     this.el.dom.setSelectionRange(end + 1, end + 1);
22396                     return;
22397                 }
22398                 
22399                 var f = value.substring(0, start).split("\t");
22400                 
22401                 if(f.pop().length != 0){
22402                     return;
22403                 }
22404                 
22405                 this.setValue(f.join("\t") + value.substring(end));
22406                 this.el.dom.setSelectionRange(start - 1, start - 1);
22407                 
22408             },
22409             
22410             "home" : function(e){
22411                 e.preventDefault();
22412                 
22413                 var curr = this.el.dom.selectionStart;
22414                 var lines = this.getValue().split("\n");
22415                 
22416                 if(!lines.length){
22417                     return;
22418                 }
22419                 
22420                 if(e.ctrlKey){
22421                     this.el.dom.setSelectionRange(0, 0);
22422                     return;
22423                 }
22424                 
22425                 var pos = 0;
22426                 
22427                 for (var i = 0; i < lines.length;i++) {
22428                     pos += lines[i].length;
22429                     
22430                     if(i != 0){
22431                         pos += 1;
22432                     }
22433                     
22434                     if(pos < curr){
22435                         continue;
22436                     }
22437                     
22438                     pos -= lines[i].length;
22439                     
22440                     break;
22441                 }
22442                 
22443                 if(!e.shiftKey){
22444                     this.el.dom.setSelectionRange(pos, pos);
22445                     return;
22446                 }
22447                 
22448                 this.el.dom.selectionStart = pos;
22449                 this.el.dom.selectionEnd = curr;
22450             },
22451             
22452             "end" : function(e){
22453                 e.preventDefault();
22454                 
22455                 var curr = this.el.dom.selectionStart;
22456                 var lines = this.getValue().split("\n");
22457                 
22458                 if(!lines.length){
22459                     return;
22460                 }
22461                 
22462                 if(e.ctrlKey){
22463                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22464                     return;
22465                 }
22466                 
22467                 var pos = 0;
22468                 
22469                 for (var i = 0; i < lines.length;i++) {
22470                     
22471                     pos += lines[i].length;
22472                     
22473                     if(i != 0){
22474                         pos += 1;
22475                     }
22476                     
22477                     if(pos < curr){
22478                         continue;
22479                     }
22480                     
22481                     break;
22482                 }
22483                 
22484                 if(!e.shiftKey){
22485                     this.el.dom.setSelectionRange(pos, pos);
22486                     return;
22487                 }
22488                 
22489                 this.el.dom.selectionStart = curr;
22490                 this.el.dom.selectionEnd = pos;
22491             },
22492
22493             scope : this,
22494
22495             doRelay : function(foo, bar, hname){
22496                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22497             },
22498
22499             forceKeyDown: true
22500         });
22501         
22502 //        if(this.autosave && this.w){
22503 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22504 //        }
22505     },
22506
22507     // private
22508     onResize : function(w, h)
22509     {
22510         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22511         var ew = false;
22512         var eh = false;
22513         
22514         if(this.el ){
22515             if(typeof w == 'number'){
22516                 var aw = w - this.wrap.getFrameWidth('lr');
22517                 this.el.setWidth(this.adjustWidth('textarea', aw));
22518                 ew = aw;
22519             }
22520             if(typeof h == 'number'){
22521                 var tbh = 0;
22522                 for (var i =0; i < this.toolbars.length;i++) {
22523                     // fixme - ask toolbars for heights?
22524                     tbh += this.toolbars[i].tb.el.getHeight();
22525                     if (this.toolbars[i].footer) {
22526                         tbh += this.toolbars[i].footer.el.getHeight();
22527                     }
22528                 }
22529                 
22530                 
22531                 
22532                 
22533                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22534                 ah -= 5; // knock a few pixes off for look..
22535 //                Roo.log(ah);
22536                 this.el.setHeight(this.adjustWidth('textarea', ah));
22537                 var eh = ah;
22538             }
22539         }
22540         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22541         this.editorcore.onResize(ew,eh);
22542         
22543     },
22544
22545     /**
22546      * Toggles the editor between standard and source edit mode.
22547      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22548      */
22549     toggleSourceEdit : function(sourceEditMode)
22550     {
22551         this.editorcore.toggleSourceEdit(sourceEditMode);
22552         
22553         if(this.editorcore.sourceEditMode){
22554             Roo.log('editor - showing textarea');
22555             
22556 //            Roo.log('in');
22557 //            Roo.log(this.syncValue());
22558             this.editorcore.syncValue();
22559             this.el.removeClass('x-hidden');
22560             this.el.dom.removeAttribute('tabIndex');
22561             this.el.focus();
22562             
22563             for (var i = 0; i < this.toolbars.length; i++) {
22564                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22565                     this.toolbars[i].tb.hide();
22566                     this.toolbars[i].footer.hide();
22567                 }
22568             }
22569             
22570         }else{
22571             Roo.log('editor - hiding textarea');
22572 //            Roo.log('out')
22573 //            Roo.log(this.pushValue()); 
22574             this.editorcore.pushValue();
22575             
22576             this.el.addClass('x-hidden');
22577             this.el.dom.setAttribute('tabIndex', -1);
22578             
22579             for (var i = 0; i < this.toolbars.length; i++) {
22580                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22581                     this.toolbars[i].tb.show();
22582                     this.toolbars[i].footer.show();
22583                 }
22584             }
22585             
22586             //this.deferFocus();
22587         }
22588         
22589         this.setSize(this.wrap.getSize());
22590         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22591         
22592         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22593     },
22594  
22595     // private (for BoxComponent)
22596     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22597
22598     // private (for BoxComponent)
22599     getResizeEl : function(){
22600         return this.wrap;
22601     },
22602
22603     // private (for BoxComponent)
22604     getPositionEl : function(){
22605         return this.wrap;
22606     },
22607
22608     // private
22609     initEvents : function(){
22610         this.originalValue = this.getValue();
22611     },
22612
22613     /**
22614      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22615      * @method
22616      */
22617     markInvalid : Roo.emptyFn,
22618     /**
22619      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22620      * @method
22621      */
22622     clearInvalid : Roo.emptyFn,
22623
22624     setValue : function(v){
22625         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22626         this.editorcore.pushValue();
22627     },
22628
22629      
22630     // private
22631     deferFocus : function(){
22632         this.focus.defer(10, this);
22633     },
22634
22635     // doc'ed in Field
22636     focus : function(){
22637         this.editorcore.focus();
22638         
22639     },
22640       
22641
22642     // private
22643     onDestroy : function(){
22644         
22645         
22646         
22647         if(this.rendered){
22648             
22649             for (var i =0; i < this.toolbars.length;i++) {
22650                 // fixme - ask toolbars for heights?
22651                 this.toolbars[i].onDestroy();
22652             }
22653             
22654             this.wrap.dom.innerHTML = '';
22655             this.wrap.remove();
22656         }
22657     },
22658
22659     // private
22660     onFirstFocus : function(){
22661         //Roo.log("onFirstFocus");
22662         this.editorcore.onFirstFocus();
22663          for (var i =0; i < this.toolbars.length;i++) {
22664             this.toolbars[i].onFirstFocus();
22665         }
22666         
22667     },
22668     
22669     // private
22670     syncValue : function()
22671     {
22672         this.editorcore.syncValue();
22673     },
22674     
22675     pushValue : function()
22676     {
22677         this.editorcore.pushValue();
22678     },
22679     
22680     setStylesheets : function(stylesheets)
22681     {
22682         this.editorcore.setStylesheets(stylesheets);
22683     },
22684     
22685     removeStylesheets : function()
22686     {
22687         this.editorcore.removeStylesheets();
22688     }
22689      
22690     
22691     // hide stuff that is not compatible
22692     /**
22693      * @event blur
22694      * @hide
22695      */
22696     /**
22697      * @event change
22698      * @hide
22699      */
22700     /**
22701      * @event focus
22702      * @hide
22703      */
22704     /**
22705      * @event specialkey
22706      * @hide
22707      */
22708     /**
22709      * @cfg {String} fieldClass @hide
22710      */
22711     /**
22712      * @cfg {String} focusClass @hide
22713      */
22714     /**
22715      * @cfg {String} autoCreate @hide
22716      */
22717     /**
22718      * @cfg {String} inputType @hide
22719      */
22720     /**
22721      * @cfg {String} invalidClass @hide
22722      */
22723     /**
22724      * @cfg {String} invalidText @hide
22725      */
22726     /**
22727      * @cfg {String} msgFx @hide
22728      */
22729     /**
22730      * @cfg {String} validateOnBlur @hide
22731      */
22732 });
22733  
22734     // <script type="text/javascript">
22735 /*
22736  * Based on
22737  * Ext JS Library 1.1.1
22738  * Copyright(c) 2006-2007, Ext JS, LLC.
22739  *  
22740  
22741  */
22742
22743 /**
22744  * @class Roo.form.HtmlEditorToolbar1
22745  * Basic Toolbar
22746  * 
22747  * Usage:
22748  *
22749  new Roo.form.HtmlEditor({
22750     ....
22751     toolbars : [
22752         new Roo.form.HtmlEditorToolbar1({
22753             disable : { fonts: 1 , format: 1, ..., ... , ...],
22754             btns : [ .... ]
22755         })
22756     }
22757      
22758  * 
22759  * @cfg {Object} disable List of elements to disable..
22760  * @cfg {Array} btns List of additional buttons.
22761  * 
22762  * 
22763  * NEEDS Extra CSS? 
22764  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22765  */
22766  
22767 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22768 {
22769     
22770     Roo.apply(this, config);
22771     
22772     // default disabled, based on 'good practice'..
22773     this.disable = this.disable || {};
22774     Roo.applyIf(this.disable, {
22775         fontSize : true,
22776         colors : true,
22777         specialElements : true
22778     });
22779     
22780     
22781     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22782     // dont call parent... till later.
22783 }
22784
22785 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22786     
22787     tb: false,
22788     
22789     rendered: false,
22790     
22791     editor : false,
22792     editorcore : false,
22793     /**
22794      * @cfg {Object} disable  List of toolbar elements to disable
22795          
22796      */
22797     disable : false,
22798     
22799     
22800      /**
22801      * @cfg {String} createLinkText The default text for the create link prompt
22802      */
22803     createLinkText : 'Please enter the URL for the link:',
22804     /**
22805      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22806      */
22807     defaultLinkValue : 'http:/'+'/',
22808    
22809     
22810       /**
22811      * @cfg {Array} fontFamilies An array of available font families
22812      */
22813     fontFamilies : [
22814         'Arial',
22815         'Courier New',
22816         'Tahoma',
22817         'Times New Roman',
22818         'Verdana'
22819     ],
22820     
22821     specialChars : [
22822            "&#169;",
22823           "&#174;",     
22824           "&#8482;",    
22825           "&#163;" ,    
22826          // "&#8212;",    
22827           "&#8230;",    
22828           "&#247;" ,    
22829         //  "&#225;" ,     ?? a acute?
22830            "&#8364;"    , //Euro
22831        //   "&#8220;"    ,
22832         //  "&#8221;"    ,
22833         //  "&#8226;"    ,
22834           "&#176;"  //   , // degrees
22835
22836          // "&#233;"     , // e ecute
22837          // "&#250;"     , // u ecute?
22838     ],
22839     
22840     specialElements : [
22841         {
22842             text: "Insert Table",
22843             xtype: 'MenuItem',
22844             xns : Roo.Menu,
22845             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22846                 
22847         },
22848         {    
22849             text: "Insert Image",
22850             xtype: 'MenuItem',
22851             xns : Roo.Menu,
22852             ihtml : '<img src="about:blank"/>'
22853             
22854         }
22855         
22856          
22857     ],
22858     
22859     
22860     inputElements : [ 
22861             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22862             "input:submit", "input:button", "select", "textarea", "label" ],
22863     formats : [
22864         ["p"] ,  
22865         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22866         ["pre"],[ "code"], 
22867         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22868         ['div'],['span']
22869     ],
22870     
22871     cleanStyles : [
22872         "font-size"
22873     ],
22874      /**
22875      * @cfg {String} defaultFont default font to use.
22876      */
22877     defaultFont: 'tahoma',
22878    
22879     fontSelect : false,
22880     
22881     
22882     formatCombo : false,
22883     
22884     init : function(editor)
22885     {
22886         this.editor = editor;
22887         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22888         var editorcore = this.editorcore;
22889         
22890         var _t = this;
22891         
22892         var fid = editorcore.frameId;
22893         var etb = this;
22894         function btn(id, toggle, handler){
22895             var xid = fid + '-'+ id ;
22896             return {
22897                 id : xid,
22898                 cmd : id,
22899                 cls : 'x-btn-icon x-edit-'+id,
22900                 enableToggle:toggle !== false,
22901                 scope: _t, // was editor...
22902                 handler:handler||_t.relayBtnCmd,
22903                 clickEvent:'mousedown',
22904                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22905                 tabIndex:-1
22906             };
22907         }
22908         
22909         
22910         
22911         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22912         this.tb = tb;
22913          // stop form submits
22914         tb.el.on('click', function(e){
22915             e.preventDefault(); // what does this do?
22916         });
22917
22918         if(!this.disable.font) { // && !Roo.isSafari){
22919             /* why no safari for fonts 
22920             editor.fontSelect = tb.el.createChild({
22921                 tag:'select',
22922                 tabIndex: -1,
22923                 cls:'x-font-select',
22924                 html: this.createFontOptions()
22925             });
22926             
22927             editor.fontSelect.on('change', function(){
22928                 var font = editor.fontSelect.dom.value;
22929                 editor.relayCmd('fontname', font);
22930                 editor.deferFocus();
22931             }, editor);
22932             
22933             tb.add(
22934                 editor.fontSelect.dom,
22935                 '-'
22936             );
22937             */
22938             
22939         };
22940         if(!this.disable.formats){
22941             this.formatCombo = new Roo.form.ComboBox({
22942                 store: new Roo.data.SimpleStore({
22943                     id : 'tag',
22944                     fields: ['tag'],
22945                     data : this.formats // from states.js
22946                 }),
22947                 blockFocus : true,
22948                 name : '',
22949                 //autoCreate : {tag: "div",  size: "20"},
22950                 displayField:'tag',
22951                 typeAhead: false,
22952                 mode: 'local',
22953                 editable : false,
22954                 triggerAction: 'all',
22955                 emptyText:'Add tag',
22956                 selectOnFocus:true,
22957                 width:135,
22958                 listeners : {
22959                     'select': function(c, r, i) {
22960                         editorcore.insertTag(r.get('tag'));
22961                         editor.focus();
22962                     }
22963                 }
22964
22965             });
22966             tb.addField(this.formatCombo);
22967             
22968         }
22969         
22970         if(!this.disable.format){
22971             tb.add(
22972                 btn('bold'),
22973                 btn('italic'),
22974                 btn('underline'),
22975                 btn('strikethrough')
22976             );
22977         };
22978         if(!this.disable.fontSize){
22979             tb.add(
22980                 '-',
22981                 
22982                 
22983                 btn('increasefontsize', false, editorcore.adjustFont),
22984                 btn('decreasefontsize', false, editorcore.adjustFont)
22985             );
22986         };
22987         
22988         
22989         if(!this.disable.colors){
22990             tb.add(
22991                 '-', {
22992                     id:editorcore.frameId +'-forecolor',
22993                     cls:'x-btn-icon x-edit-forecolor',
22994                     clickEvent:'mousedown',
22995                     tooltip: this.buttonTips['forecolor'] || undefined,
22996                     tabIndex:-1,
22997                     menu : new Roo.menu.ColorMenu({
22998                         allowReselect: true,
22999                         focus: Roo.emptyFn,
23000                         value:'000000',
23001                         plain:true,
23002                         selectHandler: function(cp, color){
23003                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23004                             editor.deferFocus();
23005                         },
23006                         scope: editorcore,
23007                         clickEvent:'mousedown'
23008                     })
23009                 }, {
23010                     id:editorcore.frameId +'backcolor',
23011                     cls:'x-btn-icon x-edit-backcolor',
23012                     clickEvent:'mousedown',
23013                     tooltip: this.buttonTips['backcolor'] || undefined,
23014                     tabIndex:-1,
23015                     menu : new Roo.menu.ColorMenu({
23016                         focus: Roo.emptyFn,
23017                         value:'FFFFFF',
23018                         plain:true,
23019                         allowReselect: true,
23020                         selectHandler: function(cp, color){
23021                             if(Roo.isGecko){
23022                                 editorcore.execCmd('useCSS', false);
23023                                 editorcore.execCmd('hilitecolor', color);
23024                                 editorcore.execCmd('useCSS', true);
23025                                 editor.deferFocus();
23026                             }else{
23027                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23028                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23029                                 editor.deferFocus();
23030                             }
23031                         },
23032                         scope:editorcore,
23033                         clickEvent:'mousedown'
23034                     })
23035                 }
23036             );
23037         };
23038         // now add all the items...
23039         
23040
23041         if(!this.disable.alignments){
23042             tb.add(
23043                 '-',
23044                 btn('justifyleft'),
23045                 btn('justifycenter'),
23046                 btn('justifyright')
23047             );
23048         };
23049
23050         //if(!Roo.isSafari){
23051             if(!this.disable.links){
23052                 tb.add(
23053                     '-',
23054                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23055                 );
23056             };
23057
23058             if(!this.disable.lists){
23059                 tb.add(
23060                     '-',
23061                     btn('insertorderedlist'),
23062                     btn('insertunorderedlist')
23063                 );
23064             }
23065             if(!this.disable.sourceEdit){
23066                 tb.add(
23067                     '-',
23068                     btn('sourceedit', true, function(btn){
23069                         this.toggleSourceEdit(btn.pressed);
23070                     })
23071                 );
23072             }
23073         //}
23074         
23075         var smenu = { };
23076         // special menu.. - needs to be tidied up..
23077         if (!this.disable.special) {
23078             smenu = {
23079                 text: "&#169;",
23080                 cls: 'x-edit-none',
23081                 
23082                 menu : {
23083                     items : []
23084                 }
23085             };
23086             for (var i =0; i < this.specialChars.length; i++) {
23087                 smenu.menu.items.push({
23088                     
23089                     html: this.specialChars[i],
23090                     handler: function(a,b) {
23091                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23092                         //editor.insertAtCursor(a.html);
23093                         
23094                     },
23095                     tabIndex:-1
23096                 });
23097             }
23098             
23099             
23100             tb.add(smenu);
23101             
23102             
23103         }
23104         
23105         var cmenu = { };
23106         if (!this.disable.cleanStyles) {
23107             cmenu = {
23108                 cls: 'x-btn-icon x-btn-clear',
23109                 
23110                 menu : {
23111                     items : []
23112                 }
23113             };
23114             for (var i =0; i < this.cleanStyles.length; i++) {
23115                 cmenu.menu.items.push({
23116                     actiontype : this.cleanStyles[i],
23117                     html: 'Remove ' + this.cleanStyles[i],
23118                     handler: function(a,b) {
23119 //                        Roo.log(a);
23120 //                        Roo.log(b);
23121                         var c = Roo.get(editorcore.doc.body);
23122                         c.select('[style]').each(function(s) {
23123                             s.dom.style.removeProperty(a.actiontype);
23124                         });
23125                         editorcore.syncValue();
23126                     },
23127                     tabIndex:-1
23128                 });
23129             }
23130              cmenu.menu.items.push({
23131                 actiontype : 'tablewidths',
23132                 html: 'Remove Table Widths',
23133                 handler: function(a,b) {
23134                     editorcore.cleanTableWidths();
23135                     editorcore.syncValue();
23136                 },
23137                 tabIndex:-1
23138             });
23139             cmenu.menu.items.push({
23140                 actiontype : 'word',
23141                 html: 'Remove MS Word Formating',
23142                 handler: function(a,b) {
23143                     editorcore.cleanWord();
23144                     editorcore.syncValue();
23145                 },
23146                 tabIndex:-1
23147             });
23148             
23149             cmenu.menu.items.push({
23150                 actiontype : 'all',
23151                 html: 'Remove All Styles',
23152                 handler: function(a,b) {
23153                     
23154                     var c = Roo.get(editorcore.doc.body);
23155                     c.select('[style]').each(function(s) {
23156                         s.dom.removeAttribute('style');
23157                     });
23158                     editorcore.syncValue();
23159                 },
23160                 tabIndex:-1
23161             });
23162             
23163             cmenu.menu.items.push({
23164                 actiontype : 'all',
23165                 html: 'Remove All CSS Classes',
23166                 handler: function(a,b) {
23167                     
23168                     var c = Roo.get(editorcore.doc.body);
23169                     c.select('[class]').each(function(s) {
23170                         s.dom.className = '';
23171                     });
23172                     editorcore.syncValue();
23173                 },
23174                 tabIndex:-1
23175             });
23176             
23177              cmenu.menu.items.push({
23178                 actiontype : 'tidy',
23179                 html: 'Tidy HTML Source',
23180                 handler: function(a,b) {
23181                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23182                     editorcore.syncValue();
23183                 },
23184                 tabIndex:-1
23185             });
23186             
23187             
23188             tb.add(cmenu);
23189         }
23190          
23191         if (!this.disable.specialElements) {
23192             var semenu = {
23193                 text: "Other;",
23194                 cls: 'x-edit-none',
23195                 menu : {
23196                     items : []
23197                 }
23198             };
23199             for (var i =0; i < this.specialElements.length; i++) {
23200                 semenu.menu.items.push(
23201                     Roo.apply({ 
23202                         handler: function(a,b) {
23203                             editor.insertAtCursor(this.ihtml);
23204                         }
23205                     }, this.specialElements[i])
23206                 );
23207                     
23208             }
23209             
23210             tb.add(semenu);
23211             
23212             
23213         }
23214          
23215         
23216         if (this.btns) {
23217             for(var i =0; i< this.btns.length;i++) {
23218                 var b = Roo.factory(this.btns[i],Roo.form);
23219                 b.cls =  'x-edit-none';
23220                 
23221                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23222                     b.cls += ' x-init-enable';
23223                 }
23224                 
23225                 b.scope = editorcore;
23226                 tb.add(b);
23227             }
23228         
23229         }
23230         
23231         
23232         
23233         // disable everything...
23234         
23235         this.tb.items.each(function(item){
23236             
23237            if(
23238                 item.id != editorcore.frameId+ '-sourceedit' && 
23239                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23240             ){
23241                 
23242                 item.disable();
23243             }
23244         });
23245         this.rendered = true;
23246         
23247         // the all the btns;
23248         editor.on('editorevent', this.updateToolbar, this);
23249         // other toolbars need to implement this..
23250         //editor.on('editmodechange', this.updateToolbar, this);
23251     },
23252     
23253     
23254     relayBtnCmd : function(btn) {
23255         this.editorcore.relayCmd(btn.cmd);
23256     },
23257     // private used internally
23258     createLink : function(){
23259         Roo.log("create link?");
23260         var url = prompt(this.createLinkText, this.defaultLinkValue);
23261         if(url && url != 'http:/'+'/'){
23262             this.editorcore.relayCmd('createlink', url);
23263         }
23264     },
23265
23266     
23267     /**
23268      * Protected method that will not generally be called directly. It triggers
23269      * a toolbar update by reading the markup state of the current selection in the editor.
23270      */
23271     updateToolbar: function(){
23272
23273         if(!this.editorcore.activated){
23274             this.editor.onFirstFocus();
23275             return;
23276         }
23277
23278         var btns = this.tb.items.map, 
23279             doc = this.editorcore.doc,
23280             frameId = this.editorcore.frameId;
23281
23282         if(!this.disable.font && !Roo.isSafari){
23283             /*
23284             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23285             if(name != this.fontSelect.dom.value){
23286                 this.fontSelect.dom.value = name;
23287             }
23288             */
23289         }
23290         if(!this.disable.format){
23291             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23292             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23293             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23294             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23295         }
23296         if(!this.disable.alignments){
23297             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23298             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23299             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23300         }
23301         if(!Roo.isSafari && !this.disable.lists){
23302             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23303             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23304         }
23305         
23306         var ans = this.editorcore.getAllAncestors();
23307         if (this.formatCombo) {
23308             
23309             
23310             var store = this.formatCombo.store;
23311             this.formatCombo.setValue("");
23312             for (var i =0; i < ans.length;i++) {
23313                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23314                     // select it..
23315                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23316                     break;
23317                 }
23318             }
23319         }
23320         
23321         
23322         
23323         // hides menus... - so this cant be on a menu...
23324         Roo.menu.MenuMgr.hideAll();
23325
23326         //this.editorsyncValue();
23327     },
23328    
23329     
23330     createFontOptions : function(){
23331         var buf = [], fs = this.fontFamilies, ff, lc;
23332         
23333         
23334         
23335         for(var i = 0, len = fs.length; i< len; i++){
23336             ff = fs[i];
23337             lc = ff.toLowerCase();
23338             buf.push(
23339                 '<option value="',lc,'" style="font-family:',ff,';"',
23340                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23341                     ff,
23342                 '</option>'
23343             );
23344         }
23345         return buf.join('');
23346     },
23347     
23348     toggleSourceEdit : function(sourceEditMode){
23349         
23350         Roo.log("toolbar toogle");
23351         if(sourceEditMode === undefined){
23352             sourceEditMode = !this.sourceEditMode;
23353         }
23354         this.sourceEditMode = sourceEditMode === true;
23355         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23356         // just toggle the button?
23357         if(btn.pressed !== this.sourceEditMode){
23358             btn.toggle(this.sourceEditMode);
23359             return;
23360         }
23361         
23362         if(sourceEditMode){
23363             Roo.log("disabling buttons");
23364             this.tb.items.each(function(item){
23365                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23366                     item.disable();
23367                 }
23368             });
23369           
23370         }else{
23371             Roo.log("enabling buttons");
23372             if(this.editorcore.initialized){
23373                 this.tb.items.each(function(item){
23374                     item.enable();
23375                 });
23376             }
23377             
23378         }
23379         Roo.log("calling toggole on editor");
23380         // tell the editor that it's been pressed..
23381         this.editor.toggleSourceEdit(sourceEditMode);
23382        
23383     },
23384      /**
23385      * Object collection of toolbar tooltips for the buttons in the editor. The key
23386      * is the command id associated with that button and the value is a valid QuickTips object.
23387      * For example:
23388 <pre><code>
23389 {
23390     bold : {
23391         title: 'Bold (Ctrl+B)',
23392         text: 'Make the selected text bold.',
23393         cls: 'x-html-editor-tip'
23394     },
23395     italic : {
23396         title: 'Italic (Ctrl+I)',
23397         text: 'Make the selected text italic.',
23398         cls: 'x-html-editor-tip'
23399     },
23400     ...
23401 </code></pre>
23402     * @type Object
23403      */
23404     buttonTips : {
23405         bold : {
23406             title: 'Bold (Ctrl+B)',
23407             text: 'Make the selected text bold.',
23408             cls: 'x-html-editor-tip'
23409         },
23410         italic : {
23411             title: 'Italic (Ctrl+I)',
23412             text: 'Make the selected text italic.',
23413             cls: 'x-html-editor-tip'
23414         },
23415         underline : {
23416             title: 'Underline (Ctrl+U)',
23417             text: 'Underline the selected text.',
23418             cls: 'x-html-editor-tip'
23419         },
23420         strikethrough : {
23421             title: 'Strikethrough',
23422             text: 'Strikethrough the selected text.',
23423             cls: 'x-html-editor-tip'
23424         },
23425         increasefontsize : {
23426             title: 'Grow Text',
23427             text: 'Increase the font size.',
23428             cls: 'x-html-editor-tip'
23429         },
23430         decreasefontsize : {
23431             title: 'Shrink Text',
23432             text: 'Decrease the font size.',
23433             cls: 'x-html-editor-tip'
23434         },
23435         backcolor : {
23436             title: 'Text Highlight Color',
23437             text: 'Change the background color of the selected text.',
23438             cls: 'x-html-editor-tip'
23439         },
23440         forecolor : {
23441             title: 'Font Color',
23442             text: 'Change the color of the selected text.',
23443             cls: 'x-html-editor-tip'
23444         },
23445         justifyleft : {
23446             title: 'Align Text Left',
23447             text: 'Align text to the left.',
23448             cls: 'x-html-editor-tip'
23449         },
23450         justifycenter : {
23451             title: 'Center Text',
23452             text: 'Center text in the editor.',
23453             cls: 'x-html-editor-tip'
23454         },
23455         justifyright : {
23456             title: 'Align Text Right',
23457             text: 'Align text to the right.',
23458             cls: 'x-html-editor-tip'
23459         },
23460         insertunorderedlist : {
23461             title: 'Bullet List',
23462             text: 'Start a bulleted list.',
23463             cls: 'x-html-editor-tip'
23464         },
23465         insertorderedlist : {
23466             title: 'Numbered List',
23467             text: 'Start a numbered list.',
23468             cls: 'x-html-editor-tip'
23469         },
23470         createlink : {
23471             title: 'Hyperlink',
23472             text: 'Make the selected text a hyperlink.',
23473             cls: 'x-html-editor-tip'
23474         },
23475         sourceedit : {
23476             title: 'Source Edit',
23477             text: 'Switch to source editing mode.',
23478             cls: 'x-html-editor-tip'
23479         }
23480     },
23481     // private
23482     onDestroy : function(){
23483         if(this.rendered){
23484             
23485             this.tb.items.each(function(item){
23486                 if(item.menu){
23487                     item.menu.removeAll();
23488                     if(item.menu.el){
23489                         item.menu.el.destroy();
23490                     }
23491                 }
23492                 item.destroy();
23493             });
23494              
23495         }
23496     },
23497     onFirstFocus: function() {
23498         this.tb.items.each(function(item){
23499            item.enable();
23500         });
23501     }
23502 });
23503
23504
23505
23506
23507 // <script type="text/javascript">
23508 /*
23509  * Based on
23510  * Ext JS Library 1.1.1
23511  * Copyright(c) 2006-2007, Ext JS, LLC.
23512  *  
23513  
23514  */
23515
23516  
23517 /**
23518  * @class Roo.form.HtmlEditor.ToolbarContext
23519  * Context Toolbar
23520  * 
23521  * Usage:
23522  *
23523  new Roo.form.HtmlEditor({
23524     ....
23525     toolbars : [
23526         { xtype: 'ToolbarStandard', styles : {} }
23527         { xtype: 'ToolbarContext', disable : {} }
23528     ]
23529 })
23530
23531      
23532  * 
23533  * @config : {Object} disable List of elements to disable.. (not done yet.)
23534  * @config : {Object} styles  Map of styles available.
23535  * 
23536  */
23537
23538 Roo.form.HtmlEditor.ToolbarContext = function(config)
23539 {
23540     
23541     Roo.apply(this, config);
23542     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23543     // dont call parent... till later.
23544     this.styles = this.styles || {};
23545 }
23546
23547  
23548
23549 Roo.form.HtmlEditor.ToolbarContext.types = {
23550     'IMG' : {
23551         width : {
23552             title: "Width",
23553             width: 40
23554         },
23555         height:  {
23556             title: "Height",
23557             width: 40
23558         },
23559         align: {
23560             title: "Align",
23561             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23562             width : 80
23563             
23564         },
23565         border: {
23566             title: "Border",
23567             width: 40
23568         },
23569         alt: {
23570             title: "Alt",
23571             width: 120
23572         },
23573         src : {
23574             title: "Src",
23575             width: 220
23576         }
23577         
23578     },
23579     'A' : {
23580         name : {
23581             title: "Name",
23582             width: 50
23583         },
23584         target:  {
23585             title: "Target",
23586             width: 120
23587         },
23588         href:  {
23589             title: "Href",
23590             width: 220
23591         } // border?
23592         
23593     },
23594     'TABLE' : {
23595         rows : {
23596             title: "Rows",
23597             width: 20
23598         },
23599         cols : {
23600             title: "Cols",
23601             width: 20
23602         },
23603         width : {
23604             title: "Width",
23605             width: 40
23606         },
23607         height : {
23608             title: "Height",
23609             width: 40
23610         },
23611         border : {
23612             title: "Border",
23613             width: 20
23614         }
23615     },
23616     'TD' : {
23617         width : {
23618             title: "Width",
23619             width: 40
23620         },
23621         height : {
23622             title: "Height",
23623             width: 40
23624         },   
23625         align: {
23626             title: "Align",
23627             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23628             width: 80
23629         },
23630         valign: {
23631             title: "Valign",
23632             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23633             width: 80
23634         },
23635         colspan: {
23636             title: "Colspan",
23637             width: 20
23638             
23639         },
23640          'font-family'  : {
23641             title : "Font",
23642             style : 'fontFamily',
23643             displayField: 'display',
23644             optname : 'font-family',
23645             width: 140
23646         }
23647     },
23648     'INPUT' : {
23649         name : {
23650             title: "name",
23651             width: 120
23652         },
23653         value : {
23654             title: "Value",
23655             width: 120
23656         },
23657         width : {
23658             title: "Width",
23659             width: 40
23660         }
23661     },
23662     'LABEL' : {
23663         'for' : {
23664             title: "For",
23665             width: 120
23666         }
23667     },
23668     'TEXTAREA' : {
23669           name : {
23670             title: "name",
23671             width: 120
23672         },
23673         rows : {
23674             title: "Rows",
23675             width: 20
23676         },
23677         cols : {
23678             title: "Cols",
23679             width: 20
23680         }
23681     },
23682     'SELECT' : {
23683         name : {
23684             title: "name",
23685             width: 120
23686         },
23687         selectoptions : {
23688             title: "Options",
23689             width: 200
23690         }
23691     },
23692     
23693     // should we really allow this??
23694     // should this just be 
23695     'BODY' : {
23696         title : {
23697             title: "Title",
23698             width: 200,
23699             disabled : true
23700         }
23701     },
23702     'SPAN' : {
23703         'font-family'  : {
23704             title : "Font",
23705             style : 'fontFamily',
23706             displayField: 'display',
23707             optname : 'font-family',
23708             width: 140
23709         }
23710     },
23711     'DIV' : {
23712         'font-family'  : {
23713             title : "Font",
23714             style : 'fontFamily',
23715             displayField: 'display',
23716             optname : 'font-family',
23717             width: 140
23718         }
23719     },
23720      'P' : {
23721         'font-family'  : {
23722             title : "Font",
23723             style : 'fontFamily',
23724             displayField: 'display',
23725             optname : 'font-family',
23726             width: 140
23727         }
23728     },
23729     
23730     '*' : {
23731         // empty..
23732     }
23733
23734 };
23735
23736 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23737 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23738
23739 Roo.form.HtmlEditor.ToolbarContext.options = {
23740         'font-family'  : [ 
23741                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23742                 [ 'Courier New', 'Courier New'],
23743                 [ 'Tahoma', 'Tahoma'],
23744                 [ 'Times New Roman,serif', 'Times'],
23745                 [ 'Verdana','Verdana' ]
23746         ]
23747 };
23748
23749 // fixme - these need to be configurable..
23750  
23751
23752 //Roo.form.HtmlEditor.ToolbarContext.types
23753
23754
23755 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23756     
23757     tb: false,
23758     
23759     rendered: false,
23760     
23761     editor : false,
23762     editorcore : false,
23763     /**
23764      * @cfg {Object} disable  List of toolbar elements to disable
23765          
23766      */
23767     disable : false,
23768     /**
23769      * @cfg {Object} styles List of styles 
23770      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23771      *
23772      * These must be defined in the page, so they get rendered correctly..
23773      * .headline { }
23774      * TD.underline { }
23775      * 
23776      */
23777     styles : false,
23778     
23779     options: false,
23780     
23781     toolbars : false,
23782     
23783     init : function(editor)
23784     {
23785         this.editor = editor;
23786         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23787         var editorcore = this.editorcore;
23788         
23789         var fid = editorcore.frameId;
23790         var etb = this;
23791         function btn(id, toggle, handler){
23792             var xid = fid + '-'+ id ;
23793             return {
23794                 id : xid,
23795                 cmd : id,
23796                 cls : 'x-btn-icon x-edit-'+id,
23797                 enableToggle:toggle !== false,
23798                 scope: editorcore, // was editor...
23799                 handler:handler||editorcore.relayBtnCmd,
23800                 clickEvent:'mousedown',
23801                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23802                 tabIndex:-1
23803             };
23804         }
23805         // create a new element.
23806         var wdiv = editor.wrap.createChild({
23807                 tag: 'div'
23808             }, editor.wrap.dom.firstChild.nextSibling, true);
23809         
23810         // can we do this more than once??
23811         
23812          // stop form submits
23813       
23814  
23815         // disable everything...
23816         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23817         this.toolbars = {};
23818            
23819         for (var i in  ty) {
23820           
23821             this.toolbars[i] = this.buildToolbar(ty[i],i);
23822         }
23823         this.tb = this.toolbars.BODY;
23824         this.tb.el.show();
23825         this.buildFooter();
23826         this.footer.show();
23827         editor.on('hide', function( ) { this.footer.hide() }, this);
23828         editor.on('show', function( ) { this.footer.show() }, this);
23829         
23830          
23831         this.rendered = true;
23832         
23833         // the all the btns;
23834         editor.on('editorevent', this.updateToolbar, this);
23835         // other toolbars need to implement this..
23836         //editor.on('editmodechange', this.updateToolbar, this);
23837     },
23838     
23839     
23840     
23841     /**
23842      * Protected method that will not generally be called directly. It triggers
23843      * a toolbar update by reading the markup state of the current selection in the editor.
23844      *
23845      * Note you can force an update by calling on('editorevent', scope, false)
23846      */
23847     updateToolbar: function(editor,ev,sel){
23848
23849         //Roo.log(ev);
23850         // capture mouse up - this is handy for selecting images..
23851         // perhaps should go somewhere else...
23852         if(!this.editorcore.activated){
23853              this.editor.onFirstFocus();
23854             return;
23855         }
23856         
23857         
23858         
23859         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23860         // selectNode - might want to handle IE?
23861         if (ev &&
23862             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23863             ev.target && ev.target.tagName == 'IMG') {
23864             // they have click on an image...
23865             // let's see if we can change the selection...
23866             sel = ev.target;
23867          
23868               var nodeRange = sel.ownerDocument.createRange();
23869             try {
23870                 nodeRange.selectNode(sel);
23871             } catch (e) {
23872                 nodeRange.selectNodeContents(sel);
23873             }
23874             //nodeRange.collapse(true);
23875             var s = this.editorcore.win.getSelection();
23876             s.removeAllRanges();
23877             s.addRange(nodeRange);
23878         }  
23879         
23880       
23881         var updateFooter = sel ? false : true;
23882         
23883         
23884         var ans = this.editorcore.getAllAncestors();
23885         
23886         // pick
23887         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23888         
23889         if (!sel) { 
23890             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23891             sel = sel ? sel : this.editorcore.doc.body;
23892             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23893             
23894         }
23895         // pick a menu that exists..
23896         var tn = sel.tagName.toUpperCase();
23897         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23898         
23899         tn = sel.tagName.toUpperCase();
23900         
23901         var lastSel = this.tb.selectedNode;
23902         
23903         this.tb.selectedNode = sel;
23904         
23905         // if current menu does not match..
23906         
23907         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23908                 
23909             this.tb.el.hide();
23910             ///console.log("show: " + tn);
23911             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23912             this.tb.el.show();
23913             // update name
23914             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23915             
23916             
23917             // update attributes
23918             if (this.tb.fields) {
23919                 this.tb.fields.each(function(e) {
23920                     if (e.stylename) {
23921                         e.setValue(sel.style[e.stylename]);
23922                         return;
23923                     } 
23924                    e.setValue(sel.getAttribute(e.attrname));
23925                 });
23926             }
23927             
23928             var hasStyles = false;
23929             for(var i in this.styles) {
23930                 hasStyles = true;
23931                 break;
23932             }
23933             
23934             // update styles
23935             if (hasStyles) { 
23936                 var st = this.tb.fields.item(0);
23937                 
23938                 st.store.removeAll();
23939                
23940                 
23941                 var cn = sel.className.split(/\s+/);
23942                 
23943                 var avs = [];
23944                 if (this.styles['*']) {
23945                     
23946                     Roo.each(this.styles['*'], function(v) {
23947                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23948                     });
23949                 }
23950                 if (this.styles[tn]) { 
23951                     Roo.each(this.styles[tn], function(v) {
23952                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23953                     });
23954                 }
23955                 
23956                 st.store.loadData(avs);
23957                 st.collapse();
23958                 st.setValue(cn);
23959             }
23960             // flag our selected Node.
23961             this.tb.selectedNode = sel;
23962            
23963            
23964             Roo.menu.MenuMgr.hideAll();
23965
23966         }
23967         
23968         if (!updateFooter) {
23969             //this.footDisp.dom.innerHTML = ''; 
23970             return;
23971         }
23972         // update the footer
23973         //
23974         var html = '';
23975         
23976         this.footerEls = ans.reverse();
23977         Roo.each(this.footerEls, function(a,i) {
23978             if (!a) { return; }
23979             html += html.length ? ' &gt; '  :  '';
23980             
23981             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23982             
23983         });
23984        
23985         // 
23986         var sz = this.footDisp.up('td').getSize();
23987         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23988         this.footDisp.dom.style.marginLeft = '5px';
23989         
23990         this.footDisp.dom.style.overflow = 'hidden';
23991         
23992         this.footDisp.dom.innerHTML = html;
23993             
23994         //this.editorsyncValue();
23995     },
23996      
23997     
23998    
23999        
24000     // private
24001     onDestroy : function(){
24002         if(this.rendered){
24003             
24004             this.tb.items.each(function(item){
24005                 if(item.menu){
24006                     item.menu.removeAll();
24007                     if(item.menu.el){
24008                         item.menu.el.destroy();
24009                     }
24010                 }
24011                 item.destroy();
24012             });
24013              
24014         }
24015     },
24016     onFirstFocus: function() {
24017         // need to do this for all the toolbars..
24018         this.tb.items.each(function(item){
24019            item.enable();
24020         });
24021     },
24022     buildToolbar: function(tlist, nm)
24023     {
24024         var editor = this.editor;
24025         var editorcore = this.editorcore;
24026          // create a new element.
24027         var wdiv = editor.wrap.createChild({
24028                 tag: 'div'
24029             }, editor.wrap.dom.firstChild.nextSibling, true);
24030         
24031        
24032         var tb = new Roo.Toolbar(wdiv);
24033         // add the name..
24034         
24035         tb.add(nm+ ":&nbsp;");
24036         
24037         var styles = [];
24038         for(var i in this.styles) {
24039             styles.push(i);
24040         }
24041         
24042         // styles...
24043         if (styles && styles.length) {
24044             
24045             // this needs a multi-select checkbox...
24046             tb.addField( new Roo.form.ComboBox({
24047                 store: new Roo.data.SimpleStore({
24048                     id : 'val',
24049                     fields: ['val', 'selected'],
24050                     data : [] 
24051                 }),
24052                 name : '-roo-edit-className',
24053                 attrname : 'className',
24054                 displayField: 'val',
24055                 typeAhead: false,
24056                 mode: 'local',
24057                 editable : false,
24058                 triggerAction: 'all',
24059                 emptyText:'Select Style',
24060                 selectOnFocus:true,
24061                 width: 130,
24062                 listeners : {
24063                     'select': function(c, r, i) {
24064                         // initial support only for on class per el..
24065                         tb.selectedNode.className =  r ? r.get('val') : '';
24066                         editorcore.syncValue();
24067                     }
24068                 }
24069     
24070             }));
24071         }
24072         
24073         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24074         var tbops = tbc.options;
24075         
24076         for (var i in tlist) {
24077             
24078             var item = tlist[i];
24079             tb.add(item.title + ":&nbsp;");
24080             
24081             
24082             //optname == used so you can configure the options available..
24083             var opts = item.opts ? item.opts : false;
24084             if (item.optname) {
24085                 opts = tbops[item.optname];
24086            
24087             }
24088             
24089             if (opts) {
24090                 // opts == pulldown..
24091                 tb.addField( new Roo.form.ComboBox({
24092                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24093                         id : 'val',
24094                         fields: ['val', 'display'],
24095                         data : opts  
24096                     }),
24097                     name : '-roo-edit-' + i,
24098                     attrname : i,
24099                     stylename : item.style ? item.style : false,
24100                     displayField: item.displayField ? item.displayField : 'val',
24101                     valueField :  'val',
24102                     typeAhead: false,
24103                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24104                     editable : false,
24105                     triggerAction: 'all',
24106                     emptyText:'Select',
24107                     selectOnFocus:true,
24108                     width: item.width ? item.width  : 130,
24109                     listeners : {
24110                         'select': function(c, r, i) {
24111                             if (c.stylename) {
24112                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24113                                 return;
24114                             }
24115                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24116                         }
24117                     }
24118
24119                 }));
24120                 continue;
24121                     
24122                  
24123                 
24124                 tb.addField( new Roo.form.TextField({
24125                     name: i,
24126                     width: 100,
24127                     //allowBlank:false,
24128                     value: ''
24129                 }));
24130                 continue;
24131             }
24132             tb.addField( new Roo.form.TextField({
24133                 name: '-roo-edit-' + i,
24134                 attrname : i,
24135                 
24136                 width: item.width,
24137                 //allowBlank:true,
24138                 value: '',
24139                 listeners: {
24140                     'change' : function(f, nv, ov) {
24141                         tb.selectedNode.setAttribute(f.attrname, nv);
24142                         editorcore.syncValue();
24143                     }
24144                 }
24145             }));
24146              
24147         }
24148         
24149         var _this = this;
24150         
24151         if(nm == 'BODY'){
24152             tb.addSeparator();
24153         
24154             tb.addButton( {
24155                 text: 'Stylesheets',
24156
24157                 listeners : {
24158                     click : function ()
24159                     {
24160                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24161                     }
24162                 }
24163             });
24164         }
24165         
24166         tb.addFill();
24167         tb.addButton( {
24168             text: 'Remove Tag',
24169     
24170             listeners : {
24171                 click : function ()
24172                 {
24173                     // remove
24174                     // undo does not work.
24175                      
24176                     var sn = tb.selectedNode;
24177                     
24178                     var pn = sn.parentNode;
24179                     
24180                     var stn =  sn.childNodes[0];
24181                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24182                     while (sn.childNodes.length) {
24183                         var node = sn.childNodes[0];
24184                         sn.removeChild(node);
24185                         //Roo.log(node);
24186                         pn.insertBefore(node, sn);
24187                         
24188                     }
24189                     pn.removeChild(sn);
24190                     var range = editorcore.createRange();
24191         
24192                     range.setStart(stn,0);
24193                     range.setEnd(en,0); //????
24194                     //range.selectNode(sel);
24195                     
24196                     
24197                     var selection = editorcore.getSelection();
24198                     selection.removeAllRanges();
24199                     selection.addRange(range);
24200                     
24201                     
24202                     
24203                     //_this.updateToolbar(null, null, pn);
24204                     _this.updateToolbar(null, null, null);
24205                     _this.footDisp.dom.innerHTML = ''; 
24206                 }
24207             }
24208             
24209                     
24210                 
24211             
24212         });
24213         
24214         
24215         tb.el.on('click', function(e){
24216             e.preventDefault(); // what does this do?
24217         });
24218         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24219         tb.el.hide();
24220         tb.name = nm;
24221         // dont need to disable them... as they will get hidden
24222         return tb;
24223          
24224         
24225     },
24226     buildFooter : function()
24227     {
24228         
24229         var fel = this.editor.wrap.createChild();
24230         this.footer = new Roo.Toolbar(fel);
24231         // toolbar has scrolly on left / right?
24232         var footDisp= new Roo.Toolbar.Fill();
24233         var _t = this;
24234         this.footer.add(
24235             {
24236                 text : '&lt;',
24237                 xtype: 'Button',
24238                 handler : function() {
24239                     _t.footDisp.scrollTo('left',0,true)
24240                 }
24241             }
24242         );
24243         this.footer.add( footDisp );
24244         this.footer.add( 
24245             {
24246                 text : '&gt;',
24247                 xtype: 'Button',
24248                 handler : function() {
24249                     // no animation..
24250                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24251                 }
24252             }
24253         );
24254         var fel = Roo.get(footDisp.el);
24255         fel.addClass('x-editor-context');
24256         this.footDispWrap = fel; 
24257         this.footDispWrap.overflow  = 'hidden';
24258         
24259         this.footDisp = fel.createChild();
24260         this.footDispWrap.on('click', this.onContextClick, this)
24261         
24262         
24263     },
24264     onContextClick : function (ev,dom)
24265     {
24266         ev.preventDefault();
24267         var  cn = dom.className;
24268         //Roo.log(cn);
24269         if (!cn.match(/x-ed-loc-/)) {
24270             return;
24271         }
24272         var n = cn.split('-').pop();
24273         var ans = this.footerEls;
24274         var sel = ans[n];
24275         
24276          // pick
24277         var range = this.editorcore.createRange();
24278         
24279         range.selectNodeContents(sel);
24280         //range.selectNode(sel);
24281         
24282         
24283         var selection = this.editorcore.getSelection();
24284         selection.removeAllRanges();
24285         selection.addRange(range);
24286         
24287         
24288         
24289         this.updateToolbar(null, null, sel);
24290         
24291         
24292     }
24293     
24294     
24295     
24296     
24297     
24298 });
24299
24300
24301
24302
24303
24304 /*
24305  * Based on:
24306  * Ext JS Library 1.1.1
24307  * Copyright(c) 2006-2007, Ext JS, LLC.
24308  *
24309  * Originally Released Under LGPL - original licence link has changed is not relivant.
24310  *
24311  * Fork - LGPL
24312  * <script type="text/javascript">
24313  */
24314  
24315 /**
24316  * @class Roo.form.BasicForm
24317  * @extends Roo.util.Observable
24318  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24319  * @constructor
24320  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24321  * @param {Object} config Configuration options
24322  */
24323 Roo.form.BasicForm = function(el, config){
24324     this.allItems = [];
24325     this.childForms = [];
24326     Roo.apply(this, config);
24327     /*
24328      * The Roo.form.Field items in this form.
24329      * @type MixedCollection
24330      */
24331      
24332      
24333     this.items = new Roo.util.MixedCollection(false, function(o){
24334         return o.id || (o.id = Roo.id());
24335     });
24336     this.addEvents({
24337         /**
24338          * @event beforeaction
24339          * Fires before any action is performed. Return false to cancel the action.
24340          * @param {Form} this
24341          * @param {Action} action The action to be performed
24342          */
24343         beforeaction: true,
24344         /**
24345          * @event actionfailed
24346          * Fires when an action fails.
24347          * @param {Form} this
24348          * @param {Action} action The action that failed
24349          */
24350         actionfailed : true,
24351         /**
24352          * @event actioncomplete
24353          * Fires when an action is completed.
24354          * @param {Form} this
24355          * @param {Action} action The action that completed
24356          */
24357         actioncomplete : true
24358     });
24359     if(el){
24360         this.initEl(el);
24361     }
24362     Roo.form.BasicForm.superclass.constructor.call(this);
24363 };
24364
24365 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24366     /**
24367      * @cfg {String} method
24368      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24369      */
24370     /**
24371      * @cfg {DataReader} reader
24372      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24373      * This is optional as there is built-in support for processing JSON.
24374      */
24375     /**
24376      * @cfg {DataReader} errorReader
24377      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24378      * This is completely optional as there is built-in support for processing JSON.
24379      */
24380     /**
24381      * @cfg {String} url
24382      * The URL to use for form actions if one isn't supplied in the action options.
24383      */
24384     /**
24385      * @cfg {Boolean} fileUpload
24386      * Set to true if this form is a file upload.
24387      */
24388      
24389     /**
24390      * @cfg {Object} baseParams
24391      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24392      */
24393      /**
24394      
24395     /**
24396      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24397      */
24398     timeout: 30,
24399
24400     // private
24401     activeAction : null,
24402
24403     /**
24404      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24405      * or setValues() data instead of when the form was first created.
24406      */
24407     trackResetOnLoad : false,
24408     
24409     
24410     /**
24411      * childForms - used for multi-tab forms
24412      * @type {Array}
24413      */
24414     childForms : false,
24415     
24416     /**
24417      * allItems - full list of fields.
24418      * @type {Array}
24419      */
24420     allItems : false,
24421     
24422     /**
24423      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24424      * element by passing it or its id or mask the form itself by passing in true.
24425      * @type Mixed
24426      */
24427     waitMsgTarget : false,
24428     
24429     /**
24430      * @type Boolean
24431      */
24432     disableMask : false,
24433
24434     // private
24435     initEl : function(el){
24436         this.el = Roo.get(el);
24437         this.id = this.el.id || Roo.id();
24438         this.el.on('submit', this.onSubmit, this);
24439         this.el.addClass('x-form');
24440     },
24441
24442     // private
24443     onSubmit : function(e){
24444         e.stopEvent();
24445     },
24446
24447     /**
24448      * Returns true if client-side validation on the form is successful.
24449      * @return Boolean
24450      */
24451     isValid : function(){
24452         var valid = true;
24453         this.items.each(function(f){
24454            if(!f.validate()){
24455                valid = false;
24456            }
24457         });
24458         return valid;
24459     },
24460
24461     /**
24462      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24463      * @return Boolean
24464      */
24465     isDirty : function(){
24466         var dirty = false;
24467         this.items.each(function(f){
24468            if(f.isDirty()){
24469                dirty = true;
24470                return false;
24471            }
24472         });
24473         return dirty;
24474     },
24475     
24476     /**
24477      * Returns true if any fields in this form have changed since their original load. (New version)
24478      * @return Boolean
24479      */
24480     
24481     hasChanged : function()
24482     {
24483         var dirty = false;
24484         this.items.each(function(f){
24485            if(f.hasChanged()){
24486                dirty = true;
24487                return false;
24488            }
24489         });
24490         return dirty;
24491         
24492     },
24493     /**
24494      * Resets all hasChanged to 'false' -
24495      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24496      * So hasChanged storage is only to be used for this purpose
24497      * @return Boolean
24498      */
24499     resetHasChanged : function()
24500     {
24501         this.items.each(function(f){
24502            f.resetHasChanged();
24503         });
24504         
24505     },
24506     
24507     
24508     /**
24509      * Performs a predefined action (submit or load) or custom actions you define on this form.
24510      * @param {String} actionName The name of the action type
24511      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24512      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24513      * accept other config options):
24514      * <pre>
24515 Property          Type             Description
24516 ----------------  ---------------  ----------------------------------------------------------------------------------
24517 url               String           The url for the action (defaults to the form's url)
24518 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24519 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24520 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24521                                    validate the form on the client (defaults to false)
24522      * </pre>
24523      * @return {BasicForm} this
24524      */
24525     doAction : function(action, options){
24526         if(typeof action == 'string'){
24527             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24528         }
24529         if(this.fireEvent('beforeaction', this, action) !== false){
24530             this.beforeAction(action);
24531             action.run.defer(100, action);
24532         }
24533         return this;
24534     },
24535
24536     /**
24537      * Shortcut to do a submit action.
24538      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24539      * @return {BasicForm} this
24540      */
24541     submit : function(options){
24542         this.doAction('submit', options);
24543         return this;
24544     },
24545
24546     /**
24547      * Shortcut to do a load action.
24548      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24549      * @return {BasicForm} this
24550      */
24551     load : function(options){
24552         this.doAction('load', options);
24553         return this;
24554     },
24555
24556     /**
24557      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24558      * @param {Record} record The record to edit
24559      * @return {BasicForm} this
24560      */
24561     updateRecord : function(record){
24562         record.beginEdit();
24563         var fs = record.fields;
24564         fs.each(function(f){
24565             var field = this.findField(f.name);
24566             if(field){
24567                 record.set(f.name, field.getValue());
24568             }
24569         }, this);
24570         record.endEdit();
24571         return this;
24572     },
24573
24574     /**
24575      * Loads an Roo.data.Record into this form.
24576      * @param {Record} record The record to load
24577      * @return {BasicForm} this
24578      */
24579     loadRecord : function(record){
24580         this.setValues(record.data);
24581         return this;
24582     },
24583
24584     // private
24585     beforeAction : function(action){
24586         var o = action.options;
24587         
24588         if(!this.disableMask) {
24589             if(this.waitMsgTarget === true){
24590                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24591             }else if(this.waitMsgTarget){
24592                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24593                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24594             }else {
24595                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24596             }
24597         }
24598         
24599          
24600     },
24601
24602     // private
24603     afterAction : function(action, success){
24604         this.activeAction = null;
24605         var o = action.options;
24606         
24607         if(!this.disableMask) {
24608             if(this.waitMsgTarget === true){
24609                 this.el.unmask();
24610             }else if(this.waitMsgTarget){
24611                 this.waitMsgTarget.unmask();
24612             }else{
24613                 Roo.MessageBox.updateProgress(1);
24614                 Roo.MessageBox.hide();
24615             }
24616         }
24617         
24618         if(success){
24619             if(o.reset){
24620                 this.reset();
24621             }
24622             Roo.callback(o.success, o.scope, [this, action]);
24623             this.fireEvent('actioncomplete', this, action);
24624             
24625         }else{
24626             
24627             // failure condition..
24628             // we have a scenario where updates need confirming.
24629             // eg. if a locking scenario exists..
24630             // we look for { errors : { needs_confirm : true }} in the response.
24631             if (
24632                 (typeof(action.result) != 'undefined')  &&
24633                 (typeof(action.result.errors) != 'undefined')  &&
24634                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24635            ){
24636                 var _t = this;
24637                 Roo.MessageBox.confirm(
24638                     "Change requires confirmation",
24639                     action.result.errorMsg,
24640                     function(r) {
24641                         if (r != 'yes') {
24642                             return;
24643                         }
24644                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24645                     }
24646                     
24647                 );
24648                 
24649                 
24650                 
24651                 return;
24652             }
24653             
24654             Roo.callback(o.failure, o.scope, [this, action]);
24655             // show an error message if no failed handler is set..
24656             if (!this.hasListener('actionfailed')) {
24657                 Roo.MessageBox.alert("Error",
24658                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24659                         action.result.errorMsg :
24660                         "Saving Failed, please check your entries or try again"
24661                 );
24662             }
24663             
24664             this.fireEvent('actionfailed', this, action);
24665         }
24666         
24667     },
24668
24669     /**
24670      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24671      * @param {String} id The value to search for
24672      * @return Field
24673      */
24674     findField : function(id){
24675         var field = this.items.get(id);
24676         if(!field){
24677             this.items.each(function(f){
24678                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24679                     field = f;
24680                     return false;
24681                 }
24682             });
24683         }
24684         return field || null;
24685     },
24686
24687     /**
24688      * Add a secondary form to this one, 
24689      * Used to provide tabbed forms. One form is primary, with hidden values 
24690      * which mirror the elements from the other forms.
24691      * 
24692      * @param {Roo.form.Form} form to add.
24693      * 
24694      */
24695     addForm : function(form)
24696     {
24697        
24698         if (this.childForms.indexOf(form) > -1) {
24699             // already added..
24700             return;
24701         }
24702         this.childForms.push(form);
24703         var n = '';
24704         Roo.each(form.allItems, function (fe) {
24705             
24706             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24707             if (this.findField(n)) { // already added..
24708                 return;
24709             }
24710             var add = new Roo.form.Hidden({
24711                 name : n
24712             });
24713             add.render(this.el);
24714             
24715             this.add( add );
24716         }, this);
24717         
24718     },
24719     /**
24720      * Mark fields in this form invalid in bulk.
24721      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24722      * @return {BasicForm} this
24723      */
24724     markInvalid : function(errors){
24725         if(errors instanceof Array){
24726             for(var i = 0, len = errors.length; i < len; i++){
24727                 var fieldError = errors[i];
24728                 var f = this.findField(fieldError.id);
24729                 if(f){
24730                     f.markInvalid(fieldError.msg);
24731                 }
24732             }
24733         }else{
24734             var field, id;
24735             for(id in errors){
24736                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24737                     field.markInvalid(errors[id]);
24738                 }
24739             }
24740         }
24741         Roo.each(this.childForms || [], function (f) {
24742             f.markInvalid(errors);
24743         });
24744         
24745         return this;
24746     },
24747
24748     /**
24749      * Set values for fields in this form in bulk.
24750      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24751      * @return {BasicForm} this
24752      */
24753     setValues : function(values){
24754         if(values instanceof Array){ // array of objects
24755             for(var i = 0, len = values.length; i < len; i++){
24756                 var v = values[i];
24757                 var f = this.findField(v.id);
24758                 if(f){
24759                     f.setValue(v.value);
24760                     if(this.trackResetOnLoad){
24761                         f.originalValue = f.getValue();
24762                     }
24763                 }
24764             }
24765         }else{ // object hash
24766             var field, id;
24767             for(id in values){
24768                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24769                     
24770                     if (field.setFromData && 
24771                         field.valueField && 
24772                         field.displayField &&
24773                         // combos' with local stores can 
24774                         // be queried via setValue()
24775                         // to set their value..
24776                         (field.store && !field.store.isLocal)
24777                         ) {
24778                         // it's a combo
24779                         var sd = { };
24780                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24781                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24782                         field.setFromData(sd);
24783                         
24784                     } else {
24785                         field.setValue(values[id]);
24786                     }
24787                     
24788                     
24789                     if(this.trackResetOnLoad){
24790                         field.originalValue = field.getValue();
24791                     }
24792                 }
24793             }
24794         }
24795         this.resetHasChanged();
24796         
24797         
24798         Roo.each(this.childForms || [], function (f) {
24799             f.setValues(values);
24800             f.resetHasChanged();
24801         });
24802                 
24803         return this;
24804     },
24805
24806     /**
24807      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24808      * they are returned as an array.
24809      * @param {Boolean} asString
24810      * @return {Object}
24811      */
24812     getValues : function(asString){
24813         if (this.childForms) {
24814             // copy values from the child forms
24815             Roo.each(this.childForms, function (f) {
24816                 this.setValues(f.getValues());
24817             }, this);
24818         }
24819         
24820         
24821         
24822         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24823         if(asString === true){
24824             return fs;
24825         }
24826         return Roo.urlDecode(fs);
24827     },
24828     
24829     /**
24830      * Returns the fields in this form as an object with key/value pairs. 
24831      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24832      * @return {Object}
24833      */
24834     getFieldValues : function(with_hidden)
24835     {
24836         if (this.childForms) {
24837             // copy values from the child forms
24838             // should this call getFieldValues - probably not as we do not currently copy
24839             // hidden fields when we generate..
24840             Roo.each(this.childForms, function (f) {
24841                 this.setValues(f.getValues());
24842             }, this);
24843         }
24844         
24845         var ret = {};
24846         this.items.each(function(f){
24847             if (!f.getName()) {
24848                 return;
24849             }
24850             var v = f.getValue();
24851             if (f.inputType =='radio') {
24852                 if (typeof(ret[f.getName()]) == 'undefined') {
24853                     ret[f.getName()] = ''; // empty..
24854                 }
24855                 
24856                 if (!f.el.dom.checked) {
24857                     return;
24858                     
24859                 }
24860                 v = f.el.dom.value;
24861                 
24862             }
24863             
24864             // not sure if this supported any more..
24865             if ((typeof(v) == 'object') && f.getRawValue) {
24866                 v = f.getRawValue() ; // dates..
24867             }
24868             // combo boxes where name != hiddenName...
24869             if (f.name != f.getName()) {
24870                 ret[f.name] = f.getRawValue();
24871             }
24872             ret[f.getName()] = v;
24873         });
24874         
24875         return ret;
24876     },
24877
24878     /**
24879      * Clears all invalid messages in this form.
24880      * @return {BasicForm} this
24881      */
24882     clearInvalid : function(){
24883         this.items.each(function(f){
24884            f.clearInvalid();
24885         });
24886         
24887         Roo.each(this.childForms || [], function (f) {
24888             f.clearInvalid();
24889         });
24890         
24891         
24892         return this;
24893     },
24894
24895     /**
24896      * Resets this form.
24897      * @return {BasicForm} this
24898      */
24899     reset : function(){
24900         this.items.each(function(f){
24901             f.reset();
24902         });
24903         
24904         Roo.each(this.childForms || [], function (f) {
24905             f.reset();
24906         });
24907         this.resetHasChanged();
24908         
24909         return this;
24910     },
24911
24912     /**
24913      * Add Roo.form components to this form.
24914      * @param {Field} field1
24915      * @param {Field} field2 (optional)
24916      * @param {Field} etc (optional)
24917      * @return {BasicForm} this
24918      */
24919     add : function(){
24920         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24921         return this;
24922     },
24923
24924
24925     /**
24926      * Removes a field from the items collection (does NOT remove its markup).
24927      * @param {Field} field
24928      * @return {BasicForm} this
24929      */
24930     remove : function(field){
24931         this.items.remove(field);
24932         return this;
24933     },
24934
24935     /**
24936      * Looks at the fields in this form, checks them for an id attribute,
24937      * and calls applyTo on the existing dom element with that id.
24938      * @return {BasicForm} this
24939      */
24940     render : function(){
24941         this.items.each(function(f){
24942             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24943                 f.applyTo(f.id);
24944             }
24945         });
24946         return this;
24947     },
24948
24949     /**
24950      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24951      * @param {Object} values
24952      * @return {BasicForm} this
24953      */
24954     applyToFields : function(o){
24955         this.items.each(function(f){
24956            Roo.apply(f, o);
24957         });
24958         return this;
24959     },
24960
24961     /**
24962      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24963      * @param {Object} values
24964      * @return {BasicForm} this
24965      */
24966     applyIfToFields : function(o){
24967         this.items.each(function(f){
24968            Roo.applyIf(f, o);
24969         });
24970         return this;
24971     }
24972 });
24973
24974 // back compat
24975 Roo.BasicForm = Roo.form.BasicForm;/*
24976  * Based on:
24977  * Ext JS Library 1.1.1
24978  * Copyright(c) 2006-2007, Ext JS, LLC.
24979  *
24980  * Originally Released Under LGPL - original licence link has changed is not relivant.
24981  *
24982  * Fork - LGPL
24983  * <script type="text/javascript">
24984  */
24985
24986 /**
24987  * @class Roo.form.Form
24988  * @extends Roo.form.BasicForm
24989  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24990  * @constructor
24991  * @param {Object} config Configuration options
24992  */
24993 Roo.form.Form = function(config){
24994     var xitems =  [];
24995     if (config.items) {
24996         xitems = config.items;
24997         delete config.items;
24998     }
24999    
25000     
25001     Roo.form.Form.superclass.constructor.call(this, null, config);
25002     this.url = this.url || this.action;
25003     if(!this.root){
25004         this.root = new Roo.form.Layout(Roo.applyIf({
25005             id: Roo.id()
25006         }, config));
25007     }
25008     this.active = this.root;
25009     /**
25010      * Array of all the buttons that have been added to this form via {@link addButton}
25011      * @type Array
25012      */
25013     this.buttons = [];
25014     this.allItems = [];
25015     this.addEvents({
25016         /**
25017          * @event clientvalidation
25018          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25019          * @param {Form} this
25020          * @param {Boolean} valid true if the form has passed client-side validation
25021          */
25022         clientvalidation: true,
25023         /**
25024          * @event rendered
25025          * Fires when the form is rendered
25026          * @param {Roo.form.Form} form
25027          */
25028         rendered : true
25029     });
25030     
25031     if (this.progressUrl) {
25032             // push a hidden field onto the list of fields..
25033             this.addxtype( {
25034                     xns: Roo.form, 
25035                     xtype : 'Hidden', 
25036                     name : 'UPLOAD_IDENTIFIER' 
25037             });
25038         }
25039         
25040     
25041     Roo.each(xitems, this.addxtype, this);
25042     
25043     
25044     
25045 };
25046
25047 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25048     /**
25049      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25050      */
25051     /**
25052      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25053      */
25054     /**
25055      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25056      */
25057     buttonAlign:'center',
25058
25059     /**
25060      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25061      */
25062     minButtonWidth:75,
25063
25064     /**
25065      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25066      * This property cascades to child containers if not set.
25067      */
25068     labelAlign:'left',
25069
25070     /**
25071      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25072      * fires a looping event with that state. This is required to bind buttons to the valid
25073      * state using the config value formBind:true on the button.
25074      */
25075     monitorValid : false,
25076
25077     /**
25078      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25079      */
25080     monitorPoll : 200,
25081     
25082     /**
25083      * @cfg {String} progressUrl - Url to return progress data 
25084      */
25085     
25086     progressUrl : false,
25087     /**
25088      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25089      * sending a formdata with extra parameters - eg uploaded elements.
25090      */
25091     
25092     formData : false,
25093     
25094     /**
25095      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25096      * fields are added and the column is closed. If no fields are passed the column remains open
25097      * until end() is called.
25098      * @param {Object} config The config to pass to the column
25099      * @param {Field} field1 (optional)
25100      * @param {Field} field2 (optional)
25101      * @param {Field} etc (optional)
25102      * @return Column The column container object
25103      */
25104     column : function(c){
25105         var col = new Roo.form.Column(c);
25106         this.start(col);
25107         if(arguments.length > 1){ // duplicate code required because of Opera
25108             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25109             this.end();
25110         }
25111         return col;
25112     },
25113
25114     /**
25115      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25116      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25117      * until end() is called.
25118      * @param {Object} config The config to pass to the fieldset
25119      * @param {Field} field1 (optional)
25120      * @param {Field} field2 (optional)
25121      * @param {Field} etc (optional)
25122      * @return FieldSet The fieldset container object
25123      */
25124     fieldset : function(c){
25125         var fs = new Roo.form.FieldSet(c);
25126         this.start(fs);
25127         if(arguments.length > 1){ // duplicate code required because of Opera
25128             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25129             this.end();
25130         }
25131         return fs;
25132     },
25133
25134     /**
25135      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25136      * fields are added and the container is closed. If no fields are passed the container remains open
25137      * until end() is called.
25138      * @param {Object} config The config to pass to the Layout
25139      * @param {Field} field1 (optional)
25140      * @param {Field} field2 (optional)
25141      * @param {Field} etc (optional)
25142      * @return Layout The container object
25143      */
25144     container : function(c){
25145         var l = new Roo.form.Layout(c);
25146         this.start(l);
25147         if(arguments.length > 1){ // duplicate code required because of Opera
25148             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25149             this.end();
25150         }
25151         return l;
25152     },
25153
25154     /**
25155      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25156      * @param {Object} container A Roo.form.Layout or subclass of Layout
25157      * @return {Form} this
25158      */
25159     start : function(c){
25160         // cascade label info
25161         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25162         this.active.stack.push(c);
25163         c.ownerCt = this.active;
25164         this.active = c;
25165         return this;
25166     },
25167
25168     /**
25169      * Closes the current open container
25170      * @return {Form} this
25171      */
25172     end : function(){
25173         if(this.active == this.root){
25174             return this;
25175         }
25176         this.active = this.active.ownerCt;
25177         return this;
25178     },
25179
25180     /**
25181      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25182      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25183      * as the label of the field.
25184      * @param {Field} field1
25185      * @param {Field} field2 (optional)
25186      * @param {Field} etc. (optional)
25187      * @return {Form} this
25188      */
25189     add : function(){
25190         this.active.stack.push.apply(this.active.stack, arguments);
25191         this.allItems.push.apply(this.allItems,arguments);
25192         var r = [];
25193         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25194             if(a[i].isFormField){
25195                 r.push(a[i]);
25196             }
25197         }
25198         if(r.length > 0){
25199             Roo.form.Form.superclass.add.apply(this, r);
25200         }
25201         return this;
25202     },
25203     
25204
25205     
25206     
25207     
25208      /**
25209      * Find any element that has been added to a form, using it's ID or name
25210      * This can include framesets, columns etc. along with regular fields..
25211      * @param {String} id - id or name to find.
25212      
25213      * @return {Element} e - or false if nothing found.
25214      */
25215     findbyId : function(id)
25216     {
25217         var ret = false;
25218         if (!id) {
25219             return ret;
25220         }
25221         Roo.each(this.allItems, function(f){
25222             if (f.id == id || f.name == id ){
25223                 ret = f;
25224                 return false;
25225             }
25226         });
25227         return ret;
25228     },
25229
25230     
25231     
25232     /**
25233      * Render this form into the passed container. This should only be called once!
25234      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25235      * @return {Form} this
25236      */
25237     render : function(ct)
25238     {
25239         
25240         
25241         
25242         ct = Roo.get(ct);
25243         var o = this.autoCreate || {
25244             tag: 'form',
25245             method : this.method || 'POST',
25246             id : this.id || Roo.id()
25247         };
25248         this.initEl(ct.createChild(o));
25249
25250         this.root.render(this.el);
25251         
25252        
25253              
25254         this.items.each(function(f){
25255             f.render('x-form-el-'+f.id);
25256         });
25257
25258         if(this.buttons.length > 0){
25259             // tables are required to maintain order and for correct IE layout
25260             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25261                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25262                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25263             }}, null, true);
25264             var tr = tb.getElementsByTagName('tr')[0];
25265             for(var i = 0, len = this.buttons.length; i < len; i++) {
25266                 var b = this.buttons[i];
25267                 var td = document.createElement('td');
25268                 td.className = 'x-form-btn-td';
25269                 b.render(tr.appendChild(td));
25270             }
25271         }
25272         if(this.monitorValid){ // initialize after render
25273             this.startMonitoring();
25274         }
25275         this.fireEvent('rendered', this);
25276         return this;
25277     },
25278
25279     /**
25280      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25281      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25282      * object or a valid Roo.DomHelper element config
25283      * @param {Function} handler The function called when the button is clicked
25284      * @param {Object} scope (optional) The scope of the handler function
25285      * @return {Roo.Button}
25286      */
25287     addButton : function(config, handler, scope){
25288         var bc = {
25289             handler: handler,
25290             scope: scope,
25291             minWidth: this.minButtonWidth,
25292             hideParent:true
25293         };
25294         if(typeof config == "string"){
25295             bc.text = config;
25296         }else{
25297             Roo.apply(bc, config);
25298         }
25299         var btn = new Roo.Button(null, bc);
25300         this.buttons.push(btn);
25301         return btn;
25302     },
25303
25304      /**
25305      * Adds a series of form elements (using the xtype property as the factory method.
25306      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25307      * @param {Object} config 
25308      */
25309     
25310     addxtype : function()
25311     {
25312         var ar = Array.prototype.slice.call(arguments, 0);
25313         var ret = false;
25314         for(var i = 0; i < ar.length; i++) {
25315             if (!ar[i]) {
25316                 continue; // skip -- if this happends something invalid got sent, we 
25317                 // should ignore it, as basically that interface element will not show up
25318                 // and that should be pretty obvious!!
25319             }
25320             
25321             if (Roo.form[ar[i].xtype]) {
25322                 ar[i].form = this;
25323                 var fe = Roo.factory(ar[i], Roo.form);
25324                 if (!ret) {
25325                     ret = fe;
25326                 }
25327                 fe.form = this;
25328                 if (fe.store) {
25329                     fe.store.form = this;
25330                 }
25331                 if (fe.isLayout) {  
25332                          
25333                     this.start(fe);
25334                     this.allItems.push(fe);
25335                     if (fe.items && fe.addxtype) {
25336                         fe.addxtype.apply(fe, fe.items);
25337                         delete fe.items;
25338                     }
25339                      this.end();
25340                     continue;
25341                 }
25342                 
25343                 
25344                  
25345                 this.add(fe);
25346               //  console.log('adding ' + ar[i].xtype);
25347             }
25348             if (ar[i].xtype == 'Button') {  
25349                 //console.log('adding button');
25350                 //console.log(ar[i]);
25351                 this.addButton(ar[i]);
25352                 this.allItems.push(fe);
25353                 continue;
25354             }
25355             
25356             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25357                 alert('end is not supported on xtype any more, use items');
25358             //    this.end();
25359             //    //console.log('adding end');
25360             }
25361             
25362         }
25363         return ret;
25364     },
25365     
25366     /**
25367      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25368      * option "monitorValid"
25369      */
25370     startMonitoring : function(){
25371         if(!this.bound){
25372             this.bound = true;
25373             Roo.TaskMgr.start({
25374                 run : this.bindHandler,
25375                 interval : this.monitorPoll || 200,
25376                 scope: this
25377             });
25378         }
25379     },
25380
25381     /**
25382      * Stops monitoring of the valid state of this form
25383      */
25384     stopMonitoring : function(){
25385         this.bound = false;
25386     },
25387
25388     // private
25389     bindHandler : function(){
25390         if(!this.bound){
25391             return false; // stops binding
25392         }
25393         var valid = true;
25394         this.items.each(function(f){
25395             if(!f.isValid(true)){
25396                 valid = false;
25397                 return false;
25398             }
25399         });
25400         for(var i = 0, len = this.buttons.length; i < len; i++){
25401             var btn = this.buttons[i];
25402             if(btn.formBind === true && btn.disabled === valid){
25403                 btn.setDisabled(!valid);
25404             }
25405         }
25406         this.fireEvent('clientvalidation', this, valid);
25407     }
25408     
25409     
25410     
25411     
25412     
25413     
25414     
25415     
25416 });
25417
25418
25419 // back compat
25420 Roo.Form = Roo.form.Form;
25421 /*
25422  * Based on:
25423  * Ext JS Library 1.1.1
25424  * Copyright(c) 2006-2007, Ext JS, LLC.
25425  *
25426  * Originally Released Under LGPL - original licence link has changed is not relivant.
25427  *
25428  * Fork - LGPL
25429  * <script type="text/javascript">
25430  */
25431
25432 // as we use this in bootstrap.
25433 Roo.namespace('Roo.form');
25434  /**
25435  * @class Roo.form.Action
25436  * Internal Class used to handle form actions
25437  * @constructor
25438  * @param {Roo.form.BasicForm} el The form element or its id
25439  * @param {Object} config Configuration options
25440  */
25441
25442  
25443  
25444 // define the action interface
25445 Roo.form.Action = function(form, options){
25446     this.form = form;
25447     this.options = options || {};
25448 };
25449 /**
25450  * Client Validation Failed
25451  * @const 
25452  */
25453 Roo.form.Action.CLIENT_INVALID = 'client';
25454 /**
25455  * Server Validation Failed
25456  * @const 
25457  */
25458 Roo.form.Action.SERVER_INVALID = 'server';
25459  /**
25460  * Connect to Server Failed
25461  * @const 
25462  */
25463 Roo.form.Action.CONNECT_FAILURE = 'connect';
25464 /**
25465  * Reading Data from Server Failed
25466  * @const 
25467  */
25468 Roo.form.Action.LOAD_FAILURE = 'load';
25469
25470 Roo.form.Action.prototype = {
25471     type : 'default',
25472     failureType : undefined,
25473     response : undefined,
25474     result : undefined,
25475
25476     // interface method
25477     run : function(options){
25478
25479     },
25480
25481     // interface method
25482     success : function(response){
25483
25484     },
25485
25486     // interface method
25487     handleResponse : function(response){
25488
25489     },
25490
25491     // default connection failure
25492     failure : function(response){
25493         
25494         this.response = response;
25495         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25496         this.form.afterAction(this, false);
25497     },
25498
25499     processResponse : function(response){
25500         this.response = response;
25501         if(!response.responseText){
25502             return true;
25503         }
25504         this.result = this.handleResponse(response);
25505         return this.result;
25506     },
25507
25508     // utility functions used internally
25509     getUrl : function(appendParams){
25510         var url = this.options.url || this.form.url || this.form.el.dom.action;
25511         if(appendParams){
25512             var p = this.getParams();
25513             if(p){
25514                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25515             }
25516         }
25517         return url;
25518     },
25519
25520     getMethod : function(){
25521         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25522     },
25523
25524     getParams : function(){
25525         var bp = this.form.baseParams;
25526         var p = this.options.params;
25527         if(p){
25528             if(typeof p == "object"){
25529                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25530             }else if(typeof p == 'string' && bp){
25531                 p += '&' + Roo.urlEncode(bp);
25532             }
25533         }else if(bp){
25534             p = Roo.urlEncode(bp);
25535         }
25536         return p;
25537     },
25538
25539     createCallback : function(){
25540         return {
25541             success: this.success,
25542             failure: this.failure,
25543             scope: this,
25544             timeout: (this.form.timeout*1000),
25545             upload: this.form.fileUpload ? this.success : undefined
25546         };
25547     }
25548 };
25549
25550 Roo.form.Action.Submit = function(form, options){
25551     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25552 };
25553
25554 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25555     type : 'submit',
25556
25557     haveProgress : false,
25558     uploadComplete : false,
25559     
25560     // uploadProgress indicator.
25561     uploadProgress : function()
25562     {
25563         if (!this.form.progressUrl) {
25564             return;
25565         }
25566         
25567         if (!this.haveProgress) {
25568             Roo.MessageBox.progress("Uploading", "Uploading");
25569         }
25570         if (this.uploadComplete) {
25571            Roo.MessageBox.hide();
25572            return;
25573         }
25574         
25575         this.haveProgress = true;
25576    
25577         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25578         
25579         var c = new Roo.data.Connection();
25580         c.request({
25581             url : this.form.progressUrl,
25582             params: {
25583                 id : uid
25584             },
25585             method: 'GET',
25586             success : function(req){
25587                //console.log(data);
25588                 var rdata = false;
25589                 var edata;
25590                 try  {
25591                    rdata = Roo.decode(req.responseText)
25592                 } catch (e) {
25593                     Roo.log("Invalid data from server..");
25594                     Roo.log(edata);
25595                     return;
25596                 }
25597                 if (!rdata || !rdata.success) {
25598                     Roo.log(rdata);
25599                     Roo.MessageBox.alert(Roo.encode(rdata));
25600                     return;
25601                 }
25602                 var data = rdata.data;
25603                 
25604                 if (this.uploadComplete) {
25605                    Roo.MessageBox.hide();
25606                    return;
25607                 }
25608                    
25609                 if (data){
25610                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25611                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25612                     );
25613                 }
25614                 this.uploadProgress.defer(2000,this);
25615             },
25616        
25617             failure: function(data) {
25618                 Roo.log('progress url failed ');
25619                 Roo.log(data);
25620             },
25621             scope : this
25622         });
25623            
25624     },
25625     
25626     
25627     run : function()
25628     {
25629         // run get Values on the form, so it syncs any secondary forms.
25630         this.form.getValues();
25631         
25632         var o = this.options;
25633         var method = this.getMethod();
25634         var isPost = method == 'POST';
25635         if(o.clientValidation === false || this.form.isValid()){
25636             
25637             if (this.form.progressUrl) {
25638                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25639                     (new Date() * 1) + '' + Math.random());
25640                     
25641             } 
25642             
25643             
25644             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25645                 form:this.form.el.dom,
25646                 url:this.getUrl(!isPost),
25647                 method: method,
25648                 params:isPost ? this.getParams() : null,
25649                 isUpload: this.form.fileUpload,
25650                 formData : this.form.formData
25651             }));
25652             
25653             this.uploadProgress();
25654
25655         }else if (o.clientValidation !== false){ // client validation failed
25656             this.failureType = Roo.form.Action.CLIENT_INVALID;
25657             this.form.afterAction(this, false);
25658         }
25659     },
25660
25661     success : function(response)
25662     {
25663         this.uploadComplete= true;
25664         if (this.haveProgress) {
25665             Roo.MessageBox.hide();
25666         }
25667         
25668         
25669         var result = this.processResponse(response);
25670         if(result === true || result.success){
25671             this.form.afterAction(this, true);
25672             return;
25673         }
25674         if(result.errors){
25675             this.form.markInvalid(result.errors);
25676             this.failureType = Roo.form.Action.SERVER_INVALID;
25677         }
25678         this.form.afterAction(this, false);
25679     },
25680     failure : function(response)
25681     {
25682         this.uploadComplete= true;
25683         if (this.haveProgress) {
25684             Roo.MessageBox.hide();
25685         }
25686         
25687         this.response = response;
25688         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25689         this.form.afterAction(this, false);
25690     },
25691     
25692     handleResponse : function(response){
25693         if(this.form.errorReader){
25694             var rs = this.form.errorReader.read(response);
25695             var errors = [];
25696             if(rs.records){
25697                 for(var i = 0, len = rs.records.length; i < len; i++) {
25698                     var r = rs.records[i];
25699                     errors[i] = r.data;
25700                 }
25701             }
25702             if(errors.length < 1){
25703                 errors = null;
25704             }
25705             return {
25706                 success : rs.success,
25707                 errors : errors
25708             };
25709         }
25710         var ret = false;
25711         try {
25712             ret = Roo.decode(response.responseText);
25713         } catch (e) {
25714             ret = {
25715                 success: false,
25716                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25717                 errors : []
25718             };
25719         }
25720         return ret;
25721         
25722     }
25723 });
25724
25725
25726 Roo.form.Action.Load = function(form, options){
25727     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25728     this.reader = this.form.reader;
25729 };
25730
25731 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25732     type : 'load',
25733
25734     run : function(){
25735         
25736         Roo.Ajax.request(Roo.apply(
25737                 this.createCallback(), {
25738                     method:this.getMethod(),
25739                     url:this.getUrl(false),
25740                     params:this.getParams()
25741         }));
25742     },
25743
25744     success : function(response){
25745         
25746         var result = this.processResponse(response);
25747         if(result === true || !result.success || !result.data){
25748             this.failureType = Roo.form.Action.LOAD_FAILURE;
25749             this.form.afterAction(this, false);
25750             return;
25751         }
25752         this.form.clearInvalid();
25753         this.form.setValues(result.data);
25754         this.form.afterAction(this, true);
25755     },
25756
25757     handleResponse : function(response){
25758         if(this.form.reader){
25759             var rs = this.form.reader.read(response);
25760             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25761             return {
25762                 success : rs.success,
25763                 data : data
25764             };
25765         }
25766         return Roo.decode(response.responseText);
25767     }
25768 });
25769
25770 Roo.form.Action.ACTION_TYPES = {
25771     'load' : Roo.form.Action.Load,
25772     'submit' : Roo.form.Action.Submit
25773 };/*
25774  * Based on:
25775  * Ext JS Library 1.1.1
25776  * Copyright(c) 2006-2007, Ext JS, LLC.
25777  *
25778  * Originally Released Under LGPL - original licence link has changed is not relivant.
25779  *
25780  * Fork - LGPL
25781  * <script type="text/javascript">
25782  */
25783  
25784 /**
25785  * @class Roo.form.Layout
25786  * @extends Roo.Component
25787  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25788  * @constructor
25789  * @param {Object} config Configuration options
25790  */
25791 Roo.form.Layout = function(config){
25792     var xitems = [];
25793     if (config.items) {
25794         xitems = config.items;
25795         delete config.items;
25796     }
25797     Roo.form.Layout.superclass.constructor.call(this, config);
25798     this.stack = [];
25799     Roo.each(xitems, this.addxtype, this);
25800      
25801 };
25802
25803 Roo.extend(Roo.form.Layout, Roo.Component, {
25804     /**
25805      * @cfg {String/Object} autoCreate
25806      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25807      */
25808     /**
25809      * @cfg {String/Object/Function} style
25810      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25811      * a function which returns such a specification.
25812      */
25813     /**
25814      * @cfg {String} labelAlign
25815      * Valid values are "left," "top" and "right" (defaults to "left")
25816      */
25817     /**
25818      * @cfg {Number} labelWidth
25819      * Fixed width in pixels of all field labels (defaults to undefined)
25820      */
25821     /**
25822      * @cfg {Boolean} clear
25823      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25824      */
25825     clear : true,
25826     /**
25827      * @cfg {String} labelSeparator
25828      * The separator to use after field labels (defaults to ':')
25829      */
25830     labelSeparator : ':',
25831     /**
25832      * @cfg {Boolean} hideLabels
25833      * True to suppress the display of field labels in this layout (defaults to false)
25834      */
25835     hideLabels : false,
25836
25837     // private
25838     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25839     
25840     isLayout : true,
25841     
25842     // private
25843     onRender : function(ct, position){
25844         if(this.el){ // from markup
25845             this.el = Roo.get(this.el);
25846         }else {  // generate
25847             var cfg = this.getAutoCreate();
25848             this.el = ct.createChild(cfg, position);
25849         }
25850         if(this.style){
25851             this.el.applyStyles(this.style);
25852         }
25853         if(this.labelAlign){
25854             this.el.addClass('x-form-label-'+this.labelAlign);
25855         }
25856         if(this.hideLabels){
25857             this.labelStyle = "display:none";
25858             this.elementStyle = "padding-left:0;";
25859         }else{
25860             if(typeof this.labelWidth == 'number'){
25861                 this.labelStyle = "width:"+this.labelWidth+"px;";
25862                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25863             }
25864             if(this.labelAlign == 'top'){
25865                 this.labelStyle = "width:auto;";
25866                 this.elementStyle = "padding-left:0;";
25867             }
25868         }
25869         var stack = this.stack;
25870         var slen = stack.length;
25871         if(slen > 0){
25872             if(!this.fieldTpl){
25873                 var t = new Roo.Template(
25874                     '<div class="x-form-item {5}">',
25875                         '<label for="{0}" style="{2}">{1}{4}</label>',
25876                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25877                         '</div>',
25878                     '</div><div class="x-form-clear-left"></div>'
25879                 );
25880                 t.disableFormats = true;
25881                 t.compile();
25882                 Roo.form.Layout.prototype.fieldTpl = t;
25883             }
25884             for(var i = 0; i < slen; i++) {
25885                 if(stack[i].isFormField){
25886                     this.renderField(stack[i]);
25887                 }else{
25888                     this.renderComponent(stack[i]);
25889                 }
25890             }
25891         }
25892         if(this.clear){
25893             this.el.createChild({cls:'x-form-clear'});
25894         }
25895     },
25896
25897     // private
25898     renderField : function(f){
25899         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25900                f.id, //0
25901                f.fieldLabel, //1
25902                f.labelStyle||this.labelStyle||'', //2
25903                this.elementStyle||'', //3
25904                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25905                f.itemCls||this.itemCls||''  //5
25906        ], true).getPrevSibling());
25907     },
25908
25909     // private
25910     renderComponent : function(c){
25911         c.render(c.isLayout ? this.el : this.el.createChild());    
25912     },
25913     /**
25914      * Adds a object form elements (using the xtype property as the factory method.)
25915      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25916      * @param {Object} config 
25917      */
25918     addxtype : function(o)
25919     {
25920         // create the lement.
25921         o.form = this.form;
25922         var fe = Roo.factory(o, Roo.form);
25923         this.form.allItems.push(fe);
25924         this.stack.push(fe);
25925         
25926         if (fe.isFormField) {
25927             this.form.items.add(fe);
25928         }
25929          
25930         return fe;
25931     }
25932 });
25933
25934 /**
25935  * @class Roo.form.Column
25936  * @extends Roo.form.Layout
25937  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25938  * @constructor
25939  * @param {Object} config Configuration options
25940  */
25941 Roo.form.Column = function(config){
25942     Roo.form.Column.superclass.constructor.call(this, config);
25943 };
25944
25945 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25946     /**
25947      * @cfg {Number/String} width
25948      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25949      */
25950     /**
25951      * @cfg {String/Object} autoCreate
25952      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25953      */
25954
25955     // private
25956     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25957
25958     // private
25959     onRender : function(ct, position){
25960         Roo.form.Column.superclass.onRender.call(this, ct, position);
25961         if(this.width){
25962             this.el.setWidth(this.width);
25963         }
25964     }
25965 });
25966
25967
25968 /**
25969  * @class Roo.form.Row
25970  * @extends Roo.form.Layout
25971  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25972  * @constructor
25973  * @param {Object} config Configuration options
25974  */
25975
25976  
25977 Roo.form.Row = function(config){
25978     Roo.form.Row.superclass.constructor.call(this, config);
25979 };
25980  
25981 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25982       /**
25983      * @cfg {Number/String} width
25984      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25985      */
25986     /**
25987      * @cfg {Number/String} height
25988      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25989      */
25990     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25991     
25992     padWidth : 20,
25993     // private
25994     onRender : function(ct, position){
25995         //console.log('row render');
25996         if(!this.rowTpl){
25997             var t = new Roo.Template(
25998                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25999                     '<label for="{0}" style="{2}">{1}{4}</label>',
26000                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26001                     '</div>',
26002                 '</div>'
26003             );
26004             t.disableFormats = true;
26005             t.compile();
26006             Roo.form.Layout.prototype.rowTpl = t;
26007         }
26008         this.fieldTpl = this.rowTpl;
26009         
26010         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26011         var labelWidth = 100;
26012         
26013         if ((this.labelAlign != 'top')) {
26014             if (typeof this.labelWidth == 'number') {
26015                 labelWidth = this.labelWidth
26016             }
26017             this.padWidth =  20 + labelWidth;
26018             
26019         }
26020         
26021         Roo.form.Column.superclass.onRender.call(this, ct, position);
26022         if(this.width){
26023             this.el.setWidth(this.width);
26024         }
26025         if(this.height){
26026             this.el.setHeight(this.height);
26027         }
26028     },
26029     
26030     // private
26031     renderField : function(f){
26032         f.fieldEl = this.fieldTpl.append(this.el, [
26033                f.id, f.fieldLabel,
26034                f.labelStyle||this.labelStyle||'',
26035                this.elementStyle||'',
26036                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26037                f.itemCls||this.itemCls||'',
26038                f.width ? f.width + this.padWidth : 160 + this.padWidth
26039        ],true);
26040     }
26041 });
26042  
26043
26044 /**
26045  * @class Roo.form.FieldSet
26046  * @extends Roo.form.Layout
26047  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26048  * @constructor
26049  * @param {Object} config Configuration options
26050  */
26051 Roo.form.FieldSet = function(config){
26052     Roo.form.FieldSet.superclass.constructor.call(this, config);
26053 };
26054
26055 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26056     /**
26057      * @cfg {String} legend
26058      * The text to display as the legend for the FieldSet (defaults to '')
26059      */
26060     /**
26061      * @cfg {String/Object} autoCreate
26062      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26063      */
26064
26065     // private
26066     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26067
26068     // private
26069     onRender : function(ct, position){
26070         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26071         if(this.legend){
26072             this.setLegend(this.legend);
26073         }
26074     },
26075
26076     // private
26077     setLegend : function(text){
26078         if(this.rendered){
26079             this.el.child('legend').update(text);
26080         }
26081     }
26082 });/*
26083  * Based on:
26084  * Ext JS Library 1.1.1
26085  * Copyright(c) 2006-2007, Ext JS, LLC.
26086  *
26087  * Originally Released Under LGPL - original licence link has changed is not relivant.
26088  *
26089  * Fork - LGPL
26090  * <script type="text/javascript">
26091  */
26092 /**
26093  * @class Roo.form.VTypes
26094  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26095  * @singleton
26096  */
26097 Roo.form.VTypes = function(){
26098     // closure these in so they are only created once.
26099     var alpha = /^[a-zA-Z_]+$/;
26100     var alphanum = /^[a-zA-Z0-9_]+$/;
26101     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26102     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26103
26104     // All these messages and functions are configurable
26105     return {
26106         /**
26107          * The function used to validate email addresses
26108          * @param {String} value The email address
26109          */
26110         'email' : function(v){
26111             return email.test(v);
26112         },
26113         /**
26114          * The error text to display when the email validation function returns false
26115          * @type String
26116          */
26117         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26118         /**
26119          * The keystroke filter mask to be applied on email input
26120          * @type RegExp
26121          */
26122         'emailMask' : /[a-z0-9_\.\-@]/i,
26123
26124         /**
26125          * The function used to validate URLs
26126          * @param {String} value The URL
26127          */
26128         'url' : function(v){
26129             return url.test(v);
26130         },
26131         /**
26132          * The error text to display when the url validation function returns false
26133          * @type String
26134          */
26135         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26136         
26137         /**
26138          * The function used to validate alpha values
26139          * @param {String} value The value
26140          */
26141         'alpha' : function(v){
26142             return alpha.test(v);
26143         },
26144         /**
26145          * The error text to display when the alpha validation function returns false
26146          * @type String
26147          */
26148         'alphaText' : 'This field should only contain letters and _',
26149         /**
26150          * The keystroke filter mask to be applied on alpha input
26151          * @type RegExp
26152          */
26153         'alphaMask' : /[a-z_]/i,
26154
26155         /**
26156          * The function used to validate alphanumeric values
26157          * @param {String} value The value
26158          */
26159         'alphanum' : function(v){
26160             return alphanum.test(v);
26161         },
26162         /**
26163          * The error text to display when the alphanumeric validation function returns false
26164          * @type String
26165          */
26166         'alphanumText' : 'This field should only contain letters, numbers and _',
26167         /**
26168          * The keystroke filter mask to be applied on alphanumeric input
26169          * @type RegExp
26170          */
26171         'alphanumMask' : /[a-z0-9_]/i
26172     };
26173 }();//<script type="text/javascript">
26174
26175 /**
26176  * @class Roo.form.FCKeditor
26177  * @extends Roo.form.TextArea
26178  * Wrapper around the FCKEditor http://www.fckeditor.net
26179  * @constructor
26180  * Creates a new FCKeditor
26181  * @param {Object} config Configuration options
26182  */
26183 Roo.form.FCKeditor = function(config){
26184     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26185     this.addEvents({
26186          /**
26187          * @event editorinit
26188          * Fired when the editor is initialized - you can add extra handlers here..
26189          * @param {FCKeditor} this
26190          * @param {Object} the FCK object.
26191          */
26192         editorinit : true
26193     });
26194     
26195     
26196 };
26197 Roo.form.FCKeditor.editors = { };
26198 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26199 {
26200     //defaultAutoCreate : {
26201     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26202     //},
26203     // private
26204     /**
26205      * @cfg {Object} fck options - see fck manual for details.
26206      */
26207     fckconfig : false,
26208     
26209     /**
26210      * @cfg {Object} fck toolbar set (Basic or Default)
26211      */
26212     toolbarSet : 'Basic',
26213     /**
26214      * @cfg {Object} fck BasePath
26215      */ 
26216     basePath : '/fckeditor/',
26217     
26218     
26219     frame : false,
26220     
26221     value : '',
26222     
26223    
26224     onRender : function(ct, position)
26225     {
26226         if(!this.el){
26227             this.defaultAutoCreate = {
26228                 tag: "textarea",
26229                 style:"width:300px;height:60px;",
26230                 autocomplete: "new-password"
26231             };
26232         }
26233         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26234         /*
26235         if(this.grow){
26236             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26237             if(this.preventScrollbars){
26238                 this.el.setStyle("overflow", "hidden");
26239             }
26240             this.el.setHeight(this.growMin);
26241         }
26242         */
26243         //console.log('onrender' + this.getId() );
26244         Roo.form.FCKeditor.editors[this.getId()] = this;
26245          
26246
26247         this.replaceTextarea() ;
26248         
26249     },
26250     
26251     getEditor : function() {
26252         return this.fckEditor;
26253     },
26254     /**
26255      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26256      * @param {Mixed} value The value to set
26257      */
26258     
26259     
26260     setValue : function(value)
26261     {
26262         //console.log('setValue: ' + value);
26263         
26264         if(typeof(value) == 'undefined') { // not sure why this is happending...
26265             return;
26266         }
26267         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26268         
26269         //if(!this.el || !this.getEditor()) {
26270         //    this.value = value;
26271             //this.setValue.defer(100,this,[value]);    
26272         //    return;
26273         //} 
26274         
26275         if(!this.getEditor()) {
26276             return;
26277         }
26278         
26279         this.getEditor().SetData(value);
26280         
26281         //
26282
26283     },
26284
26285     /**
26286      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26287      * @return {Mixed} value The field value
26288      */
26289     getValue : function()
26290     {
26291         
26292         if (this.frame && this.frame.dom.style.display == 'none') {
26293             return Roo.form.FCKeditor.superclass.getValue.call(this);
26294         }
26295         
26296         if(!this.el || !this.getEditor()) {
26297            
26298            // this.getValue.defer(100,this); 
26299             return this.value;
26300         }
26301        
26302         
26303         var value=this.getEditor().GetData();
26304         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26305         return Roo.form.FCKeditor.superclass.getValue.call(this);
26306         
26307
26308     },
26309
26310     /**
26311      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26312      * @return {Mixed} value The field value
26313      */
26314     getRawValue : function()
26315     {
26316         if (this.frame && this.frame.dom.style.display == 'none') {
26317             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26318         }
26319         
26320         if(!this.el || !this.getEditor()) {
26321             //this.getRawValue.defer(100,this); 
26322             return this.value;
26323             return;
26324         }
26325         
26326         
26327         
26328         var value=this.getEditor().GetData();
26329         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26330         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26331          
26332     },
26333     
26334     setSize : function(w,h) {
26335         
26336         
26337         
26338         //if (this.frame && this.frame.dom.style.display == 'none') {
26339         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26340         //    return;
26341         //}
26342         //if(!this.el || !this.getEditor()) {
26343         //    this.setSize.defer(100,this, [w,h]); 
26344         //    return;
26345         //}
26346         
26347         
26348         
26349         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26350         
26351         this.frame.dom.setAttribute('width', w);
26352         this.frame.dom.setAttribute('height', h);
26353         this.frame.setSize(w,h);
26354         
26355     },
26356     
26357     toggleSourceEdit : function(value) {
26358         
26359       
26360          
26361         this.el.dom.style.display = value ? '' : 'none';
26362         this.frame.dom.style.display = value ?  'none' : '';
26363         
26364     },
26365     
26366     
26367     focus: function(tag)
26368     {
26369         if (this.frame.dom.style.display == 'none') {
26370             return Roo.form.FCKeditor.superclass.focus.call(this);
26371         }
26372         if(!this.el || !this.getEditor()) {
26373             this.focus.defer(100,this, [tag]); 
26374             return;
26375         }
26376         
26377         
26378         
26379         
26380         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26381         this.getEditor().Focus();
26382         if (tgs.length) {
26383             if (!this.getEditor().Selection.GetSelection()) {
26384                 this.focus.defer(100,this, [tag]); 
26385                 return;
26386             }
26387             
26388             
26389             var r = this.getEditor().EditorDocument.createRange();
26390             r.setStart(tgs[0],0);
26391             r.setEnd(tgs[0],0);
26392             this.getEditor().Selection.GetSelection().removeAllRanges();
26393             this.getEditor().Selection.GetSelection().addRange(r);
26394             this.getEditor().Focus();
26395         }
26396         
26397     },
26398     
26399     
26400     
26401     replaceTextarea : function()
26402     {
26403         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26404             return ;
26405         }
26406         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26407         //{
26408             // We must check the elements firstly using the Id and then the name.
26409         var oTextarea = document.getElementById( this.getId() );
26410         
26411         var colElementsByName = document.getElementsByName( this.getId() ) ;
26412          
26413         oTextarea.style.display = 'none' ;
26414
26415         if ( oTextarea.tabIndex ) {            
26416             this.TabIndex = oTextarea.tabIndex ;
26417         }
26418         
26419         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26420         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26421         this.frame = Roo.get(this.getId() + '___Frame')
26422     },
26423     
26424     _getConfigHtml : function()
26425     {
26426         var sConfig = '' ;
26427
26428         for ( var o in this.fckconfig ) {
26429             sConfig += sConfig.length > 0  ? '&amp;' : '';
26430             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26431         }
26432
26433         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26434     },
26435     
26436     
26437     _getIFrameHtml : function()
26438     {
26439         var sFile = 'fckeditor.html' ;
26440         /* no idea what this is about..
26441         try
26442         {
26443             if ( (/fcksource=true/i).test( window.top.location.search ) )
26444                 sFile = 'fckeditor.original.html' ;
26445         }
26446         catch (e) { 
26447         */
26448
26449         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26450         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26451         
26452         
26453         var html = '<iframe id="' + this.getId() +
26454             '___Frame" src="' + sLink +
26455             '" width="' + this.width +
26456             '" height="' + this.height + '"' +
26457             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26458             ' frameborder="0" scrolling="no"></iframe>' ;
26459
26460         return html ;
26461     },
26462     
26463     _insertHtmlBefore : function( html, element )
26464     {
26465         if ( element.insertAdjacentHTML )       {
26466             // IE
26467             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26468         } else { // Gecko
26469             var oRange = document.createRange() ;
26470             oRange.setStartBefore( element ) ;
26471             var oFragment = oRange.createContextualFragment( html );
26472             element.parentNode.insertBefore( oFragment, element ) ;
26473         }
26474     }
26475     
26476     
26477   
26478     
26479     
26480     
26481     
26482
26483 });
26484
26485 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26486
26487 function FCKeditor_OnComplete(editorInstance){
26488     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26489     f.fckEditor = editorInstance;
26490     //console.log("loaded");
26491     f.fireEvent('editorinit', f, editorInstance);
26492
26493   
26494
26495  
26496
26497
26498
26499
26500
26501
26502
26503
26504
26505
26506
26507
26508
26509
26510
26511 //<script type="text/javascript">
26512 /**
26513  * @class Roo.form.GridField
26514  * @extends Roo.form.Field
26515  * Embed a grid (or editable grid into a form)
26516  * STATUS ALPHA
26517  * 
26518  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26519  * it needs 
26520  * xgrid.store = Roo.data.Store
26521  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26522  * xgrid.store.reader = Roo.data.JsonReader 
26523  * 
26524  * 
26525  * @constructor
26526  * Creates a new GridField
26527  * @param {Object} config Configuration options
26528  */
26529 Roo.form.GridField = function(config){
26530     Roo.form.GridField.superclass.constructor.call(this, config);
26531      
26532 };
26533
26534 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26535     /**
26536      * @cfg {Number} width  - used to restrict width of grid..
26537      */
26538     width : 100,
26539     /**
26540      * @cfg {Number} height - used to restrict height of grid..
26541      */
26542     height : 50,
26543      /**
26544      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26545          * 
26546          *}
26547      */
26548     xgrid : false, 
26549     /**
26550      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26551      * {tag: "input", type: "checkbox", autocomplete: "off"})
26552      */
26553    // defaultAutoCreate : { tag: 'div' },
26554     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26555     /**
26556      * @cfg {String} addTitle Text to include for adding a title.
26557      */
26558     addTitle : false,
26559     //
26560     onResize : function(){
26561         Roo.form.Field.superclass.onResize.apply(this, arguments);
26562     },
26563
26564     initEvents : function(){
26565         // Roo.form.Checkbox.superclass.initEvents.call(this);
26566         // has no events...
26567        
26568     },
26569
26570
26571     getResizeEl : function(){
26572         return this.wrap;
26573     },
26574
26575     getPositionEl : function(){
26576         return this.wrap;
26577     },
26578
26579     // private
26580     onRender : function(ct, position){
26581         
26582         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26583         var style = this.style;
26584         delete this.style;
26585         
26586         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26587         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26588         this.viewEl = this.wrap.createChild({ tag: 'div' });
26589         if (style) {
26590             this.viewEl.applyStyles(style);
26591         }
26592         if (this.width) {
26593             this.viewEl.setWidth(this.width);
26594         }
26595         if (this.height) {
26596             this.viewEl.setHeight(this.height);
26597         }
26598         //if(this.inputValue !== undefined){
26599         //this.setValue(this.value);
26600         
26601         
26602         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26603         
26604         
26605         this.grid.render();
26606         this.grid.getDataSource().on('remove', this.refreshValue, this);
26607         this.grid.getDataSource().on('update', this.refreshValue, this);
26608         this.grid.on('afteredit', this.refreshValue, this);
26609  
26610     },
26611      
26612     
26613     /**
26614      * Sets the value of the item. 
26615      * @param {String} either an object  or a string..
26616      */
26617     setValue : function(v){
26618         //this.value = v;
26619         v = v || []; // empty set..
26620         // this does not seem smart - it really only affects memoryproxy grids..
26621         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26622             var ds = this.grid.getDataSource();
26623             // assumes a json reader..
26624             var data = {}
26625             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26626             ds.loadData( data);
26627         }
26628         // clear selection so it does not get stale.
26629         if (this.grid.sm) { 
26630             this.grid.sm.clearSelections();
26631         }
26632         
26633         Roo.form.GridField.superclass.setValue.call(this, v);
26634         this.refreshValue();
26635         // should load data in the grid really....
26636     },
26637     
26638     // private
26639     refreshValue: function() {
26640          var val = [];
26641         this.grid.getDataSource().each(function(r) {
26642             val.push(r.data);
26643         });
26644         this.el.dom.value = Roo.encode(val);
26645     }
26646     
26647      
26648     
26649     
26650 });/*
26651  * Based on:
26652  * Ext JS Library 1.1.1
26653  * Copyright(c) 2006-2007, Ext JS, LLC.
26654  *
26655  * Originally Released Under LGPL - original licence link has changed is not relivant.
26656  *
26657  * Fork - LGPL
26658  * <script type="text/javascript">
26659  */
26660 /**
26661  * @class Roo.form.DisplayField
26662  * @extends Roo.form.Field
26663  * A generic Field to display non-editable data.
26664  * @cfg {Boolean} closable (true|false) default false
26665  * @constructor
26666  * Creates a new Display Field item.
26667  * @param {Object} config Configuration options
26668  */
26669 Roo.form.DisplayField = function(config){
26670     Roo.form.DisplayField.superclass.constructor.call(this, config);
26671     
26672     this.addEvents({
26673         /**
26674          * @event close
26675          * Fires after the click the close btn
26676              * @param {Roo.form.DisplayField} this
26677              */
26678         close : true
26679     });
26680 };
26681
26682 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26683     inputType:      'hidden',
26684     allowBlank:     true,
26685     readOnly:         true,
26686     
26687  
26688     /**
26689      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26690      */
26691     focusClass : undefined,
26692     /**
26693      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26694      */
26695     fieldClass: 'x-form-field',
26696     
26697      /**
26698      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26699      */
26700     valueRenderer: undefined,
26701     
26702     width: 100,
26703     /**
26704      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26705      * {tag: "input", type: "checkbox", autocomplete: "off"})
26706      */
26707      
26708  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26709  
26710     closable : false,
26711     
26712     onResize : function(){
26713         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26714         
26715     },
26716
26717     initEvents : function(){
26718         // Roo.form.Checkbox.superclass.initEvents.call(this);
26719         // has no events...
26720         
26721         if(this.closable){
26722             this.closeEl.on('click', this.onClose, this);
26723         }
26724        
26725     },
26726
26727
26728     getResizeEl : function(){
26729         return this.wrap;
26730     },
26731
26732     getPositionEl : function(){
26733         return this.wrap;
26734     },
26735
26736     // private
26737     onRender : function(ct, position){
26738         
26739         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26740         //if(this.inputValue !== undefined){
26741         this.wrap = this.el.wrap();
26742         
26743         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26744         
26745         if(this.closable){
26746             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26747         }
26748         
26749         if (this.bodyStyle) {
26750             this.viewEl.applyStyles(this.bodyStyle);
26751         }
26752         //this.viewEl.setStyle('padding', '2px');
26753         
26754         this.setValue(this.value);
26755         
26756     },
26757 /*
26758     // private
26759     initValue : Roo.emptyFn,
26760
26761   */
26762
26763         // private
26764     onClick : function(){
26765         
26766     },
26767
26768     /**
26769      * Sets the checked state of the checkbox.
26770      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26771      */
26772     setValue : function(v){
26773         this.value = v;
26774         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26775         // this might be called before we have a dom element..
26776         if (!this.viewEl) {
26777             return;
26778         }
26779         this.viewEl.dom.innerHTML = html;
26780         Roo.form.DisplayField.superclass.setValue.call(this, v);
26781
26782     },
26783     
26784     onClose : function(e)
26785     {
26786         e.preventDefault();
26787         
26788         this.fireEvent('close', this);
26789     }
26790 });/*
26791  * 
26792  * Licence- LGPL
26793  * 
26794  */
26795
26796 /**
26797  * @class Roo.form.DayPicker
26798  * @extends Roo.form.Field
26799  * A Day picker show [M] [T] [W] ....
26800  * @constructor
26801  * Creates a new Day Picker
26802  * @param {Object} config Configuration options
26803  */
26804 Roo.form.DayPicker= function(config){
26805     Roo.form.DayPicker.superclass.constructor.call(this, config);
26806      
26807 };
26808
26809 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26810     /**
26811      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26812      */
26813     focusClass : undefined,
26814     /**
26815      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26816      */
26817     fieldClass: "x-form-field",
26818    
26819     /**
26820      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26821      * {tag: "input", type: "checkbox", autocomplete: "off"})
26822      */
26823     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26824     
26825    
26826     actionMode : 'viewEl', 
26827     //
26828     // private
26829  
26830     inputType : 'hidden',
26831     
26832      
26833     inputElement: false, // real input element?
26834     basedOn: false, // ????
26835     
26836     isFormField: true, // not sure where this is needed!!!!
26837
26838     onResize : function(){
26839         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26840         if(!this.boxLabel){
26841             this.el.alignTo(this.wrap, 'c-c');
26842         }
26843     },
26844
26845     initEvents : function(){
26846         Roo.form.Checkbox.superclass.initEvents.call(this);
26847         this.el.on("click", this.onClick,  this);
26848         this.el.on("change", this.onClick,  this);
26849     },
26850
26851
26852     getResizeEl : function(){
26853         return this.wrap;
26854     },
26855
26856     getPositionEl : function(){
26857         return this.wrap;
26858     },
26859
26860     
26861     // private
26862     onRender : function(ct, position){
26863         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26864        
26865         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26866         
26867         var r1 = '<table><tr>';
26868         var r2 = '<tr class="x-form-daypick-icons">';
26869         for (var i=0; i < 7; i++) {
26870             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26871             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26872         }
26873         
26874         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26875         viewEl.select('img').on('click', this.onClick, this);
26876         this.viewEl = viewEl;   
26877         
26878         
26879         // this will not work on Chrome!!!
26880         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26881         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26882         
26883         
26884           
26885
26886     },
26887
26888     // private
26889     initValue : Roo.emptyFn,
26890
26891     /**
26892      * Returns the checked state of the checkbox.
26893      * @return {Boolean} True if checked, else false
26894      */
26895     getValue : function(){
26896         return this.el.dom.value;
26897         
26898     },
26899
26900         // private
26901     onClick : function(e){ 
26902         //this.setChecked(!this.checked);
26903         Roo.get(e.target).toggleClass('x-menu-item-checked');
26904         this.refreshValue();
26905         //if(this.el.dom.checked != this.checked){
26906         //    this.setValue(this.el.dom.checked);
26907        // }
26908     },
26909     
26910     // private
26911     refreshValue : function()
26912     {
26913         var val = '';
26914         this.viewEl.select('img',true).each(function(e,i,n)  {
26915             val += e.is(".x-menu-item-checked") ? String(n) : '';
26916         });
26917         this.setValue(val, true);
26918     },
26919
26920     /**
26921      * Sets the checked state of the checkbox.
26922      * On is always based on a string comparison between inputValue and the param.
26923      * @param {Boolean/String} value - the value to set 
26924      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26925      */
26926     setValue : function(v,suppressEvent){
26927         if (!this.el.dom) {
26928             return;
26929         }
26930         var old = this.el.dom.value ;
26931         this.el.dom.value = v;
26932         if (suppressEvent) {
26933             return ;
26934         }
26935          
26936         // update display..
26937         this.viewEl.select('img',true).each(function(e,i,n)  {
26938             
26939             var on = e.is(".x-menu-item-checked");
26940             var newv = v.indexOf(String(n)) > -1;
26941             if (on != newv) {
26942                 e.toggleClass('x-menu-item-checked');
26943             }
26944             
26945         });
26946         
26947         
26948         this.fireEvent('change', this, v, old);
26949         
26950         
26951     },
26952    
26953     // handle setting of hidden value by some other method!!?!?
26954     setFromHidden: function()
26955     {
26956         if(!this.el){
26957             return;
26958         }
26959         //console.log("SET FROM HIDDEN");
26960         //alert('setFrom hidden');
26961         this.setValue(this.el.dom.value);
26962     },
26963     
26964     onDestroy : function()
26965     {
26966         if(this.viewEl){
26967             Roo.get(this.viewEl).remove();
26968         }
26969          
26970         Roo.form.DayPicker.superclass.onDestroy.call(this);
26971     }
26972
26973 });/*
26974  * RooJS Library 1.1.1
26975  * Copyright(c) 2008-2011  Alan Knowles
26976  *
26977  * License - LGPL
26978  */
26979  
26980
26981 /**
26982  * @class Roo.form.ComboCheck
26983  * @extends Roo.form.ComboBox
26984  * A combobox for multiple select items.
26985  *
26986  * FIXME - could do with a reset button..
26987  * 
26988  * @constructor
26989  * Create a new ComboCheck
26990  * @param {Object} config Configuration options
26991  */
26992 Roo.form.ComboCheck = function(config){
26993     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26994     // should verify some data...
26995     // like
26996     // hiddenName = required..
26997     // displayField = required
26998     // valudField == required
26999     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27000     var _t = this;
27001     Roo.each(req, function(e) {
27002         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27003             throw "Roo.form.ComboCheck : missing value for: " + e;
27004         }
27005     });
27006     
27007     
27008 };
27009
27010 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27011      
27012      
27013     editable : false,
27014      
27015     selectedClass: 'x-menu-item-checked', 
27016     
27017     // private
27018     onRender : function(ct, position){
27019         var _t = this;
27020         
27021         
27022         
27023         if(!this.tpl){
27024             var cls = 'x-combo-list';
27025
27026             
27027             this.tpl =  new Roo.Template({
27028                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27029                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27030                    '<span>{' + this.displayField + '}</span>' +
27031                     '</div>' 
27032                 
27033             });
27034         }
27035  
27036         
27037         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27038         this.view.singleSelect = false;
27039         this.view.multiSelect = true;
27040         this.view.toggleSelect = true;
27041         this.pageTb.add(new Roo.Toolbar.Fill(), {
27042             
27043             text: 'Done',
27044             handler: function()
27045             {
27046                 _t.collapse();
27047             }
27048         });
27049     },
27050     
27051     onViewOver : function(e, t){
27052         // do nothing...
27053         return;
27054         
27055     },
27056     
27057     onViewClick : function(doFocus,index){
27058         return;
27059         
27060     },
27061     select: function () {
27062         //Roo.log("SELECT CALLED");
27063     },
27064      
27065     selectByValue : function(xv, scrollIntoView){
27066         var ar = this.getValueArray();
27067         var sels = [];
27068         
27069         Roo.each(ar, function(v) {
27070             if(v === undefined || v === null){
27071                 return;
27072             }
27073             var r = this.findRecord(this.valueField, v);
27074             if(r){
27075                 sels.push(this.store.indexOf(r))
27076                 
27077             }
27078         },this);
27079         this.view.select(sels);
27080         return false;
27081     },
27082     
27083     
27084     
27085     onSelect : function(record, index){
27086        // Roo.log("onselect Called");
27087        // this is only called by the clear button now..
27088         this.view.clearSelections();
27089         this.setValue('[]');
27090         if (this.value != this.valueBefore) {
27091             this.fireEvent('change', this, this.value, this.valueBefore);
27092             this.valueBefore = this.value;
27093         }
27094     },
27095     getValueArray : function()
27096     {
27097         var ar = [] ;
27098         
27099         try {
27100             //Roo.log(this.value);
27101             if (typeof(this.value) == 'undefined') {
27102                 return [];
27103             }
27104             var ar = Roo.decode(this.value);
27105             return  ar instanceof Array ? ar : []; //?? valid?
27106             
27107         } catch(e) {
27108             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27109             return [];
27110         }
27111          
27112     },
27113     expand : function ()
27114     {
27115         
27116         Roo.form.ComboCheck.superclass.expand.call(this);
27117         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27118         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27119         
27120
27121     },
27122     
27123     collapse : function(){
27124         Roo.form.ComboCheck.superclass.collapse.call(this);
27125         var sl = this.view.getSelectedIndexes();
27126         var st = this.store;
27127         var nv = [];
27128         var tv = [];
27129         var r;
27130         Roo.each(sl, function(i) {
27131             r = st.getAt(i);
27132             nv.push(r.get(this.valueField));
27133         },this);
27134         this.setValue(Roo.encode(nv));
27135         if (this.value != this.valueBefore) {
27136
27137             this.fireEvent('change', this, this.value, this.valueBefore);
27138             this.valueBefore = this.value;
27139         }
27140         
27141     },
27142     
27143     setValue : function(v){
27144         // Roo.log(v);
27145         this.value = v;
27146         
27147         var vals = this.getValueArray();
27148         var tv = [];
27149         Roo.each(vals, function(k) {
27150             var r = this.findRecord(this.valueField, k);
27151             if(r){
27152                 tv.push(r.data[this.displayField]);
27153             }else if(this.valueNotFoundText !== undefined){
27154                 tv.push( this.valueNotFoundText );
27155             }
27156         },this);
27157        // Roo.log(tv);
27158         
27159         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27160         this.hiddenField.value = v;
27161         this.value = v;
27162     }
27163     
27164 });/*
27165  * Based on:
27166  * Ext JS Library 1.1.1
27167  * Copyright(c) 2006-2007, Ext JS, LLC.
27168  *
27169  * Originally Released Under LGPL - original licence link has changed is not relivant.
27170  *
27171  * Fork - LGPL
27172  * <script type="text/javascript">
27173  */
27174  
27175 /**
27176  * @class Roo.form.Signature
27177  * @extends Roo.form.Field
27178  * Signature field.  
27179  * @constructor
27180  * 
27181  * @param {Object} config Configuration options
27182  */
27183
27184 Roo.form.Signature = function(config){
27185     Roo.form.Signature.superclass.constructor.call(this, config);
27186     
27187     this.addEvents({// not in used??
27188          /**
27189          * @event confirm
27190          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27191              * @param {Roo.form.Signature} combo This combo box
27192              */
27193         'confirm' : true,
27194         /**
27195          * @event reset
27196          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27197              * @param {Roo.form.ComboBox} combo This combo box
27198              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27199              */
27200         'reset' : true
27201     });
27202 };
27203
27204 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27205     /**
27206      * @cfg {Object} labels Label to use when rendering a form.
27207      * defaults to 
27208      * labels : { 
27209      *      clear : "Clear",
27210      *      confirm : "Confirm"
27211      *  }
27212      */
27213     labels : { 
27214         clear : "Clear",
27215         confirm : "Confirm"
27216     },
27217     /**
27218      * @cfg {Number} width The signature panel width (defaults to 300)
27219      */
27220     width: 300,
27221     /**
27222      * @cfg {Number} height The signature panel height (defaults to 100)
27223      */
27224     height : 100,
27225     /**
27226      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27227      */
27228     allowBlank : false,
27229     
27230     //private
27231     // {Object} signPanel The signature SVG panel element (defaults to {})
27232     signPanel : {},
27233     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27234     isMouseDown : false,
27235     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27236     isConfirmed : false,
27237     // {String} signatureTmp SVG mapping string (defaults to empty string)
27238     signatureTmp : '',
27239     
27240     
27241     defaultAutoCreate : { // modified by initCompnoent..
27242         tag: "input",
27243         type:"hidden"
27244     },
27245
27246     // private
27247     onRender : function(ct, position){
27248         
27249         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27250         
27251         this.wrap = this.el.wrap({
27252             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27253         });
27254         
27255         this.createToolbar(this);
27256         this.signPanel = this.wrap.createChild({
27257                 tag: 'div',
27258                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27259             }, this.el
27260         );
27261             
27262         this.svgID = Roo.id();
27263         this.svgEl = this.signPanel.createChild({
27264               xmlns : 'http://www.w3.org/2000/svg',
27265               tag : 'svg',
27266               id : this.svgID + "-svg",
27267               width: this.width,
27268               height: this.height,
27269               viewBox: '0 0 '+this.width+' '+this.height,
27270               cn : [
27271                 {
27272                     tag: "rect",
27273                     id: this.svgID + "-svg-r",
27274                     width: this.width,
27275                     height: this.height,
27276                     fill: "#ffa"
27277                 },
27278                 {
27279                     tag: "line",
27280                     id: this.svgID + "-svg-l",
27281                     x1: "0", // start
27282                     y1: (this.height*0.8), // start set the line in 80% of height
27283                     x2: this.width, // end
27284                     y2: (this.height*0.8), // end set the line in 80% of height
27285                     'stroke': "#666",
27286                     'stroke-width': "1",
27287                     'stroke-dasharray': "3",
27288                     'shape-rendering': "crispEdges",
27289                     'pointer-events': "none"
27290                 },
27291                 {
27292                     tag: "path",
27293                     id: this.svgID + "-svg-p",
27294                     'stroke': "navy",
27295                     'stroke-width': "3",
27296                     'fill': "none",
27297                     'pointer-events': 'none'
27298                 }
27299               ]
27300         });
27301         this.createSVG();
27302         this.svgBox = this.svgEl.dom.getScreenCTM();
27303     },
27304     createSVG : function(){ 
27305         var svg = this.signPanel;
27306         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27307         var t = this;
27308
27309         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27310         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27311         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27312         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27313         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27314         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27315         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27316         
27317     },
27318     isTouchEvent : function(e){
27319         return e.type.match(/^touch/);
27320     },
27321     getCoords : function (e) {
27322         var pt    = this.svgEl.dom.createSVGPoint();
27323         pt.x = e.clientX; 
27324         pt.y = e.clientY;
27325         if (this.isTouchEvent(e)) {
27326             pt.x =  e.targetTouches[0].clientX;
27327             pt.y = e.targetTouches[0].clientY;
27328         }
27329         var a = this.svgEl.dom.getScreenCTM();
27330         var b = a.inverse();
27331         var mx = pt.matrixTransform(b);
27332         return mx.x + ',' + mx.y;
27333     },
27334     //mouse event headler 
27335     down : function (e) {
27336         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27337         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27338         
27339         this.isMouseDown = true;
27340         
27341         e.preventDefault();
27342     },
27343     move : function (e) {
27344         if (this.isMouseDown) {
27345             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27346             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27347         }
27348         
27349         e.preventDefault();
27350     },
27351     up : function (e) {
27352         this.isMouseDown = false;
27353         var sp = this.signatureTmp.split(' ');
27354         
27355         if(sp.length > 1){
27356             if(!sp[sp.length-2].match(/^L/)){
27357                 sp.pop();
27358                 sp.pop();
27359                 sp.push("");
27360                 this.signatureTmp = sp.join(" ");
27361             }
27362         }
27363         if(this.getValue() != this.signatureTmp){
27364             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27365             this.isConfirmed = false;
27366         }
27367         e.preventDefault();
27368     },
27369     
27370     /**
27371      * Protected method that will not generally be called directly. It
27372      * is called when the editor creates its toolbar. Override this method if you need to
27373      * add custom toolbar buttons.
27374      * @param {HtmlEditor} editor
27375      */
27376     createToolbar : function(editor){
27377          function btn(id, toggle, handler){
27378             var xid = fid + '-'+ id ;
27379             return {
27380                 id : xid,
27381                 cmd : id,
27382                 cls : 'x-btn-icon x-edit-'+id,
27383                 enableToggle:toggle !== false,
27384                 scope: editor, // was editor...
27385                 handler:handler||editor.relayBtnCmd,
27386                 clickEvent:'mousedown',
27387                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27388                 tabIndex:-1
27389             };
27390         }
27391         
27392         
27393         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27394         this.tb = tb;
27395         this.tb.add(
27396            {
27397                 cls : ' x-signature-btn x-signature-'+id,
27398                 scope: editor, // was editor...
27399                 handler: this.reset,
27400                 clickEvent:'mousedown',
27401                 text: this.labels.clear
27402             },
27403             {
27404                  xtype : 'Fill',
27405                  xns: Roo.Toolbar
27406             }, 
27407             {
27408                 cls : '  x-signature-btn x-signature-'+id,
27409                 scope: editor, // was editor...
27410                 handler: this.confirmHandler,
27411                 clickEvent:'mousedown',
27412                 text: this.labels.confirm
27413             }
27414         );
27415     
27416     },
27417     //public
27418     /**
27419      * when user is clicked confirm then show this image.....
27420      * 
27421      * @return {String} Image Data URI
27422      */
27423     getImageDataURI : function(){
27424         var svg = this.svgEl.dom.parentNode.innerHTML;
27425         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27426         return src; 
27427     },
27428     /**
27429      * 
27430      * @return {Boolean} this.isConfirmed
27431      */
27432     getConfirmed : function(){
27433         return this.isConfirmed;
27434     },
27435     /**
27436      * 
27437      * @return {Number} this.width
27438      */
27439     getWidth : function(){
27440         return this.width;
27441     },
27442     /**
27443      * 
27444      * @return {Number} this.height
27445      */
27446     getHeight : function(){
27447         return this.height;
27448     },
27449     // private
27450     getSignature : function(){
27451         return this.signatureTmp;
27452     },
27453     // private
27454     reset : function(){
27455         this.signatureTmp = '';
27456         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27457         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27458         this.isConfirmed = false;
27459         Roo.form.Signature.superclass.reset.call(this);
27460     },
27461     setSignature : function(s){
27462         this.signatureTmp = s;
27463         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27464         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27465         this.setValue(s);
27466         this.isConfirmed = false;
27467         Roo.form.Signature.superclass.reset.call(this);
27468     }, 
27469     test : function(){
27470 //        Roo.log(this.signPanel.dom.contentWindow.up())
27471     },
27472     //private
27473     setConfirmed : function(){
27474         
27475         
27476         
27477 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27478     },
27479     // private
27480     confirmHandler : function(){
27481         if(!this.getSignature()){
27482             return;
27483         }
27484         
27485         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27486         this.setValue(this.getSignature());
27487         this.isConfirmed = true;
27488         
27489         this.fireEvent('confirm', this);
27490     },
27491     // private
27492     // Subclasses should provide the validation implementation by overriding this
27493     validateValue : function(value){
27494         if(this.allowBlank){
27495             return true;
27496         }
27497         
27498         if(this.isConfirmed){
27499             return true;
27500         }
27501         return false;
27502     }
27503 });/*
27504  * Based on:
27505  * Ext JS Library 1.1.1
27506  * Copyright(c) 2006-2007, Ext JS, LLC.
27507  *
27508  * Originally Released Under LGPL - original licence link has changed is not relivant.
27509  *
27510  * Fork - LGPL
27511  * <script type="text/javascript">
27512  */
27513  
27514
27515 /**
27516  * @class Roo.form.ComboBox
27517  * @extends Roo.form.TriggerField
27518  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27519  * @constructor
27520  * Create a new ComboBox.
27521  * @param {Object} config Configuration options
27522  */
27523 Roo.form.Select = function(config){
27524     Roo.form.Select.superclass.constructor.call(this, config);
27525      
27526 };
27527
27528 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27529     /**
27530      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27531      */
27532     /**
27533      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27534      * rendering into an Roo.Editor, defaults to false)
27535      */
27536     /**
27537      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27538      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27539      */
27540     /**
27541      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27542      */
27543     /**
27544      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27545      * the dropdown list (defaults to undefined, with no header element)
27546      */
27547
27548      /**
27549      * @cfg {String/Roo.Template} tpl The template to use to render the output
27550      */
27551      
27552     // private
27553     defaultAutoCreate : {tag: "select"  },
27554     /**
27555      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27556      */
27557     listWidth: undefined,
27558     /**
27559      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27560      * mode = 'remote' or 'text' if mode = 'local')
27561      */
27562     displayField: undefined,
27563     /**
27564      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27565      * mode = 'remote' or 'value' if mode = 'local'). 
27566      * Note: use of a valueField requires the user make a selection
27567      * in order for a value to be mapped.
27568      */
27569     valueField: undefined,
27570     
27571     
27572     /**
27573      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27574      * field's data value (defaults to the underlying DOM element's name)
27575      */
27576     hiddenName: undefined,
27577     /**
27578      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27579      */
27580     listClass: '',
27581     /**
27582      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27583      */
27584     selectedClass: 'x-combo-selected',
27585     /**
27586      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27587      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27588      * which displays a downward arrow icon).
27589      */
27590     triggerClass : 'x-form-arrow-trigger',
27591     /**
27592      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27593      */
27594     shadow:'sides',
27595     /**
27596      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27597      * anchor positions (defaults to 'tl-bl')
27598      */
27599     listAlign: 'tl-bl?',
27600     /**
27601      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27602      */
27603     maxHeight: 300,
27604     /**
27605      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27606      * query specified by the allQuery config option (defaults to 'query')
27607      */
27608     triggerAction: 'query',
27609     /**
27610      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27611      * (defaults to 4, does not apply if editable = false)
27612      */
27613     minChars : 4,
27614     /**
27615      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27616      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27617      */
27618     typeAhead: false,
27619     /**
27620      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27621      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27622      */
27623     queryDelay: 500,
27624     /**
27625      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27626      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27627      */
27628     pageSize: 0,
27629     /**
27630      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27631      * when editable = true (defaults to false)
27632      */
27633     selectOnFocus:false,
27634     /**
27635      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27636      */
27637     queryParam: 'query',
27638     /**
27639      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27640      * when mode = 'remote' (defaults to 'Loading...')
27641      */
27642     loadingText: 'Loading...',
27643     /**
27644      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27645      */
27646     resizable: false,
27647     /**
27648      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27649      */
27650     handleHeight : 8,
27651     /**
27652      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27653      * traditional select (defaults to true)
27654      */
27655     editable: true,
27656     /**
27657      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27658      */
27659     allQuery: '',
27660     /**
27661      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27662      */
27663     mode: 'remote',
27664     /**
27665      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27666      * listWidth has a higher value)
27667      */
27668     minListWidth : 70,
27669     /**
27670      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27671      * allow the user to set arbitrary text into the field (defaults to false)
27672      */
27673     forceSelection:false,
27674     /**
27675      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27676      * if typeAhead = true (defaults to 250)
27677      */
27678     typeAheadDelay : 250,
27679     /**
27680      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27681      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27682      */
27683     valueNotFoundText : undefined,
27684     
27685     /**
27686      * @cfg {String} defaultValue The value displayed after loading the store.
27687      */
27688     defaultValue: '',
27689     
27690     /**
27691      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27692      */
27693     blockFocus : false,
27694     
27695     /**
27696      * @cfg {Boolean} disableClear Disable showing of clear button.
27697      */
27698     disableClear : false,
27699     /**
27700      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27701      */
27702     alwaysQuery : false,
27703     
27704     //private
27705     addicon : false,
27706     editicon: false,
27707     
27708     // element that contains real text value.. (when hidden is used..)
27709      
27710     // private
27711     onRender : function(ct, position){
27712         Roo.form.Field.prototype.onRender.call(this, ct, position);
27713         
27714         if(this.store){
27715             this.store.on('beforeload', this.onBeforeLoad, this);
27716             this.store.on('load', this.onLoad, this);
27717             this.store.on('loadexception', this.onLoadException, this);
27718             this.store.load({});
27719         }
27720         
27721         
27722         
27723     },
27724
27725     // private
27726     initEvents : function(){
27727         //Roo.form.ComboBox.superclass.initEvents.call(this);
27728  
27729     },
27730
27731     onDestroy : function(){
27732        
27733         if(this.store){
27734             this.store.un('beforeload', this.onBeforeLoad, this);
27735             this.store.un('load', this.onLoad, this);
27736             this.store.un('loadexception', this.onLoadException, this);
27737         }
27738         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27739     },
27740
27741     // private
27742     fireKey : function(e){
27743         if(e.isNavKeyPress() && !this.list.isVisible()){
27744             this.fireEvent("specialkey", this, e);
27745         }
27746     },
27747
27748     // private
27749     onResize: function(w, h){
27750         
27751         return; 
27752     
27753         
27754     },
27755
27756     /**
27757      * Allow or prevent the user from directly editing the field text.  If false is passed,
27758      * the user will only be able to select from the items defined in the dropdown list.  This method
27759      * is the runtime equivalent of setting the 'editable' config option at config time.
27760      * @param {Boolean} value True to allow the user to directly edit the field text
27761      */
27762     setEditable : function(value){
27763          
27764     },
27765
27766     // private
27767     onBeforeLoad : function(){
27768         
27769         Roo.log("Select before load");
27770         return;
27771     
27772         this.innerList.update(this.loadingText ?
27773                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27774         //this.restrictHeight();
27775         this.selectedIndex = -1;
27776     },
27777
27778     // private
27779     onLoad : function(){
27780
27781     
27782         var dom = this.el.dom;
27783         dom.innerHTML = '';
27784          var od = dom.ownerDocument;
27785          
27786         if (this.emptyText) {
27787             var op = od.createElement('option');
27788             op.setAttribute('value', '');
27789             op.innerHTML = String.format('{0}', this.emptyText);
27790             dom.appendChild(op);
27791         }
27792         if(this.store.getCount() > 0){
27793            
27794             var vf = this.valueField;
27795             var df = this.displayField;
27796             this.store.data.each(function(r) {
27797                 // which colmsn to use... testing - cdoe / title..
27798                 var op = od.createElement('option');
27799                 op.setAttribute('value', r.data[vf]);
27800                 op.innerHTML = String.format('{0}', r.data[df]);
27801                 dom.appendChild(op);
27802             });
27803             if (typeof(this.defaultValue != 'undefined')) {
27804                 this.setValue(this.defaultValue);
27805             }
27806             
27807              
27808         }else{
27809             //this.onEmptyResults();
27810         }
27811         //this.el.focus();
27812     },
27813     // private
27814     onLoadException : function()
27815     {
27816         dom.innerHTML = '';
27817             
27818         Roo.log("Select on load exception");
27819         return;
27820     
27821         this.collapse();
27822         Roo.log(this.store.reader.jsonData);
27823         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27824             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27825         }
27826         
27827         
27828     },
27829     // private
27830     onTypeAhead : function(){
27831          
27832     },
27833
27834     // private
27835     onSelect : function(record, index){
27836         Roo.log('on select?');
27837         return;
27838         if(this.fireEvent('beforeselect', this, record, index) !== false){
27839             this.setFromData(index > -1 ? record.data : false);
27840             this.collapse();
27841             this.fireEvent('select', this, record, index);
27842         }
27843     },
27844
27845     /**
27846      * Returns the currently selected field value or empty string if no value is set.
27847      * @return {String} value The selected value
27848      */
27849     getValue : function(){
27850         var dom = this.el.dom;
27851         this.value = dom.options[dom.selectedIndex].value;
27852         return this.value;
27853         
27854     },
27855
27856     /**
27857      * Clears any text/value currently set in the field
27858      */
27859     clearValue : function(){
27860         this.value = '';
27861         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27862         
27863     },
27864
27865     /**
27866      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27867      * will be displayed in the field.  If the value does not match the data value of an existing item,
27868      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27869      * Otherwise the field will be blank (although the value will still be set).
27870      * @param {String} value The value to match
27871      */
27872     setValue : function(v){
27873         var d = this.el.dom;
27874         for (var i =0; i < d.options.length;i++) {
27875             if (v == d.options[i].value) {
27876                 d.selectedIndex = i;
27877                 this.value = v;
27878                 return;
27879             }
27880         }
27881         this.clearValue();
27882     },
27883     /**
27884      * @property {Object} the last set data for the element
27885      */
27886     
27887     lastData : false,
27888     /**
27889      * Sets the value of the field based on a object which is related to the record format for the store.
27890      * @param {Object} value the value to set as. or false on reset?
27891      */
27892     setFromData : function(o){
27893         Roo.log('setfrom data?');
27894          
27895         
27896         
27897     },
27898     // private
27899     reset : function(){
27900         this.clearValue();
27901     },
27902     // private
27903     findRecord : function(prop, value){
27904         
27905         return false;
27906     
27907         var record;
27908         if(this.store.getCount() > 0){
27909             this.store.each(function(r){
27910                 if(r.data[prop] == value){
27911                     record = r;
27912                     return false;
27913                 }
27914                 return true;
27915             });
27916         }
27917         return record;
27918     },
27919     
27920     getName: function()
27921     {
27922         // returns hidden if it's set..
27923         if (!this.rendered) {return ''};
27924         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27925         
27926     },
27927      
27928
27929     
27930
27931     // private
27932     onEmptyResults : function(){
27933         Roo.log('empty results');
27934         //this.collapse();
27935     },
27936
27937     /**
27938      * Returns true if the dropdown list is expanded, else false.
27939      */
27940     isExpanded : function(){
27941         return false;
27942     },
27943
27944     /**
27945      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27946      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27947      * @param {String} value The data value of the item to select
27948      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27949      * selected item if it is not currently in view (defaults to true)
27950      * @return {Boolean} True if the value matched an item in the list, else false
27951      */
27952     selectByValue : function(v, scrollIntoView){
27953         Roo.log('select By Value');
27954         return false;
27955     
27956         if(v !== undefined && v !== null){
27957             var r = this.findRecord(this.valueField || this.displayField, v);
27958             if(r){
27959                 this.select(this.store.indexOf(r), scrollIntoView);
27960                 return true;
27961             }
27962         }
27963         return false;
27964     },
27965
27966     /**
27967      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27968      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27969      * @param {Number} index The zero-based index of the list item to select
27970      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27971      * selected item if it is not currently in view (defaults to true)
27972      */
27973     select : function(index, scrollIntoView){
27974         Roo.log('select ');
27975         return  ;
27976         
27977         this.selectedIndex = index;
27978         this.view.select(index);
27979         if(scrollIntoView !== false){
27980             var el = this.view.getNode(index);
27981             if(el){
27982                 this.innerList.scrollChildIntoView(el, false);
27983             }
27984         }
27985     },
27986
27987       
27988
27989     // private
27990     validateBlur : function(){
27991         
27992         return;
27993         
27994     },
27995
27996     // private
27997     initQuery : function(){
27998         this.doQuery(this.getRawValue());
27999     },
28000
28001     // private
28002     doForce : function(){
28003         if(this.el.dom.value.length > 0){
28004             this.el.dom.value =
28005                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28006              
28007         }
28008     },
28009
28010     /**
28011      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28012      * query allowing the query action to be canceled if needed.
28013      * @param {String} query The SQL query to execute
28014      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28015      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28016      * saved in the current store (defaults to false)
28017      */
28018     doQuery : function(q, forceAll){
28019         
28020         Roo.log('doQuery?');
28021         if(q === undefined || q === null){
28022             q = '';
28023         }
28024         var qe = {
28025             query: q,
28026             forceAll: forceAll,
28027             combo: this,
28028             cancel:false
28029         };
28030         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28031             return false;
28032         }
28033         q = qe.query;
28034         forceAll = qe.forceAll;
28035         if(forceAll === true || (q.length >= this.minChars)){
28036             if(this.lastQuery != q || this.alwaysQuery){
28037                 this.lastQuery = q;
28038                 if(this.mode == 'local'){
28039                     this.selectedIndex = -1;
28040                     if(forceAll){
28041                         this.store.clearFilter();
28042                     }else{
28043                         this.store.filter(this.displayField, q);
28044                     }
28045                     this.onLoad();
28046                 }else{
28047                     this.store.baseParams[this.queryParam] = q;
28048                     this.store.load({
28049                         params: this.getParams(q)
28050                     });
28051                     this.expand();
28052                 }
28053             }else{
28054                 this.selectedIndex = -1;
28055                 this.onLoad();   
28056             }
28057         }
28058     },
28059
28060     // private
28061     getParams : function(q){
28062         var p = {};
28063         //p[this.queryParam] = q;
28064         if(this.pageSize){
28065             p.start = 0;
28066             p.limit = this.pageSize;
28067         }
28068         return p;
28069     },
28070
28071     /**
28072      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28073      */
28074     collapse : function(){
28075         
28076     },
28077
28078     // private
28079     collapseIf : function(e){
28080         
28081     },
28082
28083     /**
28084      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28085      */
28086     expand : function(){
28087         
28088     } ,
28089
28090     // private
28091      
28092
28093     /** 
28094     * @cfg {Boolean} grow 
28095     * @hide 
28096     */
28097     /** 
28098     * @cfg {Number} growMin 
28099     * @hide 
28100     */
28101     /** 
28102     * @cfg {Number} growMax 
28103     * @hide 
28104     */
28105     /**
28106      * @hide
28107      * @method autoSize
28108      */
28109     
28110     setWidth : function()
28111     {
28112         
28113     },
28114     getResizeEl : function(){
28115         return this.el;
28116     }
28117 });//<script type="text/javasscript">
28118  
28119
28120 /**
28121  * @class Roo.DDView
28122  * A DnD enabled version of Roo.View.
28123  * @param {Element/String} container The Element in which to create the View.
28124  * @param {String} tpl The template string used to create the markup for each element of the View
28125  * @param {Object} config The configuration properties. These include all the config options of
28126  * {@link Roo.View} plus some specific to this class.<br>
28127  * <p>
28128  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28129  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28130  * <p>
28131  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28132 .x-view-drag-insert-above {
28133         border-top:1px dotted #3366cc;
28134 }
28135 .x-view-drag-insert-below {
28136         border-bottom:1px dotted #3366cc;
28137 }
28138 </code></pre>
28139  * 
28140  */
28141  
28142 Roo.DDView = function(container, tpl, config) {
28143     Roo.DDView.superclass.constructor.apply(this, arguments);
28144     this.getEl().setStyle("outline", "0px none");
28145     this.getEl().unselectable();
28146     if (this.dragGroup) {
28147                 this.setDraggable(this.dragGroup.split(","));
28148     }
28149     if (this.dropGroup) {
28150                 this.setDroppable(this.dropGroup.split(","));
28151     }
28152     if (this.deletable) {
28153         this.setDeletable();
28154     }
28155     this.isDirtyFlag = false;
28156         this.addEvents({
28157                 "drop" : true
28158         });
28159 };
28160
28161 Roo.extend(Roo.DDView, Roo.View, {
28162 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28163 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28164 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28165 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28166
28167         isFormField: true,
28168
28169         reset: Roo.emptyFn,
28170         
28171         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28172
28173         validate: function() {
28174                 return true;
28175         },
28176         
28177         destroy: function() {
28178                 this.purgeListeners();
28179                 this.getEl.removeAllListeners();
28180                 this.getEl().remove();
28181                 if (this.dragZone) {
28182                         if (this.dragZone.destroy) {
28183                                 this.dragZone.destroy();
28184                         }
28185                 }
28186                 if (this.dropZone) {
28187                         if (this.dropZone.destroy) {
28188                                 this.dropZone.destroy();
28189                         }
28190                 }
28191         },
28192
28193 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28194         getName: function() {
28195                 return this.name;
28196         },
28197
28198 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28199         setValue: function(v) {
28200                 if (!this.store) {
28201                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28202                 }
28203                 var data = {};
28204                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28205                 this.store.proxy = new Roo.data.MemoryProxy(data);
28206                 this.store.load();
28207         },
28208
28209 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28210         getValue: function() {
28211                 var result = '(';
28212                 this.store.each(function(rec) {
28213                         result += rec.id + ',';
28214                 });
28215                 return result.substr(0, result.length - 1) + ')';
28216         },
28217         
28218         getIds: function() {
28219                 var i = 0, result = new Array(this.store.getCount());
28220                 this.store.each(function(rec) {
28221                         result[i++] = rec.id;
28222                 });
28223                 return result;
28224         },
28225         
28226         isDirty: function() {
28227                 return this.isDirtyFlag;
28228         },
28229
28230 /**
28231  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28232  *      whole Element becomes the target, and this causes the drop gesture to append.
28233  */
28234     getTargetFromEvent : function(e) {
28235                 var target = e.getTarget();
28236                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28237                 target = target.parentNode;
28238                 }
28239                 if (!target) {
28240                         target = this.el.dom.lastChild || this.el.dom;
28241                 }
28242                 return target;
28243     },
28244
28245 /**
28246  *      Create the drag data which consists of an object which has the property "ddel" as
28247  *      the drag proxy element. 
28248  */
28249     getDragData : function(e) {
28250         var target = this.findItemFromChild(e.getTarget());
28251                 if(target) {
28252                         this.handleSelection(e);
28253                         var selNodes = this.getSelectedNodes();
28254             var dragData = {
28255                 source: this,
28256                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28257                 nodes: selNodes,
28258                 records: []
28259                         };
28260                         var selectedIndices = this.getSelectedIndexes();
28261                         for (var i = 0; i < selectedIndices.length; i++) {
28262                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28263                         }
28264                         if (selNodes.length == 1) {
28265                                 dragData.ddel = target.cloneNode(true); // the div element
28266                         } else {
28267                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28268                                 div.className = 'multi-proxy';
28269                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28270                                         div.appendChild(selNodes[i].cloneNode(true));
28271                                 }
28272                                 dragData.ddel = div;
28273                         }
28274             //console.log(dragData)
28275             //console.log(dragData.ddel.innerHTML)
28276                         return dragData;
28277                 }
28278         //console.log('nodragData')
28279                 return false;
28280     },
28281     
28282 /**     Specify to which ddGroup items in this DDView may be dragged. */
28283     setDraggable: function(ddGroup) {
28284         if (ddGroup instanceof Array) {
28285                 Roo.each(ddGroup, this.setDraggable, this);
28286                 return;
28287         }
28288         if (this.dragZone) {
28289                 this.dragZone.addToGroup(ddGroup);
28290         } else {
28291                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28292                                 containerScroll: true,
28293                                 ddGroup: ddGroup 
28294
28295                         });
28296 //                      Draggability implies selection. DragZone's mousedown selects the element.
28297                         if (!this.multiSelect) { this.singleSelect = true; }
28298
28299 //                      Wire the DragZone's handlers up to methods in *this*
28300                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28301                 }
28302     },
28303
28304 /**     Specify from which ddGroup this DDView accepts drops. */
28305     setDroppable: function(ddGroup) {
28306         if (ddGroup instanceof Array) {
28307                 Roo.each(ddGroup, this.setDroppable, this);
28308                 return;
28309         }
28310         if (this.dropZone) {
28311                 this.dropZone.addToGroup(ddGroup);
28312         } else {
28313                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28314                                 containerScroll: true,
28315                                 ddGroup: ddGroup
28316                         });
28317
28318 //                      Wire the DropZone's handlers up to methods in *this*
28319                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28320                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28321                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28322                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28323                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28324                 }
28325     },
28326
28327 /**     Decide whether to drop above or below a View node. */
28328     getDropPoint : function(e, n, dd){
28329         if (n == this.el.dom) { return "above"; }
28330                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28331                 var c = t + (b - t) / 2;
28332                 var y = Roo.lib.Event.getPageY(e);
28333                 if(y <= c) {
28334                         return "above";
28335                 }else{
28336                         return "below";
28337                 }
28338     },
28339
28340     onNodeEnter : function(n, dd, e, data){
28341                 return false;
28342     },
28343     
28344     onNodeOver : function(n, dd, e, data){
28345                 var pt = this.getDropPoint(e, n, dd);
28346                 // set the insert point style on the target node
28347                 var dragElClass = this.dropNotAllowed;
28348                 if (pt) {
28349                         var targetElClass;
28350                         if (pt == "above"){
28351                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28352                                 targetElClass = "x-view-drag-insert-above";
28353                         } else {
28354                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28355                                 targetElClass = "x-view-drag-insert-below";
28356                         }
28357                         if (this.lastInsertClass != targetElClass){
28358                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28359                                 this.lastInsertClass = targetElClass;
28360                         }
28361                 }
28362                 return dragElClass;
28363         },
28364
28365     onNodeOut : function(n, dd, e, data){
28366                 this.removeDropIndicators(n);
28367     },
28368
28369     onNodeDrop : function(n, dd, e, data){
28370         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28371                 return false;
28372         }
28373         var pt = this.getDropPoint(e, n, dd);
28374                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28375                 if (pt == "below") { insertAt++; }
28376                 for (var i = 0; i < data.records.length; i++) {
28377                         var r = data.records[i];
28378                         var dup = this.store.getById(r.id);
28379                         if (dup && (dd != this.dragZone)) {
28380                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28381                         } else {
28382                                 if (data.copy) {
28383                                         this.store.insert(insertAt++, r.copy());
28384                                 } else {
28385                                         data.source.isDirtyFlag = true;
28386                                         r.store.remove(r);
28387                                         this.store.insert(insertAt++, r);
28388                                 }
28389                                 this.isDirtyFlag = true;
28390                         }
28391                 }
28392                 this.dragZone.cachedTarget = null;
28393                 return true;
28394     },
28395
28396     removeDropIndicators : function(n){
28397                 if(n){
28398                         Roo.fly(n).removeClass([
28399                                 "x-view-drag-insert-above",
28400                                 "x-view-drag-insert-below"]);
28401                         this.lastInsertClass = "_noclass";
28402                 }
28403     },
28404
28405 /**
28406  *      Utility method. Add a delete option to the DDView's context menu.
28407  *      @param {String} imageUrl The URL of the "delete" icon image.
28408  */
28409         setDeletable: function(imageUrl) {
28410                 if (!this.singleSelect && !this.multiSelect) {
28411                         this.singleSelect = true;
28412                 }
28413                 var c = this.getContextMenu();
28414                 this.contextMenu.on("itemclick", function(item) {
28415                         switch (item.id) {
28416                                 case "delete":
28417                                         this.remove(this.getSelectedIndexes());
28418                                         break;
28419                         }
28420                 }, this);
28421                 this.contextMenu.add({
28422                         icon: imageUrl,
28423                         id: "delete",
28424                         text: 'Delete'
28425                 });
28426         },
28427         
28428 /**     Return the context menu for this DDView. */
28429         getContextMenu: function() {
28430                 if (!this.contextMenu) {
28431 //                      Create the View's context menu
28432                         this.contextMenu = new Roo.menu.Menu({
28433                                 id: this.id + "-contextmenu"
28434                         });
28435                         this.el.on("contextmenu", this.showContextMenu, this);
28436                 }
28437                 return this.contextMenu;
28438         },
28439         
28440         disableContextMenu: function() {
28441                 if (this.contextMenu) {
28442                         this.el.un("contextmenu", this.showContextMenu, this);
28443                 }
28444         },
28445
28446         showContextMenu: function(e, item) {
28447         item = this.findItemFromChild(e.getTarget());
28448                 if (item) {
28449                         e.stopEvent();
28450                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28451                         this.contextMenu.showAt(e.getXY());
28452             }
28453     },
28454
28455 /**
28456  *      Remove {@link Roo.data.Record}s at the specified indices.
28457  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28458  */
28459     remove: function(selectedIndices) {
28460                 selectedIndices = [].concat(selectedIndices);
28461                 for (var i = 0; i < selectedIndices.length; i++) {
28462                         var rec = this.store.getAt(selectedIndices[i]);
28463                         this.store.remove(rec);
28464                 }
28465     },
28466
28467 /**
28468  *      Double click fires the event, but also, if this is draggable, and there is only one other
28469  *      related DropZone, it transfers the selected node.
28470  */
28471     onDblClick : function(e){
28472         var item = this.findItemFromChild(e.getTarget());
28473         if(item){
28474             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28475                 return false;
28476             }
28477             if (this.dragGroup) {
28478                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28479                     while (targets.indexOf(this.dropZone) > -1) {
28480                             targets.remove(this.dropZone);
28481                                 }
28482                     if (targets.length == 1) {
28483                                         this.dragZone.cachedTarget = null;
28484                         var el = Roo.get(targets[0].getEl());
28485                         var box = el.getBox(true);
28486                         targets[0].onNodeDrop(el.dom, {
28487                                 target: el.dom,
28488                                 xy: [box.x, box.y + box.height - 1]
28489                         }, null, this.getDragData(e));
28490                     }
28491                 }
28492         }
28493     },
28494     
28495     handleSelection: function(e) {
28496                 this.dragZone.cachedTarget = null;
28497         var item = this.findItemFromChild(e.getTarget());
28498         if (!item) {
28499                 this.clearSelections(true);
28500                 return;
28501         }
28502                 if (item && (this.multiSelect || this.singleSelect)){
28503                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28504                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28505                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28506                                 this.unselect(item);
28507                         } else {
28508                                 this.select(item, this.multiSelect && e.ctrlKey);
28509                                 this.lastSelection = item;
28510                         }
28511                 }
28512     },
28513
28514     onItemClick : function(item, index, e){
28515                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28516                         return false;
28517                 }
28518                 return true;
28519     },
28520
28521     unselect : function(nodeInfo, suppressEvent){
28522                 var node = this.getNode(nodeInfo);
28523                 if(node && this.isSelected(node)){
28524                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28525                                 Roo.fly(node).removeClass(this.selectedClass);
28526                                 this.selections.remove(node);
28527                                 if(!suppressEvent){
28528                                         this.fireEvent("selectionchange", this, this.selections);
28529                                 }
28530                         }
28531                 }
28532     }
28533 });
28534 /*
28535  * Based on:
28536  * Ext JS Library 1.1.1
28537  * Copyright(c) 2006-2007, Ext JS, LLC.
28538  *
28539  * Originally Released Under LGPL - original licence link has changed is not relivant.
28540  *
28541  * Fork - LGPL
28542  * <script type="text/javascript">
28543  */
28544  
28545 /**
28546  * @class Roo.LayoutManager
28547  * @extends Roo.util.Observable
28548  * Base class for layout managers.
28549  */
28550 Roo.LayoutManager = function(container, config){
28551     Roo.LayoutManager.superclass.constructor.call(this);
28552     this.el = Roo.get(container);
28553     // ie scrollbar fix
28554     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28555         document.body.scroll = "no";
28556     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28557         this.el.position('relative');
28558     }
28559     this.id = this.el.id;
28560     this.el.addClass("x-layout-container");
28561     /** false to disable window resize monitoring @type Boolean */
28562     this.monitorWindowResize = true;
28563     this.regions = {};
28564     this.addEvents({
28565         /**
28566          * @event layout
28567          * Fires when a layout is performed. 
28568          * @param {Roo.LayoutManager} this
28569          */
28570         "layout" : true,
28571         /**
28572          * @event regionresized
28573          * Fires when the user resizes a region. 
28574          * @param {Roo.LayoutRegion} region The resized region
28575          * @param {Number} newSize The new size (width for east/west, height for north/south)
28576          */
28577         "regionresized" : true,
28578         /**
28579          * @event regioncollapsed
28580          * Fires when a region is collapsed. 
28581          * @param {Roo.LayoutRegion} region The collapsed region
28582          */
28583         "regioncollapsed" : true,
28584         /**
28585          * @event regionexpanded
28586          * Fires when a region is expanded.  
28587          * @param {Roo.LayoutRegion} region The expanded region
28588          */
28589         "regionexpanded" : true
28590     });
28591     this.updating = false;
28592     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28593 };
28594
28595 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28596     /**
28597      * Returns true if this layout is currently being updated
28598      * @return {Boolean}
28599      */
28600     isUpdating : function(){
28601         return this.updating; 
28602     },
28603     
28604     /**
28605      * Suspend the LayoutManager from doing auto-layouts while
28606      * making multiple add or remove calls
28607      */
28608     beginUpdate : function(){
28609         this.updating = true;    
28610     },
28611     
28612     /**
28613      * Restore auto-layouts and optionally disable the manager from performing a layout
28614      * @param {Boolean} noLayout true to disable a layout update 
28615      */
28616     endUpdate : function(noLayout){
28617         this.updating = false;
28618         if(!noLayout){
28619             this.layout();
28620         }    
28621     },
28622     
28623     layout: function(){
28624         
28625     },
28626     
28627     onRegionResized : function(region, newSize){
28628         this.fireEvent("regionresized", region, newSize);
28629         this.layout();
28630     },
28631     
28632     onRegionCollapsed : function(region){
28633         this.fireEvent("regioncollapsed", region);
28634     },
28635     
28636     onRegionExpanded : function(region){
28637         this.fireEvent("regionexpanded", region);
28638     },
28639         
28640     /**
28641      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28642      * performs box-model adjustments.
28643      * @return {Object} The size as an object {width: (the width), height: (the height)}
28644      */
28645     getViewSize : function(){
28646         var size;
28647         if(this.el.dom != document.body){
28648             size = this.el.getSize();
28649         }else{
28650             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28651         }
28652         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28653         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28654         return size;
28655     },
28656     
28657     /**
28658      * Returns the Element this layout is bound to.
28659      * @return {Roo.Element}
28660      */
28661     getEl : function(){
28662         return this.el;
28663     },
28664     
28665     /**
28666      * Returns the specified region.
28667      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28668      * @return {Roo.LayoutRegion}
28669      */
28670     getRegion : function(target){
28671         return this.regions[target.toLowerCase()];
28672     },
28673     
28674     onWindowResize : function(){
28675         if(this.monitorWindowResize){
28676             this.layout();
28677         }
28678     }
28679 });/*
28680  * Based on:
28681  * Ext JS Library 1.1.1
28682  * Copyright(c) 2006-2007, Ext JS, LLC.
28683  *
28684  * Originally Released Under LGPL - original licence link has changed is not relivant.
28685  *
28686  * Fork - LGPL
28687  * <script type="text/javascript">
28688  */
28689 /**
28690  * @class Roo.BorderLayout
28691  * @extends Roo.LayoutManager
28692  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28693  * please see: <br><br>
28694  * <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>
28695  * <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>
28696  * Example:
28697  <pre><code>
28698  var layout = new Roo.BorderLayout(document.body, {
28699     north: {
28700         initialSize: 25,
28701         titlebar: false
28702     },
28703     west: {
28704         split:true,
28705         initialSize: 200,
28706         minSize: 175,
28707         maxSize: 400,
28708         titlebar: true,
28709         collapsible: true
28710     },
28711     east: {
28712         split:true,
28713         initialSize: 202,
28714         minSize: 175,
28715         maxSize: 400,
28716         titlebar: true,
28717         collapsible: true
28718     },
28719     south: {
28720         split:true,
28721         initialSize: 100,
28722         minSize: 100,
28723         maxSize: 200,
28724         titlebar: true,
28725         collapsible: true
28726     },
28727     center: {
28728         titlebar: true,
28729         autoScroll:true,
28730         resizeTabs: true,
28731         minTabWidth: 50,
28732         preferredTabWidth: 150
28733     }
28734 });
28735
28736 // shorthand
28737 var CP = Roo.ContentPanel;
28738
28739 layout.beginUpdate();
28740 layout.add("north", new CP("north", "North"));
28741 layout.add("south", new CP("south", {title: "South", closable: true}));
28742 layout.add("west", new CP("west", {title: "West"}));
28743 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28744 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28745 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28746 layout.getRegion("center").showPanel("center1");
28747 layout.endUpdate();
28748 </code></pre>
28749
28750 <b>The container the layout is rendered into can be either the body element or any other element.
28751 If it is not the body element, the container needs to either be an absolute positioned element,
28752 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28753 the container size if it is not the body element.</b>
28754
28755 * @constructor
28756 * Create a new BorderLayout
28757 * @param {String/HTMLElement/Element} container The container this layout is bound to
28758 * @param {Object} config Configuration options
28759  */
28760 Roo.BorderLayout = function(container, config){
28761     config = config || {};
28762     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28763     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28764     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28765         var target = this.factory.validRegions[i];
28766         if(config[target]){
28767             this.addRegion(target, config[target]);
28768         }
28769     }
28770 };
28771
28772 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28773     /**
28774      * Creates and adds a new region if it doesn't already exist.
28775      * @param {String} target The target region key (north, south, east, west or center).
28776      * @param {Object} config The regions config object
28777      * @return {BorderLayoutRegion} The new region
28778      */
28779     addRegion : function(target, config){
28780         if(!this.regions[target]){
28781             var r = this.factory.create(target, this, config);
28782             this.bindRegion(target, r);
28783         }
28784         return this.regions[target];
28785     },
28786
28787     // private (kinda)
28788     bindRegion : function(name, r){
28789         this.regions[name] = r;
28790         r.on("visibilitychange", this.layout, this);
28791         r.on("paneladded", this.layout, this);
28792         r.on("panelremoved", this.layout, this);
28793         r.on("invalidated", this.layout, this);
28794         r.on("resized", this.onRegionResized, this);
28795         r.on("collapsed", this.onRegionCollapsed, this);
28796         r.on("expanded", this.onRegionExpanded, this);
28797     },
28798
28799     /**
28800      * Performs a layout update.
28801      */
28802     layout : function(){
28803         if(this.updating) {
28804             return;
28805         }
28806         var size = this.getViewSize();
28807         var w = size.width;
28808         var h = size.height;
28809         var centerW = w;
28810         var centerH = h;
28811         var centerY = 0;
28812         var centerX = 0;
28813         //var x = 0, y = 0;
28814
28815         var rs = this.regions;
28816         var north = rs["north"];
28817         var south = rs["south"]; 
28818         var west = rs["west"];
28819         var east = rs["east"];
28820         var center = rs["center"];
28821         //if(this.hideOnLayout){ // not supported anymore
28822             //c.el.setStyle("display", "none");
28823         //}
28824         if(north && north.isVisible()){
28825             var b = north.getBox();
28826             var m = north.getMargins();
28827             b.width = w - (m.left+m.right);
28828             b.x = m.left;
28829             b.y = m.top;
28830             centerY = b.height + b.y + m.bottom;
28831             centerH -= centerY;
28832             north.updateBox(this.safeBox(b));
28833         }
28834         if(south && south.isVisible()){
28835             var b = south.getBox();
28836             var m = south.getMargins();
28837             b.width = w - (m.left+m.right);
28838             b.x = m.left;
28839             var totalHeight = (b.height + m.top + m.bottom);
28840             b.y = h - totalHeight + m.top;
28841             centerH -= totalHeight;
28842             south.updateBox(this.safeBox(b));
28843         }
28844         if(west && west.isVisible()){
28845             var b = west.getBox();
28846             var m = west.getMargins();
28847             b.height = centerH - (m.top+m.bottom);
28848             b.x = m.left;
28849             b.y = centerY + m.top;
28850             var totalWidth = (b.width + m.left + m.right);
28851             centerX += totalWidth;
28852             centerW -= totalWidth;
28853             west.updateBox(this.safeBox(b));
28854         }
28855         if(east && east.isVisible()){
28856             var b = east.getBox();
28857             var m = east.getMargins();
28858             b.height = centerH - (m.top+m.bottom);
28859             var totalWidth = (b.width + m.left + m.right);
28860             b.x = w - totalWidth + m.left;
28861             b.y = centerY + m.top;
28862             centerW -= totalWidth;
28863             east.updateBox(this.safeBox(b));
28864         }
28865         if(center){
28866             var m = center.getMargins();
28867             var centerBox = {
28868                 x: centerX + m.left,
28869                 y: centerY + m.top,
28870                 width: centerW - (m.left+m.right),
28871                 height: centerH - (m.top+m.bottom)
28872             };
28873             //if(this.hideOnLayout){
28874                 //center.el.setStyle("display", "block");
28875             //}
28876             center.updateBox(this.safeBox(centerBox));
28877         }
28878         this.el.repaint();
28879         this.fireEvent("layout", this);
28880     },
28881
28882     // private
28883     safeBox : function(box){
28884         box.width = Math.max(0, box.width);
28885         box.height = Math.max(0, box.height);
28886         return box;
28887     },
28888
28889     /**
28890      * Adds a ContentPanel (or subclass) to this layout.
28891      * @param {String} target The target region key (north, south, east, west or center).
28892      * @param {Roo.ContentPanel} panel The panel to add
28893      * @return {Roo.ContentPanel} The added panel
28894      */
28895     add : function(target, panel){
28896          
28897         target = target.toLowerCase();
28898         return this.regions[target].add(panel);
28899     },
28900
28901     /**
28902      * Remove a ContentPanel (or subclass) to this layout.
28903      * @param {String} target The target region key (north, south, east, west or center).
28904      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28905      * @return {Roo.ContentPanel} The removed panel
28906      */
28907     remove : function(target, panel){
28908         target = target.toLowerCase();
28909         return this.regions[target].remove(panel);
28910     },
28911
28912     /**
28913      * Searches all regions for a panel with the specified id
28914      * @param {String} panelId
28915      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28916      */
28917     findPanel : function(panelId){
28918         var rs = this.regions;
28919         for(var target in rs){
28920             if(typeof rs[target] != "function"){
28921                 var p = rs[target].getPanel(panelId);
28922                 if(p){
28923                     return p;
28924                 }
28925             }
28926         }
28927         return null;
28928     },
28929
28930     /**
28931      * Searches all regions for a panel with the specified id and activates (shows) it.
28932      * @param {String/ContentPanel} panelId The panels id or the panel itself
28933      * @return {Roo.ContentPanel} The shown panel or null
28934      */
28935     showPanel : function(panelId) {
28936       var rs = this.regions;
28937       for(var target in rs){
28938          var r = rs[target];
28939          if(typeof r != "function"){
28940             if(r.hasPanel(panelId)){
28941                return r.showPanel(panelId);
28942             }
28943          }
28944       }
28945       return null;
28946    },
28947
28948    /**
28949      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28950      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28951      */
28952     restoreState : function(provider){
28953         if(!provider){
28954             provider = Roo.state.Manager;
28955         }
28956         var sm = new Roo.LayoutStateManager();
28957         sm.init(this, provider);
28958     },
28959
28960     /**
28961      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28962      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28963      * a valid ContentPanel config object.  Example:
28964      * <pre><code>
28965 // Create the main layout
28966 var layout = new Roo.BorderLayout('main-ct', {
28967     west: {
28968         split:true,
28969         minSize: 175,
28970         titlebar: true
28971     },
28972     center: {
28973         title:'Components'
28974     }
28975 }, 'main-ct');
28976
28977 // Create and add multiple ContentPanels at once via configs
28978 layout.batchAdd({
28979    west: {
28980        id: 'source-files',
28981        autoCreate:true,
28982        title:'Ext Source Files',
28983        autoScroll:true,
28984        fitToFrame:true
28985    },
28986    center : {
28987        el: cview,
28988        autoScroll:true,
28989        fitToFrame:true,
28990        toolbar: tb,
28991        resizeEl:'cbody'
28992    }
28993 });
28994 </code></pre>
28995      * @param {Object} regions An object containing ContentPanel configs by region name
28996      */
28997     batchAdd : function(regions){
28998         this.beginUpdate();
28999         for(var rname in regions){
29000             var lr = this.regions[rname];
29001             if(lr){
29002                 this.addTypedPanels(lr, regions[rname]);
29003             }
29004         }
29005         this.endUpdate();
29006     },
29007
29008     // private
29009     addTypedPanels : function(lr, ps){
29010         if(typeof ps == 'string'){
29011             lr.add(new Roo.ContentPanel(ps));
29012         }
29013         else if(ps instanceof Array){
29014             for(var i =0, len = ps.length; i < len; i++){
29015                 this.addTypedPanels(lr, ps[i]);
29016             }
29017         }
29018         else if(!ps.events){ // raw config?
29019             var el = ps.el;
29020             delete ps.el; // prevent conflict
29021             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29022         }
29023         else {  // panel object assumed!
29024             lr.add(ps);
29025         }
29026     },
29027     /**
29028      * Adds a xtype elements to the layout.
29029      * <pre><code>
29030
29031 layout.addxtype({
29032        xtype : 'ContentPanel',
29033        region: 'west',
29034        items: [ .... ]
29035    }
29036 );
29037
29038 layout.addxtype({
29039         xtype : 'NestedLayoutPanel',
29040         region: 'west',
29041         layout: {
29042            center: { },
29043            west: { }   
29044         },
29045         items : [ ... list of content panels or nested layout panels.. ]
29046    }
29047 );
29048 </code></pre>
29049      * @param {Object} cfg Xtype definition of item to add.
29050      */
29051     addxtype : function(cfg)
29052     {
29053         // basically accepts a pannel...
29054         // can accept a layout region..!?!?
29055         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29056         
29057         if (!cfg.xtype.match(/Panel$/)) {
29058             return false;
29059         }
29060         var ret = false;
29061         
29062         if (typeof(cfg.region) == 'undefined') {
29063             Roo.log("Failed to add Panel, region was not set");
29064             Roo.log(cfg);
29065             return false;
29066         }
29067         var region = cfg.region;
29068         delete cfg.region;
29069         
29070           
29071         var xitems = [];
29072         if (cfg.items) {
29073             xitems = cfg.items;
29074             delete cfg.items;
29075         }
29076         var nb = false;
29077         
29078         switch(cfg.xtype) 
29079         {
29080             case 'ContentPanel':  // ContentPanel (el, cfg)
29081             case 'ScrollPanel':  // ContentPanel (el, cfg)
29082             case 'ViewPanel': 
29083                 if(cfg.autoCreate) {
29084                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29085                 } else {
29086                     var el = this.el.createChild();
29087                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29088                 }
29089                 
29090                 this.add(region, ret);
29091                 break;
29092             
29093             
29094             case 'TreePanel': // our new panel!
29095                 cfg.el = this.el.createChild();
29096                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29097                 this.add(region, ret);
29098                 break;
29099             
29100             case 'NestedLayoutPanel': 
29101                 // create a new Layout (which is  a Border Layout...
29102                 var el = this.el.createChild();
29103                 var clayout = cfg.layout;
29104                 delete cfg.layout;
29105                 clayout.items   = clayout.items  || [];
29106                 // replace this exitems with the clayout ones..
29107                 xitems = clayout.items;
29108                  
29109                 
29110                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29111                     cfg.background = false;
29112                 }
29113                 var layout = new Roo.BorderLayout(el, clayout);
29114                 
29115                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29116                 //console.log('adding nested layout panel '  + cfg.toSource());
29117                 this.add(region, ret);
29118                 nb = {}; /// find first...
29119                 break;
29120                 
29121             case 'GridPanel': 
29122             
29123                 // needs grid and region
29124                 
29125                 //var el = this.getRegion(region).el.createChild();
29126                 var el = this.el.createChild();
29127                 // create the grid first...
29128                 
29129                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29130                 delete cfg.grid;
29131                 if (region == 'center' && this.active ) {
29132                     cfg.background = false;
29133                 }
29134                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29135                 
29136                 this.add(region, ret);
29137                 if (cfg.background) {
29138                     ret.on('activate', function(gp) {
29139                         if (!gp.grid.rendered) {
29140                             gp.grid.render();
29141                         }
29142                     });
29143                 } else {
29144                     grid.render();
29145                 }
29146                 break;
29147            
29148            
29149            
29150                 
29151                 
29152                 
29153             default:
29154                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29155                     
29156                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29157                     this.add(region, ret);
29158                 } else {
29159                 
29160                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29161                     return null;
29162                 }
29163                 
29164              // GridPanel (grid, cfg)
29165             
29166         }
29167         this.beginUpdate();
29168         // add children..
29169         var region = '';
29170         var abn = {};
29171         Roo.each(xitems, function(i)  {
29172             region = nb && i.region ? i.region : false;
29173             
29174             var add = ret.addxtype(i);
29175            
29176             if (region) {
29177                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29178                 if (!i.background) {
29179                     abn[region] = nb[region] ;
29180                 }
29181             }
29182             
29183         });
29184         this.endUpdate();
29185
29186         // make the last non-background panel active..
29187         //if (nb) { Roo.log(abn); }
29188         if (nb) {
29189             
29190             for(var r in abn) {
29191                 region = this.getRegion(r);
29192                 if (region) {
29193                     // tried using nb[r], but it does not work..
29194                      
29195                     region.showPanel(abn[r]);
29196                    
29197                 }
29198             }
29199         }
29200         return ret;
29201         
29202     }
29203 });
29204
29205 /**
29206  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29207  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29208  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29209  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29210  * <pre><code>
29211 // shorthand
29212 var CP = Roo.ContentPanel;
29213
29214 var layout = Roo.BorderLayout.create({
29215     north: {
29216         initialSize: 25,
29217         titlebar: false,
29218         panels: [new CP("north", "North")]
29219     },
29220     west: {
29221         split:true,
29222         initialSize: 200,
29223         minSize: 175,
29224         maxSize: 400,
29225         titlebar: true,
29226         collapsible: true,
29227         panels: [new CP("west", {title: "West"})]
29228     },
29229     east: {
29230         split:true,
29231         initialSize: 202,
29232         minSize: 175,
29233         maxSize: 400,
29234         titlebar: true,
29235         collapsible: true,
29236         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29237     },
29238     south: {
29239         split:true,
29240         initialSize: 100,
29241         minSize: 100,
29242         maxSize: 200,
29243         titlebar: true,
29244         collapsible: true,
29245         panels: [new CP("south", {title: "South", closable: true})]
29246     },
29247     center: {
29248         titlebar: true,
29249         autoScroll:true,
29250         resizeTabs: true,
29251         minTabWidth: 50,
29252         preferredTabWidth: 150,
29253         panels: [
29254             new CP("center1", {title: "Close Me", closable: true}),
29255             new CP("center2", {title: "Center Panel", closable: false})
29256         ]
29257     }
29258 }, document.body);
29259
29260 layout.getRegion("center").showPanel("center1");
29261 </code></pre>
29262  * @param config
29263  * @param targetEl
29264  */
29265 Roo.BorderLayout.create = function(config, targetEl){
29266     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29267     layout.beginUpdate();
29268     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29269     for(var j = 0, jlen = regions.length; j < jlen; j++){
29270         var lr = regions[j];
29271         if(layout.regions[lr] && config[lr].panels){
29272             var r = layout.regions[lr];
29273             var ps = config[lr].panels;
29274             layout.addTypedPanels(r, ps);
29275         }
29276     }
29277     layout.endUpdate();
29278     return layout;
29279 };
29280
29281 // private
29282 Roo.BorderLayout.RegionFactory = {
29283     // private
29284     validRegions : ["north","south","east","west","center"],
29285
29286     // private
29287     create : function(target, mgr, config){
29288         target = target.toLowerCase();
29289         if(config.lightweight || config.basic){
29290             return new Roo.BasicLayoutRegion(mgr, config, target);
29291         }
29292         switch(target){
29293             case "north":
29294                 return new Roo.NorthLayoutRegion(mgr, config);
29295             case "south":
29296                 return new Roo.SouthLayoutRegion(mgr, config);
29297             case "east":
29298                 return new Roo.EastLayoutRegion(mgr, config);
29299             case "west":
29300                 return new Roo.WestLayoutRegion(mgr, config);
29301             case "center":
29302                 return new Roo.CenterLayoutRegion(mgr, config);
29303         }
29304         throw 'Layout region "'+target+'" not supported.';
29305     }
29306 };/*
29307  * Based on:
29308  * Ext JS Library 1.1.1
29309  * Copyright(c) 2006-2007, Ext JS, LLC.
29310  *
29311  * Originally Released Under LGPL - original licence link has changed is not relivant.
29312  *
29313  * Fork - LGPL
29314  * <script type="text/javascript">
29315  */
29316  
29317 /**
29318  * @class Roo.BasicLayoutRegion
29319  * @extends Roo.util.Observable
29320  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29321  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29322  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29323  */
29324 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29325     this.mgr = mgr;
29326     this.position  = pos;
29327     this.events = {
29328         /**
29329          * @scope Roo.BasicLayoutRegion
29330          */
29331         
29332         /**
29333          * @event beforeremove
29334          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29335          * @param {Roo.LayoutRegion} this
29336          * @param {Roo.ContentPanel} panel The panel
29337          * @param {Object} e The cancel event object
29338          */
29339         "beforeremove" : true,
29340         /**
29341          * @event invalidated
29342          * Fires when the layout for this region is changed.
29343          * @param {Roo.LayoutRegion} this
29344          */
29345         "invalidated" : true,
29346         /**
29347          * @event visibilitychange
29348          * Fires when this region is shown or hidden 
29349          * @param {Roo.LayoutRegion} this
29350          * @param {Boolean} visibility true or false
29351          */
29352         "visibilitychange" : true,
29353         /**
29354          * @event paneladded
29355          * Fires when a panel is added. 
29356          * @param {Roo.LayoutRegion} this
29357          * @param {Roo.ContentPanel} panel The panel
29358          */
29359         "paneladded" : true,
29360         /**
29361          * @event panelremoved
29362          * Fires when a panel is removed. 
29363          * @param {Roo.LayoutRegion} this
29364          * @param {Roo.ContentPanel} panel The panel
29365          */
29366         "panelremoved" : true,
29367         /**
29368          * @event beforecollapse
29369          * Fires when this region before collapse.
29370          * @param {Roo.LayoutRegion} this
29371          */
29372         "beforecollapse" : true,
29373         /**
29374          * @event collapsed
29375          * Fires when this region is collapsed.
29376          * @param {Roo.LayoutRegion} this
29377          */
29378         "collapsed" : true,
29379         /**
29380          * @event expanded
29381          * Fires when this region is expanded.
29382          * @param {Roo.LayoutRegion} this
29383          */
29384         "expanded" : true,
29385         /**
29386          * @event slideshow
29387          * Fires when this region is slid into view.
29388          * @param {Roo.LayoutRegion} this
29389          */
29390         "slideshow" : true,
29391         /**
29392          * @event slidehide
29393          * Fires when this region slides out of view. 
29394          * @param {Roo.LayoutRegion} this
29395          */
29396         "slidehide" : true,
29397         /**
29398          * @event panelactivated
29399          * Fires when a panel is activated. 
29400          * @param {Roo.LayoutRegion} this
29401          * @param {Roo.ContentPanel} panel The activated panel
29402          */
29403         "panelactivated" : true,
29404         /**
29405          * @event resized
29406          * Fires when the user resizes this region. 
29407          * @param {Roo.LayoutRegion} this
29408          * @param {Number} newSize The new size (width for east/west, height for north/south)
29409          */
29410         "resized" : true
29411     };
29412     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29413     this.panels = new Roo.util.MixedCollection();
29414     this.panels.getKey = this.getPanelId.createDelegate(this);
29415     this.box = null;
29416     this.activePanel = null;
29417     // ensure listeners are added...
29418     
29419     if (config.listeners || config.events) {
29420         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29421             listeners : config.listeners || {},
29422             events : config.events || {}
29423         });
29424     }
29425     
29426     if(skipConfig !== true){
29427         this.applyConfig(config);
29428     }
29429 };
29430
29431 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29432     getPanelId : function(p){
29433         return p.getId();
29434     },
29435     
29436     applyConfig : function(config){
29437         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29438         this.config = config;
29439         
29440     },
29441     
29442     /**
29443      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29444      * the width, for horizontal (north, south) the height.
29445      * @param {Number} newSize The new width or height
29446      */
29447     resizeTo : function(newSize){
29448         var el = this.el ? this.el :
29449                  (this.activePanel ? this.activePanel.getEl() : null);
29450         if(el){
29451             switch(this.position){
29452                 case "east":
29453                 case "west":
29454                     el.setWidth(newSize);
29455                     this.fireEvent("resized", this, newSize);
29456                 break;
29457                 case "north":
29458                 case "south":
29459                     el.setHeight(newSize);
29460                     this.fireEvent("resized", this, newSize);
29461                 break;                
29462             }
29463         }
29464     },
29465     
29466     getBox : function(){
29467         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29468     },
29469     
29470     getMargins : function(){
29471         return this.margins;
29472     },
29473     
29474     updateBox : function(box){
29475         this.box = box;
29476         var el = this.activePanel.getEl();
29477         el.dom.style.left = box.x + "px";
29478         el.dom.style.top = box.y + "px";
29479         this.activePanel.setSize(box.width, box.height);
29480     },
29481     
29482     /**
29483      * Returns the container element for this region.
29484      * @return {Roo.Element}
29485      */
29486     getEl : function(){
29487         return this.activePanel;
29488     },
29489     
29490     /**
29491      * Returns true if this region is currently visible.
29492      * @return {Boolean}
29493      */
29494     isVisible : function(){
29495         return this.activePanel ? true : false;
29496     },
29497     
29498     setActivePanel : function(panel){
29499         panel = this.getPanel(panel);
29500         if(this.activePanel && this.activePanel != panel){
29501             this.activePanel.setActiveState(false);
29502             this.activePanel.getEl().setLeftTop(-10000,-10000);
29503         }
29504         this.activePanel = panel;
29505         panel.setActiveState(true);
29506         if(this.box){
29507             panel.setSize(this.box.width, this.box.height);
29508         }
29509         this.fireEvent("panelactivated", this, panel);
29510         this.fireEvent("invalidated");
29511     },
29512     
29513     /**
29514      * Show the specified panel.
29515      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29516      * @return {Roo.ContentPanel} The shown panel or null
29517      */
29518     showPanel : function(panel){
29519         if(panel = this.getPanel(panel)){
29520             this.setActivePanel(panel);
29521         }
29522         return panel;
29523     },
29524     
29525     /**
29526      * Get the active panel for this region.
29527      * @return {Roo.ContentPanel} The active panel or null
29528      */
29529     getActivePanel : function(){
29530         return this.activePanel;
29531     },
29532     
29533     /**
29534      * Add the passed ContentPanel(s)
29535      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29536      * @return {Roo.ContentPanel} The panel added (if only one was added)
29537      */
29538     add : function(panel){
29539         if(arguments.length > 1){
29540             for(var i = 0, len = arguments.length; i < len; i++) {
29541                 this.add(arguments[i]);
29542             }
29543             return null;
29544         }
29545         if(this.hasPanel(panel)){
29546             this.showPanel(panel);
29547             return panel;
29548         }
29549         var el = panel.getEl();
29550         if(el.dom.parentNode != this.mgr.el.dom){
29551             this.mgr.el.dom.appendChild(el.dom);
29552         }
29553         if(panel.setRegion){
29554             panel.setRegion(this);
29555         }
29556         this.panels.add(panel);
29557         el.setStyle("position", "absolute");
29558         if(!panel.background){
29559             this.setActivePanel(panel);
29560             if(this.config.initialSize && this.panels.getCount()==1){
29561                 this.resizeTo(this.config.initialSize);
29562             }
29563         }
29564         this.fireEvent("paneladded", this, panel);
29565         return panel;
29566     },
29567     
29568     /**
29569      * Returns true if the panel is in this region.
29570      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29571      * @return {Boolean}
29572      */
29573     hasPanel : function(panel){
29574         if(typeof panel == "object"){ // must be panel obj
29575             panel = panel.getId();
29576         }
29577         return this.getPanel(panel) ? true : false;
29578     },
29579     
29580     /**
29581      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29582      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29583      * @param {Boolean} preservePanel Overrides the config preservePanel option
29584      * @return {Roo.ContentPanel} The panel that was removed
29585      */
29586     remove : function(panel, preservePanel){
29587         panel = this.getPanel(panel);
29588         if(!panel){
29589             return null;
29590         }
29591         var e = {};
29592         this.fireEvent("beforeremove", this, panel, e);
29593         if(e.cancel === true){
29594             return null;
29595         }
29596         var panelId = panel.getId();
29597         this.panels.removeKey(panelId);
29598         return panel;
29599     },
29600     
29601     /**
29602      * Returns the panel specified or null if it's not in this region.
29603      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29604      * @return {Roo.ContentPanel}
29605      */
29606     getPanel : function(id){
29607         if(typeof id == "object"){ // must be panel obj
29608             return id;
29609         }
29610         return this.panels.get(id);
29611     },
29612     
29613     /**
29614      * Returns this regions position (north/south/east/west/center).
29615      * @return {String} 
29616      */
29617     getPosition: function(){
29618         return this.position;    
29619     }
29620 });/*
29621  * Based on:
29622  * Ext JS Library 1.1.1
29623  * Copyright(c) 2006-2007, Ext JS, LLC.
29624  *
29625  * Originally Released Under LGPL - original licence link has changed is not relivant.
29626  *
29627  * Fork - LGPL
29628  * <script type="text/javascript">
29629  */
29630  
29631 /**
29632  * @class Roo.LayoutRegion
29633  * @extends Roo.BasicLayoutRegion
29634  * This class represents a region in a layout manager.
29635  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29636  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29637  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29638  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29639  * @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})
29640  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29641  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29642  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29643  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29644  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29645  * @cfg {String}    title           The title for the region (overrides panel titles)
29646  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29647  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29648  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29649  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29650  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29651  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29652  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29653  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29654  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29655  * @cfg {Boolean}   showPin         True to show a pin button
29656  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29657  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29658  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29659  * @cfg {Number}    width           For East/West panels
29660  * @cfg {Number}    height          For North/South panels
29661  * @cfg {Boolean}   split           To show the splitter
29662  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29663  */
29664 Roo.LayoutRegion = function(mgr, config, pos){
29665     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29666     var dh = Roo.DomHelper;
29667     /** This region's container element 
29668     * @type Roo.Element */
29669     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29670     /** This region's title element 
29671     * @type Roo.Element */
29672
29673     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29674         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29675         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29676     ]}, true);
29677     this.titleEl.enableDisplayMode();
29678     /** This region's title text element 
29679     * @type HTMLElement */
29680     this.titleTextEl = this.titleEl.dom.firstChild;
29681     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29682     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29683     this.closeBtn.enableDisplayMode();
29684     this.closeBtn.on("click", this.closeClicked, this);
29685     this.closeBtn.hide();
29686
29687     this.createBody(config);
29688     this.visible = true;
29689     this.collapsed = false;
29690
29691     if(config.hideWhenEmpty){
29692         this.hide();
29693         this.on("paneladded", this.validateVisibility, this);
29694         this.on("panelremoved", this.validateVisibility, this);
29695     }
29696     this.applyConfig(config);
29697 };
29698
29699 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29700
29701     createBody : function(){
29702         /** This region's body element 
29703         * @type Roo.Element */
29704         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29705     },
29706
29707     applyConfig : function(c){
29708         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29709             var dh = Roo.DomHelper;
29710             if(c.titlebar !== false){
29711                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29712                 this.collapseBtn.on("click", this.collapse, this);
29713                 this.collapseBtn.enableDisplayMode();
29714
29715                 if(c.showPin === true || this.showPin){
29716                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29717                     this.stickBtn.enableDisplayMode();
29718                     this.stickBtn.on("click", this.expand, this);
29719                     this.stickBtn.hide();
29720                 }
29721             }
29722             /** This region's collapsed element
29723             * @type Roo.Element */
29724             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29725                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29726             ]}, true);
29727             if(c.floatable !== false){
29728                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29729                this.collapsedEl.on("click", this.collapseClick, this);
29730             }
29731
29732             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29733                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29734                    id: "message", unselectable: "on", style:{"float":"left"}});
29735                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29736              }
29737             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29738             this.expandBtn.on("click", this.expand, this);
29739         }
29740         if(this.collapseBtn){
29741             this.collapseBtn.setVisible(c.collapsible == true);
29742         }
29743         this.cmargins = c.cmargins || this.cmargins ||
29744                          (this.position == "west" || this.position == "east" ?
29745                              {top: 0, left: 2, right:2, bottom: 0} :
29746                              {top: 2, left: 0, right:0, bottom: 2});
29747         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29748         this.bottomTabs = c.tabPosition != "top";
29749         this.autoScroll = c.autoScroll || false;
29750         if(this.autoScroll){
29751             this.bodyEl.setStyle("overflow", "auto");
29752         }else{
29753             this.bodyEl.setStyle("overflow", "hidden");
29754         }
29755         //if(c.titlebar !== false){
29756             if((!c.titlebar && !c.title) || c.titlebar === false){
29757                 this.titleEl.hide();
29758             }else{
29759                 this.titleEl.show();
29760                 if(c.title){
29761                     this.titleTextEl.innerHTML = c.title;
29762                 }
29763             }
29764         //}
29765         this.duration = c.duration || .30;
29766         this.slideDuration = c.slideDuration || .45;
29767         this.config = c;
29768         if(c.collapsed){
29769             this.collapse(true);
29770         }
29771         if(c.hidden){
29772             this.hide();
29773         }
29774     },
29775     /**
29776      * Returns true if this region is currently visible.
29777      * @return {Boolean}
29778      */
29779     isVisible : function(){
29780         return this.visible;
29781     },
29782
29783     /**
29784      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29785      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29786      */
29787     setCollapsedTitle : function(title){
29788         title = title || "&#160;";
29789         if(this.collapsedTitleTextEl){
29790             this.collapsedTitleTextEl.innerHTML = title;
29791         }
29792     },
29793
29794     getBox : function(){
29795         var b;
29796         if(!this.collapsed){
29797             b = this.el.getBox(false, true);
29798         }else{
29799             b = this.collapsedEl.getBox(false, true);
29800         }
29801         return b;
29802     },
29803
29804     getMargins : function(){
29805         return this.collapsed ? this.cmargins : this.margins;
29806     },
29807
29808     highlight : function(){
29809         this.el.addClass("x-layout-panel-dragover");
29810     },
29811
29812     unhighlight : function(){
29813         this.el.removeClass("x-layout-panel-dragover");
29814     },
29815
29816     updateBox : function(box){
29817         this.box = box;
29818         if(!this.collapsed){
29819             this.el.dom.style.left = box.x + "px";
29820             this.el.dom.style.top = box.y + "px";
29821             this.updateBody(box.width, box.height);
29822         }else{
29823             this.collapsedEl.dom.style.left = box.x + "px";
29824             this.collapsedEl.dom.style.top = box.y + "px";
29825             this.collapsedEl.setSize(box.width, box.height);
29826         }
29827         if(this.tabs){
29828             this.tabs.autoSizeTabs();
29829         }
29830     },
29831
29832     updateBody : function(w, h){
29833         if(w !== null){
29834             this.el.setWidth(w);
29835             w -= this.el.getBorderWidth("rl");
29836             if(this.config.adjustments){
29837                 w += this.config.adjustments[0];
29838             }
29839         }
29840         if(h !== null){
29841             this.el.setHeight(h);
29842             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29843             h -= this.el.getBorderWidth("tb");
29844             if(this.config.adjustments){
29845                 h += this.config.adjustments[1];
29846             }
29847             this.bodyEl.setHeight(h);
29848             if(this.tabs){
29849                 h = this.tabs.syncHeight(h);
29850             }
29851         }
29852         if(this.panelSize){
29853             w = w !== null ? w : this.panelSize.width;
29854             h = h !== null ? h : this.panelSize.height;
29855         }
29856         if(this.activePanel){
29857             var el = this.activePanel.getEl();
29858             w = w !== null ? w : el.getWidth();
29859             h = h !== null ? h : el.getHeight();
29860             this.panelSize = {width: w, height: h};
29861             this.activePanel.setSize(w, h);
29862         }
29863         if(Roo.isIE && this.tabs){
29864             this.tabs.el.repaint();
29865         }
29866     },
29867
29868     /**
29869      * Returns the container element for this region.
29870      * @return {Roo.Element}
29871      */
29872     getEl : function(){
29873         return this.el;
29874     },
29875
29876     /**
29877      * Hides this region.
29878      */
29879     hide : function(){
29880         if(!this.collapsed){
29881             this.el.dom.style.left = "-2000px";
29882             this.el.hide();
29883         }else{
29884             this.collapsedEl.dom.style.left = "-2000px";
29885             this.collapsedEl.hide();
29886         }
29887         this.visible = false;
29888         this.fireEvent("visibilitychange", this, false);
29889     },
29890
29891     /**
29892      * Shows this region if it was previously hidden.
29893      */
29894     show : function(){
29895         if(!this.collapsed){
29896             this.el.show();
29897         }else{
29898             this.collapsedEl.show();
29899         }
29900         this.visible = true;
29901         this.fireEvent("visibilitychange", this, true);
29902     },
29903
29904     closeClicked : function(){
29905         if(this.activePanel){
29906             this.remove(this.activePanel);
29907         }
29908     },
29909
29910     collapseClick : function(e){
29911         if(this.isSlid){
29912            e.stopPropagation();
29913            this.slideIn();
29914         }else{
29915            e.stopPropagation();
29916            this.slideOut();
29917         }
29918     },
29919
29920     /**
29921      * Collapses this region.
29922      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29923      */
29924     collapse : function(skipAnim, skipCheck = false){
29925         if(this.collapsed) {
29926             return;
29927         }
29928         
29929         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29930             
29931             this.collapsed = true;
29932             if(this.split){
29933                 this.split.el.hide();
29934             }
29935             if(this.config.animate && skipAnim !== true){
29936                 this.fireEvent("invalidated", this);
29937                 this.animateCollapse();
29938             }else{
29939                 this.el.setLocation(-20000,-20000);
29940                 this.el.hide();
29941                 this.collapsedEl.show();
29942                 this.fireEvent("collapsed", this);
29943                 this.fireEvent("invalidated", this);
29944             }
29945         }
29946         
29947     },
29948
29949     animateCollapse : function(){
29950         // overridden
29951     },
29952
29953     /**
29954      * Expands this region if it was previously collapsed.
29955      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29956      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29957      */
29958     expand : function(e, skipAnim){
29959         if(e) {
29960             e.stopPropagation();
29961         }
29962         if(!this.collapsed || this.el.hasActiveFx()) {
29963             return;
29964         }
29965         if(this.isSlid){
29966             this.afterSlideIn();
29967             skipAnim = true;
29968         }
29969         this.collapsed = false;
29970         if(this.config.animate && skipAnim !== true){
29971             this.animateExpand();
29972         }else{
29973             this.el.show();
29974             if(this.split){
29975                 this.split.el.show();
29976             }
29977             this.collapsedEl.setLocation(-2000,-2000);
29978             this.collapsedEl.hide();
29979             this.fireEvent("invalidated", this);
29980             this.fireEvent("expanded", this);
29981         }
29982     },
29983
29984     animateExpand : function(){
29985         // overridden
29986     },
29987
29988     initTabs : function()
29989     {
29990         this.bodyEl.setStyle("overflow", "hidden");
29991         var ts = new Roo.TabPanel(
29992                 this.bodyEl.dom,
29993                 {
29994                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29995                     disableTooltips: this.config.disableTabTips,
29996                     toolbar : this.config.toolbar
29997                 }
29998         );
29999         if(this.config.hideTabs){
30000             ts.stripWrap.setDisplayed(false);
30001         }
30002         this.tabs = ts;
30003         ts.resizeTabs = this.config.resizeTabs === true;
30004         ts.minTabWidth = this.config.minTabWidth || 40;
30005         ts.maxTabWidth = this.config.maxTabWidth || 250;
30006         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30007         ts.monitorResize = false;
30008         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30009         ts.bodyEl.addClass('x-layout-tabs-body');
30010         this.panels.each(this.initPanelAsTab, this);
30011     },
30012
30013     initPanelAsTab : function(panel){
30014         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30015                     this.config.closeOnTab && panel.isClosable());
30016         if(panel.tabTip !== undefined){
30017             ti.setTooltip(panel.tabTip);
30018         }
30019         ti.on("activate", function(){
30020               this.setActivePanel(panel);
30021         }, this);
30022         if(this.config.closeOnTab){
30023             ti.on("beforeclose", function(t, e){
30024                 e.cancel = true;
30025                 this.remove(panel);
30026             }, this);
30027         }
30028         return ti;
30029     },
30030
30031     updatePanelTitle : function(panel, title){
30032         if(this.activePanel == panel){
30033             this.updateTitle(title);
30034         }
30035         if(this.tabs){
30036             var ti = this.tabs.getTab(panel.getEl().id);
30037             ti.setText(title);
30038             if(panel.tabTip !== undefined){
30039                 ti.setTooltip(panel.tabTip);
30040             }
30041         }
30042     },
30043
30044     updateTitle : function(title){
30045         if(this.titleTextEl && !this.config.title){
30046             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30047         }
30048     },
30049
30050     setActivePanel : function(panel){
30051         panel = this.getPanel(panel);
30052         if(this.activePanel && this.activePanel != panel){
30053             this.activePanel.setActiveState(false);
30054         }
30055         this.activePanel = panel;
30056         panel.setActiveState(true);
30057         if(this.panelSize){
30058             panel.setSize(this.panelSize.width, this.panelSize.height);
30059         }
30060         if(this.closeBtn){
30061             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30062         }
30063         this.updateTitle(panel.getTitle());
30064         if(this.tabs){
30065             this.fireEvent("invalidated", this);
30066         }
30067         this.fireEvent("panelactivated", this, panel);
30068     },
30069
30070     /**
30071      * Shows the specified panel.
30072      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30073      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30074      */
30075     showPanel : function(panel)
30076     {
30077         panel = this.getPanel(panel);
30078         if(panel){
30079             if(this.tabs){
30080                 var tab = this.tabs.getTab(panel.getEl().id);
30081                 if(tab.isHidden()){
30082                     this.tabs.unhideTab(tab.id);
30083                 }
30084                 tab.activate();
30085             }else{
30086                 this.setActivePanel(panel);
30087             }
30088         }
30089         return panel;
30090     },
30091
30092     /**
30093      * Get the active panel for this region.
30094      * @return {Roo.ContentPanel} The active panel or null
30095      */
30096     getActivePanel : function(){
30097         return this.activePanel;
30098     },
30099
30100     validateVisibility : function(){
30101         if(this.panels.getCount() < 1){
30102             this.updateTitle("&#160;");
30103             this.closeBtn.hide();
30104             this.hide();
30105         }else{
30106             if(!this.isVisible()){
30107                 this.show();
30108             }
30109         }
30110     },
30111
30112     /**
30113      * Adds the passed ContentPanel(s) to this region.
30114      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30115      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30116      */
30117     add : function(panel){
30118         if(arguments.length > 1){
30119             for(var i = 0, len = arguments.length; i < len; i++) {
30120                 this.add(arguments[i]);
30121             }
30122             return null;
30123         }
30124         if(this.hasPanel(panel)){
30125             this.showPanel(panel);
30126             return panel;
30127         }
30128         panel.setRegion(this);
30129         this.panels.add(panel);
30130         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30131             this.bodyEl.dom.appendChild(panel.getEl().dom);
30132             if(panel.background !== true){
30133                 this.setActivePanel(panel);
30134             }
30135             this.fireEvent("paneladded", this, panel);
30136             return panel;
30137         }
30138         if(!this.tabs){
30139             this.initTabs();
30140         }else{
30141             this.initPanelAsTab(panel);
30142         }
30143         if(panel.background !== true){
30144             this.tabs.activate(panel.getEl().id);
30145         }
30146         this.fireEvent("paneladded", this, panel);
30147         return panel;
30148     },
30149
30150     /**
30151      * Hides the tab for the specified panel.
30152      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30153      */
30154     hidePanel : function(panel){
30155         if(this.tabs && (panel = this.getPanel(panel))){
30156             this.tabs.hideTab(panel.getEl().id);
30157         }
30158     },
30159
30160     /**
30161      * Unhides the tab for a previously hidden panel.
30162      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30163      */
30164     unhidePanel : function(panel){
30165         if(this.tabs && (panel = this.getPanel(panel))){
30166             this.tabs.unhideTab(panel.getEl().id);
30167         }
30168     },
30169
30170     clearPanels : function(){
30171         while(this.panels.getCount() > 0){
30172              this.remove(this.panels.first());
30173         }
30174     },
30175
30176     /**
30177      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30178      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30179      * @param {Boolean} preservePanel Overrides the config preservePanel option
30180      * @return {Roo.ContentPanel} The panel that was removed
30181      */
30182     remove : function(panel, preservePanel){
30183         panel = this.getPanel(panel);
30184         if(!panel){
30185             return null;
30186         }
30187         var e = {};
30188         this.fireEvent("beforeremove", this, panel, e);
30189         if(e.cancel === true){
30190             return null;
30191         }
30192         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30193         var panelId = panel.getId();
30194         this.panels.removeKey(panelId);
30195         if(preservePanel){
30196             document.body.appendChild(panel.getEl().dom);
30197         }
30198         if(this.tabs){
30199             this.tabs.removeTab(panel.getEl().id);
30200         }else if (!preservePanel){
30201             this.bodyEl.dom.removeChild(panel.getEl().dom);
30202         }
30203         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30204             var p = this.panels.first();
30205             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30206             tempEl.appendChild(p.getEl().dom);
30207             this.bodyEl.update("");
30208             this.bodyEl.dom.appendChild(p.getEl().dom);
30209             tempEl = null;
30210             this.updateTitle(p.getTitle());
30211             this.tabs = null;
30212             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30213             this.setActivePanel(p);
30214         }
30215         panel.setRegion(null);
30216         if(this.activePanel == panel){
30217             this.activePanel = null;
30218         }
30219         if(this.config.autoDestroy !== false && preservePanel !== true){
30220             try{panel.destroy();}catch(e){}
30221         }
30222         this.fireEvent("panelremoved", this, panel);
30223         return panel;
30224     },
30225
30226     /**
30227      * Returns the TabPanel component used by this region
30228      * @return {Roo.TabPanel}
30229      */
30230     getTabs : function(){
30231         return this.tabs;
30232     },
30233
30234     createTool : function(parentEl, className){
30235         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30236             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30237         btn.addClassOnOver("x-layout-tools-button-over");
30238         return btn;
30239     }
30240 });/*
30241  * Based on:
30242  * Ext JS Library 1.1.1
30243  * Copyright(c) 2006-2007, Ext JS, LLC.
30244  *
30245  * Originally Released Under LGPL - original licence link has changed is not relivant.
30246  *
30247  * Fork - LGPL
30248  * <script type="text/javascript">
30249  */
30250  
30251
30252
30253 /**
30254  * @class Roo.SplitLayoutRegion
30255  * @extends Roo.LayoutRegion
30256  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30257  */
30258 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30259     this.cursor = cursor;
30260     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30261 };
30262
30263 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30264     splitTip : "Drag to resize.",
30265     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30266     useSplitTips : false,
30267
30268     applyConfig : function(config){
30269         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30270         if(config.split){
30271             if(!this.split){
30272                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30273                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30274                 /** The SplitBar for this region 
30275                 * @type Roo.SplitBar */
30276                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30277                 this.split.on("moved", this.onSplitMove, this);
30278                 this.split.useShim = config.useShim === true;
30279                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30280                 if(this.useSplitTips){
30281                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30282                 }
30283                 if(config.collapsible){
30284                     this.split.el.on("dblclick", this.collapse,  this);
30285                 }
30286             }
30287             if(typeof config.minSize != "undefined"){
30288                 this.split.minSize = config.minSize;
30289             }
30290             if(typeof config.maxSize != "undefined"){
30291                 this.split.maxSize = config.maxSize;
30292             }
30293             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30294                 this.hideSplitter();
30295             }
30296         }
30297     },
30298
30299     getHMaxSize : function(){
30300          var cmax = this.config.maxSize || 10000;
30301          var center = this.mgr.getRegion("center");
30302          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30303     },
30304
30305     getVMaxSize : function(){
30306          var cmax = this.config.maxSize || 10000;
30307          var center = this.mgr.getRegion("center");
30308          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30309     },
30310
30311     onSplitMove : function(split, newSize){
30312         this.fireEvent("resized", this, newSize);
30313     },
30314     
30315     /** 
30316      * Returns the {@link Roo.SplitBar} for this region.
30317      * @return {Roo.SplitBar}
30318      */
30319     getSplitBar : function(){
30320         return this.split;
30321     },
30322     
30323     hide : function(){
30324         this.hideSplitter();
30325         Roo.SplitLayoutRegion.superclass.hide.call(this);
30326     },
30327
30328     hideSplitter : function(){
30329         if(this.split){
30330             this.split.el.setLocation(-2000,-2000);
30331             this.split.el.hide();
30332         }
30333     },
30334
30335     show : function(){
30336         if(this.split){
30337             this.split.el.show();
30338         }
30339         Roo.SplitLayoutRegion.superclass.show.call(this);
30340     },
30341     
30342     beforeSlide: function(){
30343         if(Roo.isGecko){// firefox overflow auto bug workaround
30344             this.bodyEl.clip();
30345             if(this.tabs) {
30346                 this.tabs.bodyEl.clip();
30347             }
30348             if(this.activePanel){
30349                 this.activePanel.getEl().clip();
30350                 
30351                 if(this.activePanel.beforeSlide){
30352                     this.activePanel.beforeSlide();
30353                 }
30354             }
30355         }
30356     },
30357     
30358     afterSlide : function(){
30359         if(Roo.isGecko){// firefox overflow auto bug workaround
30360             this.bodyEl.unclip();
30361             if(this.tabs) {
30362                 this.tabs.bodyEl.unclip();
30363             }
30364             if(this.activePanel){
30365                 this.activePanel.getEl().unclip();
30366                 if(this.activePanel.afterSlide){
30367                     this.activePanel.afterSlide();
30368                 }
30369             }
30370         }
30371     },
30372
30373     initAutoHide : function(){
30374         if(this.autoHide !== false){
30375             if(!this.autoHideHd){
30376                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30377                 this.autoHideHd = {
30378                     "mouseout": function(e){
30379                         if(!e.within(this.el, true)){
30380                             st.delay(500);
30381                         }
30382                     },
30383                     "mouseover" : function(e){
30384                         st.cancel();
30385                     },
30386                     scope : this
30387                 };
30388             }
30389             this.el.on(this.autoHideHd);
30390         }
30391     },
30392
30393     clearAutoHide : function(){
30394         if(this.autoHide !== false){
30395             this.el.un("mouseout", this.autoHideHd.mouseout);
30396             this.el.un("mouseover", this.autoHideHd.mouseover);
30397         }
30398     },
30399
30400     clearMonitor : function(){
30401         Roo.get(document).un("click", this.slideInIf, this);
30402     },
30403
30404     // these names are backwards but not changed for compat
30405     slideOut : function(){
30406         if(this.isSlid || this.el.hasActiveFx()){
30407             return;
30408         }
30409         this.isSlid = true;
30410         if(this.collapseBtn){
30411             this.collapseBtn.hide();
30412         }
30413         this.closeBtnState = this.closeBtn.getStyle('display');
30414         this.closeBtn.hide();
30415         if(this.stickBtn){
30416             this.stickBtn.show();
30417         }
30418         this.el.show();
30419         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30420         this.beforeSlide();
30421         this.el.setStyle("z-index", 10001);
30422         this.el.slideIn(this.getSlideAnchor(), {
30423             callback: function(){
30424                 this.afterSlide();
30425                 this.initAutoHide();
30426                 Roo.get(document).on("click", this.slideInIf, this);
30427                 this.fireEvent("slideshow", this);
30428             },
30429             scope: this,
30430             block: true
30431         });
30432     },
30433
30434     afterSlideIn : function(){
30435         this.clearAutoHide();
30436         this.isSlid = false;
30437         this.clearMonitor();
30438         this.el.setStyle("z-index", "");
30439         if(this.collapseBtn){
30440             this.collapseBtn.show();
30441         }
30442         this.closeBtn.setStyle('display', this.closeBtnState);
30443         if(this.stickBtn){
30444             this.stickBtn.hide();
30445         }
30446         this.fireEvent("slidehide", this);
30447     },
30448
30449     slideIn : function(cb){
30450         if(!this.isSlid || this.el.hasActiveFx()){
30451             Roo.callback(cb);
30452             return;
30453         }
30454         this.isSlid = false;
30455         this.beforeSlide();
30456         this.el.slideOut(this.getSlideAnchor(), {
30457             callback: function(){
30458                 this.el.setLeftTop(-10000, -10000);
30459                 this.afterSlide();
30460                 this.afterSlideIn();
30461                 Roo.callback(cb);
30462             },
30463             scope: this,
30464             block: true
30465         });
30466     },
30467     
30468     slideInIf : function(e){
30469         if(!e.within(this.el)){
30470             this.slideIn();
30471         }
30472     },
30473
30474     animateCollapse : function(){
30475         this.beforeSlide();
30476         this.el.setStyle("z-index", 20000);
30477         var anchor = this.getSlideAnchor();
30478         this.el.slideOut(anchor, {
30479             callback : function(){
30480                 this.el.setStyle("z-index", "");
30481                 this.collapsedEl.slideIn(anchor, {duration:.3});
30482                 this.afterSlide();
30483                 this.el.setLocation(-10000,-10000);
30484                 this.el.hide();
30485                 this.fireEvent("collapsed", this);
30486             },
30487             scope: this,
30488             block: true
30489         });
30490     },
30491
30492     animateExpand : function(){
30493         this.beforeSlide();
30494         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30495         this.el.setStyle("z-index", 20000);
30496         this.collapsedEl.hide({
30497             duration:.1
30498         });
30499         this.el.slideIn(this.getSlideAnchor(), {
30500             callback : function(){
30501                 this.el.setStyle("z-index", "");
30502                 this.afterSlide();
30503                 if(this.split){
30504                     this.split.el.show();
30505                 }
30506                 this.fireEvent("invalidated", this);
30507                 this.fireEvent("expanded", this);
30508             },
30509             scope: this,
30510             block: true
30511         });
30512     },
30513
30514     anchors : {
30515         "west" : "left",
30516         "east" : "right",
30517         "north" : "top",
30518         "south" : "bottom"
30519     },
30520
30521     sanchors : {
30522         "west" : "l",
30523         "east" : "r",
30524         "north" : "t",
30525         "south" : "b"
30526     },
30527
30528     canchors : {
30529         "west" : "tl-tr",
30530         "east" : "tr-tl",
30531         "north" : "tl-bl",
30532         "south" : "bl-tl"
30533     },
30534
30535     getAnchor : function(){
30536         return this.anchors[this.position];
30537     },
30538
30539     getCollapseAnchor : function(){
30540         return this.canchors[this.position];
30541     },
30542
30543     getSlideAnchor : function(){
30544         return this.sanchors[this.position];
30545     },
30546
30547     getAlignAdj : function(){
30548         var cm = this.cmargins;
30549         switch(this.position){
30550             case "west":
30551                 return [0, 0];
30552             break;
30553             case "east":
30554                 return [0, 0];
30555             break;
30556             case "north":
30557                 return [0, 0];
30558             break;
30559             case "south":
30560                 return [0, 0];
30561             break;
30562         }
30563     },
30564
30565     getExpandAdj : function(){
30566         var c = this.collapsedEl, cm = this.cmargins;
30567         switch(this.position){
30568             case "west":
30569                 return [-(cm.right+c.getWidth()+cm.left), 0];
30570             break;
30571             case "east":
30572                 return [cm.right+c.getWidth()+cm.left, 0];
30573             break;
30574             case "north":
30575                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30576             break;
30577             case "south":
30578                 return [0, cm.top+cm.bottom+c.getHeight()];
30579             break;
30580         }
30581     }
30582 });/*
30583  * Based on:
30584  * Ext JS Library 1.1.1
30585  * Copyright(c) 2006-2007, Ext JS, LLC.
30586  *
30587  * Originally Released Under LGPL - original licence link has changed is not relivant.
30588  *
30589  * Fork - LGPL
30590  * <script type="text/javascript">
30591  */
30592 /*
30593  * These classes are private internal classes
30594  */
30595 Roo.CenterLayoutRegion = function(mgr, config){
30596     Roo.LayoutRegion.call(this, mgr, config, "center");
30597     this.visible = true;
30598     this.minWidth = config.minWidth || 20;
30599     this.minHeight = config.minHeight || 20;
30600 };
30601
30602 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30603     hide : function(){
30604         // center panel can't be hidden
30605     },
30606     
30607     show : function(){
30608         // center panel can't be hidden
30609     },
30610     
30611     getMinWidth: function(){
30612         return this.minWidth;
30613     },
30614     
30615     getMinHeight: function(){
30616         return this.minHeight;
30617     }
30618 });
30619
30620
30621 Roo.NorthLayoutRegion = function(mgr, config){
30622     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30623     if(this.split){
30624         this.split.placement = Roo.SplitBar.TOP;
30625         this.split.orientation = Roo.SplitBar.VERTICAL;
30626         this.split.el.addClass("x-layout-split-v");
30627     }
30628     var size = config.initialSize || config.height;
30629     if(typeof size != "undefined"){
30630         this.el.setHeight(size);
30631     }
30632 };
30633 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30634     orientation: Roo.SplitBar.VERTICAL,
30635     getBox : function(){
30636         if(this.collapsed){
30637             return this.collapsedEl.getBox();
30638         }
30639         var box = this.el.getBox();
30640         if(this.split){
30641             box.height += this.split.el.getHeight();
30642         }
30643         return box;
30644     },
30645     
30646     updateBox : function(box){
30647         if(this.split && !this.collapsed){
30648             box.height -= this.split.el.getHeight();
30649             this.split.el.setLeft(box.x);
30650             this.split.el.setTop(box.y+box.height);
30651             this.split.el.setWidth(box.width);
30652         }
30653         if(this.collapsed){
30654             this.updateBody(box.width, null);
30655         }
30656         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30657     }
30658 });
30659
30660 Roo.SouthLayoutRegion = function(mgr, config){
30661     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30662     if(this.split){
30663         this.split.placement = Roo.SplitBar.BOTTOM;
30664         this.split.orientation = Roo.SplitBar.VERTICAL;
30665         this.split.el.addClass("x-layout-split-v");
30666     }
30667     var size = config.initialSize || config.height;
30668     if(typeof size != "undefined"){
30669         this.el.setHeight(size);
30670     }
30671 };
30672 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30673     orientation: Roo.SplitBar.VERTICAL,
30674     getBox : function(){
30675         if(this.collapsed){
30676             return this.collapsedEl.getBox();
30677         }
30678         var box = this.el.getBox();
30679         if(this.split){
30680             var sh = this.split.el.getHeight();
30681             box.height += sh;
30682             box.y -= sh;
30683         }
30684         return box;
30685     },
30686     
30687     updateBox : function(box){
30688         if(this.split && !this.collapsed){
30689             var sh = this.split.el.getHeight();
30690             box.height -= sh;
30691             box.y += sh;
30692             this.split.el.setLeft(box.x);
30693             this.split.el.setTop(box.y-sh);
30694             this.split.el.setWidth(box.width);
30695         }
30696         if(this.collapsed){
30697             this.updateBody(box.width, null);
30698         }
30699         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30700     }
30701 });
30702
30703 Roo.EastLayoutRegion = function(mgr, config){
30704     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30705     if(this.split){
30706         this.split.placement = Roo.SplitBar.RIGHT;
30707         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30708         this.split.el.addClass("x-layout-split-h");
30709     }
30710     var size = config.initialSize || config.width;
30711     if(typeof size != "undefined"){
30712         this.el.setWidth(size);
30713     }
30714 };
30715 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30716     orientation: Roo.SplitBar.HORIZONTAL,
30717     getBox : function(){
30718         if(this.collapsed){
30719             return this.collapsedEl.getBox();
30720         }
30721         var box = this.el.getBox();
30722         if(this.split){
30723             var sw = this.split.el.getWidth();
30724             box.width += sw;
30725             box.x -= sw;
30726         }
30727         return box;
30728     },
30729
30730     updateBox : function(box){
30731         if(this.split && !this.collapsed){
30732             var sw = this.split.el.getWidth();
30733             box.width -= sw;
30734             this.split.el.setLeft(box.x);
30735             this.split.el.setTop(box.y);
30736             this.split.el.setHeight(box.height);
30737             box.x += sw;
30738         }
30739         if(this.collapsed){
30740             this.updateBody(null, box.height);
30741         }
30742         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30743     }
30744 });
30745
30746 Roo.WestLayoutRegion = function(mgr, config){
30747     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30748     if(this.split){
30749         this.split.placement = Roo.SplitBar.LEFT;
30750         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30751         this.split.el.addClass("x-layout-split-h");
30752     }
30753     var size = config.initialSize || config.width;
30754     if(typeof size != "undefined"){
30755         this.el.setWidth(size);
30756     }
30757 };
30758 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30759     orientation: Roo.SplitBar.HORIZONTAL,
30760     getBox : function(){
30761         if(this.collapsed){
30762             return this.collapsedEl.getBox();
30763         }
30764         var box = this.el.getBox();
30765         if(this.split){
30766             box.width += this.split.el.getWidth();
30767         }
30768         return box;
30769     },
30770     
30771     updateBox : function(box){
30772         if(this.split && !this.collapsed){
30773             var sw = this.split.el.getWidth();
30774             box.width -= sw;
30775             this.split.el.setLeft(box.x+box.width);
30776             this.split.el.setTop(box.y);
30777             this.split.el.setHeight(box.height);
30778         }
30779         if(this.collapsed){
30780             this.updateBody(null, box.height);
30781         }
30782         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30783     }
30784 });
30785 /*
30786  * Based on:
30787  * Ext JS Library 1.1.1
30788  * Copyright(c) 2006-2007, Ext JS, LLC.
30789  *
30790  * Originally Released Under LGPL - original licence link has changed is not relivant.
30791  *
30792  * Fork - LGPL
30793  * <script type="text/javascript">
30794  */
30795  
30796  
30797 /*
30798  * Private internal class for reading and applying state
30799  */
30800 Roo.LayoutStateManager = function(layout){
30801      // default empty state
30802      this.state = {
30803         north: {},
30804         south: {},
30805         east: {},
30806         west: {}       
30807     };
30808 };
30809
30810 Roo.LayoutStateManager.prototype = {
30811     init : function(layout, provider){
30812         this.provider = provider;
30813         var state = provider.get(layout.id+"-layout-state");
30814         if(state){
30815             var wasUpdating = layout.isUpdating();
30816             if(!wasUpdating){
30817                 layout.beginUpdate();
30818             }
30819             for(var key in state){
30820                 if(typeof state[key] != "function"){
30821                     var rstate = state[key];
30822                     var r = layout.getRegion(key);
30823                     if(r && rstate){
30824                         if(rstate.size){
30825                             r.resizeTo(rstate.size);
30826                         }
30827                         if(rstate.collapsed == true){
30828                             r.collapse(true);
30829                         }else{
30830                             r.expand(null, true);
30831                         }
30832                     }
30833                 }
30834             }
30835             if(!wasUpdating){
30836                 layout.endUpdate();
30837             }
30838             this.state = state; 
30839         }
30840         this.layout = layout;
30841         layout.on("regionresized", this.onRegionResized, this);
30842         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30843         layout.on("regionexpanded", this.onRegionExpanded, this);
30844     },
30845     
30846     storeState : function(){
30847         this.provider.set(this.layout.id+"-layout-state", this.state);
30848     },
30849     
30850     onRegionResized : function(region, newSize){
30851         this.state[region.getPosition()].size = newSize;
30852         this.storeState();
30853     },
30854     
30855     onRegionCollapsed : function(region){
30856         this.state[region.getPosition()].collapsed = true;
30857         this.storeState();
30858     },
30859     
30860     onRegionExpanded : function(region){
30861         this.state[region.getPosition()].collapsed = false;
30862         this.storeState();
30863     }
30864 };/*
30865  * Based on:
30866  * Ext JS Library 1.1.1
30867  * Copyright(c) 2006-2007, Ext JS, LLC.
30868  *
30869  * Originally Released Under LGPL - original licence link has changed is not relivant.
30870  *
30871  * Fork - LGPL
30872  * <script type="text/javascript">
30873  */
30874 /**
30875  * @class Roo.ContentPanel
30876  * @extends Roo.util.Observable
30877  * A basic ContentPanel element.
30878  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30879  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30880  * @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
30881  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30882  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30883  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30884  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30885  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30886  * @cfg {String} title          The title for this panel
30887  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30888  * @cfg {String} url            Calls {@link #setUrl} with this value
30889  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30890  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30891  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30892  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30893
30894  * @constructor
30895  * Create a new ContentPanel.
30896  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30897  * @param {String/Object} config A string to set only the title or a config object
30898  * @param {String} content (optional) Set the HTML content for this panel
30899  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30900  */
30901 Roo.ContentPanel = function(el, config, content){
30902     
30903      
30904     /*
30905     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30906         config = el;
30907         el = Roo.id();
30908     }
30909     if (config && config.parentLayout) { 
30910         el = config.parentLayout.el.createChild(); 
30911     }
30912     */
30913     if(el.autoCreate){ // xtype is available if this is called from factory
30914         config = el;
30915         el = Roo.id();
30916     }
30917     this.el = Roo.get(el);
30918     if(!this.el && config && config.autoCreate){
30919         if(typeof config.autoCreate == "object"){
30920             if(!config.autoCreate.id){
30921                 config.autoCreate.id = config.id||el;
30922             }
30923             this.el = Roo.DomHelper.append(document.body,
30924                         config.autoCreate, true);
30925         }else{
30926             this.el = Roo.DomHelper.append(document.body,
30927                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30928         }
30929     }
30930     this.closable = false;
30931     this.loaded = false;
30932     this.active = false;
30933     if(typeof config == "string"){
30934         this.title = config;
30935     }else{
30936         Roo.apply(this, config);
30937     }
30938     
30939     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30940         this.wrapEl = this.el.wrap();
30941         this.toolbar.container = this.el.insertSibling(false, 'before');
30942         this.toolbar = new Roo.Toolbar(this.toolbar);
30943     }
30944     
30945     // xtype created footer. - not sure if will work as we normally have to render first..
30946     if (this.footer && !this.footer.el && this.footer.xtype) {
30947         if (!this.wrapEl) {
30948             this.wrapEl = this.el.wrap();
30949         }
30950     
30951         this.footer.container = this.wrapEl.createChild();
30952          
30953         this.footer = Roo.factory(this.footer, Roo);
30954         
30955     }
30956     
30957     if(this.resizeEl){
30958         this.resizeEl = Roo.get(this.resizeEl, true);
30959     }else{
30960         this.resizeEl = this.el;
30961     }
30962     // handle view.xtype
30963     
30964  
30965     
30966     
30967     this.addEvents({
30968         /**
30969          * @event activate
30970          * Fires when this panel is activated. 
30971          * @param {Roo.ContentPanel} this
30972          */
30973         "activate" : true,
30974         /**
30975          * @event deactivate
30976          * Fires when this panel is activated. 
30977          * @param {Roo.ContentPanel} this
30978          */
30979         "deactivate" : true,
30980
30981         /**
30982          * @event resize
30983          * Fires when this panel is resized if fitToFrame is true.
30984          * @param {Roo.ContentPanel} this
30985          * @param {Number} width The width after any component adjustments
30986          * @param {Number} height The height after any component adjustments
30987          */
30988         "resize" : true,
30989         
30990          /**
30991          * @event render
30992          * Fires when this tab is created
30993          * @param {Roo.ContentPanel} this
30994          */
30995         "render" : true
30996          
30997         
30998     });
30999     
31000
31001     
31002     
31003     if(this.autoScroll){
31004         this.resizeEl.setStyle("overflow", "auto");
31005     } else {
31006         // fix randome scrolling
31007         this.el.on('scroll', function() {
31008             Roo.log('fix random scolling');
31009             this.scrollTo('top',0); 
31010         });
31011     }
31012     content = content || this.content;
31013     if(content){
31014         this.setContent(content);
31015     }
31016     if(config && config.url){
31017         this.setUrl(this.url, this.params, this.loadOnce);
31018     }
31019     
31020     
31021     
31022     Roo.ContentPanel.superclass.constructor.call(this);
31023     
31024     if (this.view && typeof(this.view.xtype) != 'undefined') {
31025         this.view.el = this.el.appendChild(document.createElement("div"));
31026         this.view = Roo.factory(this.view); 
31027         this.view.render  &&  this.view.render(false, '');  
31028     }
31029     
31030     
31031     this.fireEvent('render', this);
31032 };
31033
31034 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31035     tabTip:'',
31036     setRegion : function(region){
31037         this.region = region;
31038         if(region){
31039            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31040         }else{
31041            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31042         } 
31043     },
31044     
31045     /**
31046      * Returns the toolbar for this Panel if one was configured. 
31047      * @return {Roo.Toolbar} 
31048      */
31049     getToolbar : function(){
31050         return this.toolbar;
31051     },
31052     
31053     setActiveState : function(active){
31054         this.active = active;
31055         if(!active){
31056             this.fireEvent("deactivate", this);
31057         }else{
31058             this.fireEvent("activate", this);
31059         }
31060     },
31061     /**
31062      * Updates this panel's element
31063      * @param {String} content The new content
31064      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31065     */
31066     setContent : function(content, loadScripts){
31067         this.el.update(content, loadScripts);
31068     },
31069
31070     ignoreResize : function(w, h){
31071         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31072             return true;
31073         }else{
31074             this.lastSize = {width: w, height: h};
31075             return false;
31076         }
31077     },
31078     /**
31079      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31080      * @return {Roo.UpdateManager} The UpdateManager
31081      */
31082     getUpdateManager : function(){
31083         return this.el.getUpdateManager();
31084     },
31085      /**
31086      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31087      * @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:
31088 <pre><code>
31089 panel.load({
31090     url: "your-url.php",
31091     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31092     callback: yourFunction,
31093     scope: yourObject, //(optional scope)
31094     discardUrl: false,
31095     nocache: false,
31096     text: "Loading...",
31097     timeout: 30,
31098     scripts: false
31099 });
31100 </code></pre>
31101      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31102      * 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.
31103      * @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}
31104      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31105      * @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.
31106      * @return {Roo.ContentPanel} this
31107      */
31108     load : function(){
31109         var um = this.el.getUpdateManager();
31110         um.update.apply(um, arguments);
31111         return this;
31112     },
31113
31114
31115     /**
31116      * 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.
31117      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31118      * @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)
31119      * @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)
31120      * @return {Roo.UpdateManager} The UpdateManager
31121      */
31122     setUrl : function(url, params, loadOnce){
31123         if(this.refreshDelegate){
31124             this.removeListener("activate", this.refreshDelegate);
31125         }
31126         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31127         this.on("activate", this.refreshDelegate);
31128         return this.el.getUpdateManager();
31129     },
31130     
31131     _handleRefresh : function(url, params, loadOnce){
31132         if(!loadOnce || !this.loaded){
31133             var updater = this.el.getUpdateManager();
31134             updater.update(url, params, this._setLoaded.createDelegate(this));
31135         }
31136     },
31137     
31138     _setLoaded : function(){
31139         this.loaded = true;
31140     }, 
31141     
31142     /**
31143      * Returns this panel's id
31144      * @return {String} 
31145      */
31146     getId : function(){
31147         return this.el.id;
31148     },
31149     
31150     /** 
31151      * Returns this panel's element - used by regiosn to add.
31152      * @return {Roo.Element} 
31153      */
31154     getEl : function(){
31155         return this.wrapEl || this.el;
31156     },
31157     
31158     adjustForComponents : function(width, height)
31159     {
31160         //Roo.log('adjustForComponents ');
31161         if(this.resizeEl != this.el){
31162             width -= this.el.getFrameWidth('lr');
31163             height -= this.el.getFrameWidth('tb');
31164         }
31165         if(this.toolbar){
31166             var te = this.toolbar.getEl();
31167             height -= te.getHeight();
31168             te.setWidth(width);
31169         }
31170         if(this.footer){
31171             var te = this.footer.getEl();
31172             //Roo.log("footer:" + te.getHeight());
31173             
31174             height -= te.getHeight();
31175             te.setWidth(width);
31176         }
31177         
31178         
31179         if(this.adjustments){
31180             width += this.adjustments[0];
31181             height += this.adjustments[1];
31182         }
31183         return {"width": width, "height": height};
31184     },
31185     
31186     setSize : function(width, height){
31187         if(this.fitToFrame && !this.ignoreResize(width, height)){
31188             if(this.fitContainer && this.resizeEl != this.el){
31189                 this.el.setSize(width, height);
31190             }
31191             var size = this.adjustForComponents(width, height);
31192             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31193             this.fireEvent('resize', this, size.width, size.height);
31194         }
31195     },
31196     
31197     /**
31198      * Returns this panel's title
31199      * @return {String} 
31200      */
31201     getTitle : function(){
31202         return this.title;
31203     },
31204     
31205     /**
31206      * Set this panel's title
31207      * @param {String} title
31208      */
31209     setTitle : function(title){
31210         this.title = title;
31211         if(this.region){
31212             this.region.updatePanelTitle(this, title);
31213         }
31214     },
31215     
31216     /**
31217      * Returns true is this panel was configured to be closable
31218      * @return {Boolean} 
31219      */
31220     isClosable : function(){
31221         return this.closable;
31222     },
31223     
31224     beforeSlide : function(){
31225         this.el.clip();
31226         this.resizeEl.clip();
31227     },
31228     
31229     afterSlide : function(){
31230         this.el.unclip();
31231         this.resizeEl.unclip();
31232     },
31233     
31234     /**
31235      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31236      *   Will fail silently if the {@link #setUrl} method has not been called.
31237      *   This does not activate the panel, just updates its content.
31238      */
31239     refresh : function(){
31240         if(this.refreshDelegate){
31241            this.loaded = false;
31242            this.refreshDelegate();
31243         }
31244     },
31245     
31246     /**
31247      * Destroys this panel
31248      */
31249     destroy : function(){
31250         this.el.removeAllListeners();
31251         var tempEl = document.createElement("span");
31252         tempEl.appendChild(this.el.dom);
31253         tempEl.innerHTML = "";
31254         this.el.remove();
31255         this.el = null;
31256     },
31257     
31258     /**
31259      * form - if the content panel contains a form - this is a reference to it.
31260      * @type {Roo.form.Form}
31261      */
31262     form : false,
31263     /**
31264      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31265      *    This contains a reference to it.
31266      * @type {Roo.View}
31267      */
31268     view : false,
31269     
31270       /**
31271      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31272      * <pre><code>
31273
31274 layout.addxtype({
31275        xtype : 'Form',
31276        items: [ .... ]
31277    }
31278 );
31279
31280 </code></pre>
31281      * @param {Object} cfg Xtype definition of item to add.
31282      */
31283     
31284     addxtype : function(cfg) {
31285         // add form..
31286         if (cfg.xtype.match(/^Form$/)) {
31287             
31288             var el;
31289             //if (this.footer) {
31290             //    el = this.footer.container.insertSibling(false, 'before');
31291             //} else {
31292                 el = this.el.createChild();
31293             //}
31294
31295             this.form = new  Roo.form.Form(cfg);
31296             
31297             
31298             if ( this.form.allItems.length) {
31299                 this.form.render(el.dom);
31300             }
31301             return this.form;
31302         }
31303         // should only have one of theses..
31304         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31305             // views.. should not be just added - used named prop 'view''
31306             
31307             cfg.el = this.el.appendChild(document.createElement("div"));
31308             // factory?
31309             
31310             var ret = new Roo.factory(cfg);
31311              
31312              ret.render && ret.render(false, ''); // render blank..
31313             this.view = ret;
31314             return ret;
31315         }
31316         return false;
31317     }
31318 });
31319
31320 /**
31321  * @class Roo.GridPanel
31322  * @extends Roo.ContentPanel
31323  * @constructor
31324  * Create a new GridPanel.
31325  * @param {Roo.grid.Grid} grid The grid for this panel
31326  * @param {String/Object} config A string to set only the panel's title, or a config object
31327  */
31328 Roo.GridPanel = function(grid, config){
31329     
31330   
31331     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31332         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31333         
31334     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31335     
31336     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31337     
31338     if(this.toolbar){
31339         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31340     }
31341     // xtype created footer. - not sure if will work as we normally have to render first..
31342     if (this.footer && !this.footer.el && this.footer.xtype) {
31343         
31344         this.footer.container = this.grid.getView().getFooterPanel(true);
31345         this.footer.dataSource = this.grid.dataSource;
31346         this.footer = Roo.factory(this.footer, Roo);
31347         
31348     }
31349     
31350     grid.monitorWindowResize = false; // turn off autosizing
31351     grid.autoHeight = false;
31352     grid.autoWidth = false;
31353     this.grid = grid;
31354     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31355 };
31356
31357 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31358     getId : function(){
31359         return this.grid.id;
31360     },
31361     
31362     /**
31363      * Returns the grid for this panel
31364      * @return {Roo.grid.Grid} 
31365      */
31366     getGrid : function(){
31367         return this.grid;    
31368     },
31369     
31370     setSize : function(width, height){
31371         if(!this.ignoreResize(width, height)){
31372             var grid = this.grid;
31373             var size = this.adjustForComponents(width, height);
31374             grid.getGridEl().setSize(size.width, size.height);
31375             grid.autoSize();
31376         }
31377     },
31378     
31379     beforeSlide : function(){
31380         this.grid.getView().scroller.clip();
31381     },
31382     
31383     afterSlide : function(){
31384         this.grid.getView().scroller.unclip();
31385     },
31386     
31387     destroy : function(){
31388         this.grid.destroy();
31389         delete this.grid;
31390         Roo.GridPanel.superclass.destroy.call(this); 
31391     }
31392 });
31393
31394
31395 /**
31396  * @class Roo.NestedLayoutPanel
31397  * @extends Roo.ContentPanel
31398  * @constructor
31399  * Create a new NestedLayoutPanel.
31400  * 
31401  * 
31402  * @param {Roo.BorderLayout} layout The layout for this panel
31403  * @param {String/Object} config A string to set only the title or a config object
31404  */
31405 Roo.NestedLayoutPanel = function(layout, config)
31406 {
31407     // construct with only one argument..
31408     /* FIXME - implement nicer consturctors
31409     if (layout.layout) {
31410         config = layout;
31411         layout = config.layout;
31412         delete config.layout;
31413     }
31414     if (layout.xtype && !layout.getEl) {
31415         // then layout needs constructing..
31416         layout = Roo.factory(layout, Roo);
31417     }
31418     */
31419     
31420     
31421     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31422     
31423     layout.monitorWindowResize = false; // turn off autosizing
31424     this.layout = layout;
31425     this.layout.getEl().addClass("x-layout-nested-layout");
31426     
31427     
31428     
31429     
31430 };
31431
31432 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31433
31434     setSize : function(width, height){
31435         if(!this.ignoreResize(width, height)){
31436             var size = this.adjustForComponents(width, height);
31437             var el = this.layout.getEl();
31438             el.setSize(size.width, size.height);
31439             var touch = el.dom.offsetWidth;
31440             this.layout.layout();
31441             // ie requires a double layout on the first pass
31442             if(Roo.isIE && !this.initialized){
31443                 this.initialized = true;
31444                 this.layout.layout();
31445             }
31446         }
31447     },
31448     
31449     // activate all subpanels if not currently active..
31450     
31451     setActiveState : function(active){
31452         this.active = active;
31453         if(!active){
31454             this.fireEvent("deactivate", this);
31455             return;
31456         }
31457         
31458         this.fireEvent("activate", this);
31459         // not sure if this should happen before or after..
31460         if (!this.layout) {
31461             return; // should not happen..
31462         }
31463         var reg = false;
31464         for (var r in this.layout.regions) {
31465             reg = this.layout.getRegion(r);
31466             if (reg.getActivePanel()) {
31467                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31468                 reg.setActivePanel(reg.getActivePanel());
31469                 continue;
31470             }
31471             if (!reg.panels.length) {
31472                 continue;
31473             }
31474             reg.showPanel(reg.getPanel(0));
31475         }
31476         
31477         
31478         
31479         
31480     },
31481     
31482     /**
31483      * Returns the nested BorderLayout for this panel
31484      * @return {Roo.BorderLayout} 
31485      */
31486     getLayout : function(){
31487         return this.layout;
31488     },
31489     
31490      /**
31491      * Adds a xtype elements to the layout of the nested panel
31492      * <pre><code>
31493
31494 panel.addxtype({
31495        xtype : 'ContentPanel',
31496        region: 'west',
31497        items: [ .... ]
31498    }
31499 );
31500
31501 panel.addxtype({
31502         xtype : 'NestedLayoutPanel',
31503         region: 'west',
31504         layout: {
31505            center: { },
31506            west: { }   
31507         },
31508         items : [ ... list of content panels or nested layout panels.. ]
31509    }
31510 );
31511 </code></pre>
31512      * @param {Object} cfg Xtype definition of item to add.
31513      */
31514     addxtype : function(cfg) {
31515         return this.layout.addxtype(cfg);
31516     
31517     }
31518 });
31519
31520 Roo.ScrollPanel = function(el, config, content){
31521     config = config || {};
31522     config.fitToFrame = true;
31523     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31524     
31525     this.el.dom.style.overflow = "hidden";
31526     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31527     this.el.removeClass("x-layout-inactive-content");
31528     this.el.on("mousewheel", this.onWheel, this);
31529
31530     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31531     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31532     up.unselectable(); down.unselectable();
31533     up.on("click", this.scrollUp, this);
31534     down.on("click", this.scrollDown, this);
31535     up.addClassOnOver("x-scroller-btn-over");
31536     down.addClassOnOver("x-scroller-btn-over");
31537     up.addClassOnClick("x-scroller-btn-click");
31538     down.addClassOnClick("x-scroller-btn-click");
31539     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31540
31541     this.resizeEl = this.el;
31542     this.el = wrap; this.up = up; this.down = down;
31543 };
31544
31545 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31546     increment : 100,
31547     wheelIncrement : 5,
31548     scrollUp : function(){
31549         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31550     },
31551
31552     scrollDown : function(){
31553         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31554     },
31555
31556     afterScroll : function(){
31557         var el = this.resizeEl;
31558         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31559         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31560         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31561     },
31562
31563     setSize : function(){
31564         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31565         this.afterScroll();
31566     },
31567
31568     onWheel : function(e){
31569         var d = e.getWheelDelta();
31570         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31571         this.afterScroll();
31572         e.stopEvent();
31573     },
31574
31575     setContent : function(content, loadScripts){
31576         this.resizeEl.update(content, loadScripts);
31577     }
31578
31579 });
31580
31581
31582
31583
31584
31585
31586
31587
31588
31589 /**
31590  * @class Roo.TreePanel
31591  * @extends Roo.ContentPanel
31592  * @constructor
31593  * Create a new TreePanel. - defaults to fit/scoll contents.
31594  * @param {String/Object} config A string to set only the panel's title, or a config object
31595  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31596  */
31597 Roo.TreePanel = function(config){
31598     var el = config.el;
31599     var tree = config.tree;
31600     delete config.tree; 
31601     delete config.el; // hopefull!
31602     
31603     // wrapper for IE7 strict & safari scroll issue
31604     
31605     var treeEl = el.createChild();
31606     config.resizeEl = treeEl;
31607     
31608     
31609     
31610     Roo.TreePanel.superclass.constructor.call(this, el, config);
31611  
31612  
31613     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31614     //console.log(tree);
31615     this.on('activate', function()
31616     {
31617         if (this.tree.rendered) {
31618             return;
31619         }
31620         //console.log('render tree');
31621         this.tree.render();
31622     });
31623     // this should not be needed.. - it's actually the 'el' that resizes?
31624     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31625     
31626     //this.on('resize',  function (cp, w, h) {
31627     //        this.tree.innerCt.setWidth(w);
31628     //        this.tree.innerCt.setHeight(h);
31629     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31630     //});
31631
31632         
31633     
31634 };
31635
31636 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31637     fitToFrame : true,
31638     autoScroll : true
31639 });
31640
31641
31642
31643
31644
31645
31646
31647
31648
31649
31650
31651 /*
31652  * Based on:
31653  * Ext JS Library 1.1.1
31654  * Copyright(c) 2006-2007, Ext JS, LLC.
31655  *
31656  * Originally Released Under LGPL - original licence link has changed is not relivant.
31657  *
31658  * Fork - LGPL
31659  * <script type="text/javascript">
31660  */
31661  
31662
31663 /**
31664  * @class Roo.ReaderLayout
31665  * @extends Roo.BorderLayout
31666  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31667  * center region containing two nested regions (a top one for a list view and one for item preview below),
31668  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31669  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31670  * expedites the setup of the overall layout and regions for this common application style.
31671  * Example:
31672  <pre><code>
31673 var reader = new Roo.ReaderLayout();
31674 var CP = Roo.ContentPanel;  // shortcut for adding
31675
31676 reader.beginUpdate();
31677 reader.add("north", new CP("north", "North"));
31678 reader.add("west", new CP("west", {title: "West"}));
31679 reader.add("east", new CP("east", {title: "East"}));
31680
31681 reader.regions.listView.add(new CP("listView", "List"));
31682 reader.regions.preview.add(new CP("preview", "Preview"));
31683 reader.endUpdate();
31684 </code></pre>
31685 * @constructor
31686 * Create a new ReaderLayout
31687 * @param {Object} config Configuration options
31688 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31689 * document.body if omitted)
31690 */
31691 Roo.ReaderLayout = function(config, renderTo){
31692     var c = config || {size:{}};
31693     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31694         north: c.north !== false ? Roo.apply({
31695             split:false,
31696             initialSize: 32,
31697             titlebar: false
31698         }, c.north) : false,
31699         west: c.west !== false ? Roo.apply({
31700             split:true,
31701             initialSize: 200,
31702             minSize: 175,
31703             maxSize: 400,
31704             titlebar: true,
31705             collapsible: true,
31706             animate: true,
31707             margins:{left:5,right:0,bottom:5,top:5},
31708             cmargins:{left:5,right:5,bottom:5,top:5}
31709         }, c.west) : false,
31710         east: c.east !== false ? Roo.apply({
31711             split:true,
31712             initialSize: 200,
31713             minSize: 175,
31714             maxSize: 400,
31715             titlebar: true,
31716             collapsible: true,
31717             animate: true,
31718             margins:{left:0,right:5,bottom:5,top:5},
31719             cmargins:{left:5,right:5,bottom:5,top:5}
31720         }, c.east) : false,
31721         center: Roo.apply({
31722             tabPosition: 'top',
31723             autoScroll:false,
31724             closeOnTab: true,
31725             titlebar:false,
31726             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31727         }, c.center)
31728     });
31729
31730     this.el.addClass('x-reader');
31731
31732     this.beginUpdate();
31733
31734     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31735         south: c.preview !== false ? Roo.apply({
31736             split:true,
31737             initialSize: 200,
31738             minSize: 100,
31739             autoScroll:true,
31740             collapsible:true,
31741             titlebar: true,
31742             cmargins:{top:5,left:0, right:0, bottom:0}
31743         }, c.preview) : false,
31744         center: Roo.apply({
31745             autoScroll:false,
31746             titlebar:false,
31747             minHeight:200
31748         }, c.listView)
31749     });
31750     this.add('center', new Roo.NestedLayoutPanel(inner,
31751             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31752
31753     this.endUpdate();
31754
31755     this.regions.preview = inner.getRegion('south');
31756     this.regions.listView = inner.getRegion('center');
31757 };
31758
31759 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31760  * Based on:
31761  * Ext JS Library 1.1.1
31762  * Copyright(c) 2006-2007, Ext JS, LLC.
31763  *
31764  * Originally Released Under LGPL - original licence link has changed is not relivant.
31765  *
31766  * Fork - LGPL
31767  * <script type="text/javascript">
31768  */
31769  
31770 /**
31771  * @class Roo.grid.Grid
31772  * @extends Roo.util.Observable
31773  * This class represents the primary interface of a component based grid control.
31774  * <br><br>Usage:<pre><code>
31775  var grid = new Roo.grid.Grid("my-container-id", {
31776      ds: myDataStore,
31777      cm: myColModel,
31778      selModel: mySelectionModel,
31779      autoSizeColumns: true,
31780      monitorWindowResize: false,
31781      trackMouseOver: true
31782  });
31783  // set any options
31784  grid.render();
31785  * </code></pre>
31786  * <b>Common Problems:</b><br/>
31787  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31788  * element will correct this<br/>
31789  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31790  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31791  * are unpredictable.<br/>
31792  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31793  * grid to calculate dimensions/offsets.<br/>
31794   * @constructor
31795  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31796  * The container MUST have some type of size defined for the grid to fill. The container will be
31797  * automatically set to position relative if it isn't already.
31798  * @param {Object} config A config object that sets properties on this grid.
31799  */
31800 Roo.grid.Grid = function(container, config){
31801         // initialize the container
31802         this.container = Roo.get(container);
31803         this.container.update("");
31804         this.container.setStyle("overflow", "hidden");
31805     this.container.addClass('x-grid-container');
31806
31807     this.id = this.container.id;
31808
31809     Roo.apply(this, config);
31810     // check and correct shorthanded configs
31811     if(this.ds){
31812         this.dataSource = this.ds;
31813         delete this.ds;
31814     }
31815     if(this.cm){
31816         this.colModel = this.cm;
31817         delete this.cm;
31818     }
31819     if(this.sm){
31820         this.selModel = this.sm;
31821         delete this.sm;
31822     }
31823
31824     if (this.selModel) {
31825         this.selModel = Roo.factory(this.selModel, Roo.grid);
31826         this.sm = this.selModel;
31827         this.sm.xmodule = this.xmodule || false;
31828     }
31829     if (typeof(this.colModel.config) == 'undefined') {
31830         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31831         this.cm = this.colModel;
31832         this.cm.xmodule = this.xmodule || false;
31833     }
31834     if (this.dataSource) {
31835         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31836         this.ds = this.dataSource;
31837         this.ds.xmodule = this.xmodule || false;
31838          
31839     }
31840     
31841     
31842     
31843     if(this.width){
31844         this.container.setWidth(this.width);
31845     }
31846
31847     if(this.height){
31848         this.container.setHeight(this.height);
31849     }
31850     /** @private */
31851         this.addEvents({
31852         // raw events
31853         /**
31854          * @event click
31855          * The raw click event for the entire grid.
31856          * @param {Roo.EventObject} e
31857          */
31858         "click" : true,
31859         /**
31860          * @event dblclick
31861          * The raw dblclick event for the entire grid.
31862          * @param {Roo.EventObject} e
31863          */
31864         "dblclick" : true,
31865         /**
31866          * @event contextmenu
31867          * The raw contextmenu event for the entire grid.
31868          * @param {Roo.EventObject} e
31869          */
31870         "contextmenu" : true,
31871         /**
31872          * @event mousedown
31873          * The raw mousedown event for the entire grid.
31874          * @param {Roo.EventObject} e
31875          */
31876         "mousedown" : true,
31877         /**
31878          * @event mouseup
31879          * The raw mouseup event for the entire grid.
31880          * @param {Roo.EventObject} e
31881          */
31882         "mouseup" : true,
31883         /**
31884          * @event mouseover
31885          * The raw mouseover event for the entire grid.
31886          * @param {Roo.EventObject} e
31887          */
31888         "mouseover" : true,
31889         /**
31890          * @event mouseout
31891          * The raw mouseout event for the entire grid.
31892          * @param {Roo.EventObject} e
31893          */
31894         "mouseout" : true,
31895         /**
31896          * @event keypress
31897          * The raw keypress event for the entire grid.
31898          * @param {Roo.EventObject} e
31899          */
31900         "keypress" : true,
31901         /**
31902          * @event keydown
31903          * The raw keydown event for the entire grid.
31904          * @param {Roo.EventObject} e
31905          */
31906         "keydown" : true,
31907
31908         // custom events
31909
31910         /**
31911          * @event cellclick
31912          * Fires when a cell is clicked
31913          * @param {Grid} this
31914          * @param {Number} rowIndex
31915          * @param {Number} columnIndex
31916          * @param {Roo.EventObject} e
31917          */
31918         "cellclick" : true,
31919         /**
31920          * @event celldblclick
31921          * Fires when a cell is double clicked
31922          * @param {Grid} this
31923          * @param {Number} rowIndex
31924          * @param {Number} columnIndex
31925          * @param {Roo.EventObject} e
31926          */
31927         "celldblclick" : true,
31928         /**
31929          * @event rowclick
31930          * Fires when a row is clicked
31931          * @param {Grid} this
31932          * @param {Number} rowIndex
31933          * @param {Roo.EventObject} e
31934          */
31935         "rowclick" : true,
31936         /**
31937          * @event rowdblclick
31938          * Fires when a row is double clicked
31939          * @param {Grid} this
31940          * @param {Number} rowIndex
31941          * @param {Roo.EventObject} e
31942          */
31943         "rowdblclick" : true,
31944         /**
31945          * @event headerclick
31946          * Fires when a header is clicked
31947          * @param {Grid} this
31948          * @param {Number} columnIndex
31949          * @param {Roo.EventObject} e
31950          */
31951         "headerclick" : true,
31952         /**
31953          * @event headerdblclick
31954          * Fires when a header cell is double clicked
31955          * @param {Grid} this
31956          * @param {Number} columnIndex
31957          * @param {Roo.EventObject} e
31958          */
31959         "headerdblclick" : true,
31960         /**
31961          * @event rowcontextmenu
31962          * Fires when a row is right clicked
31963          * @param {Grid} this
31964          * @param {Number} rowIndex
31965          * @param {Roo.EventObject} e
31966          */
31967         "rowcontextmenu" : true,
31968         /**
31969          * @event cellcontextmenu
31970          * Fires when a cell is right clicked
31971          * @param {Grid} this
31972          * @param {Number} rowIndex
31973          * @param {Number} cellIndex
31974          * @param {Roo.EventObject} e
31975          */
31976          "cellcontextmenu" : true,
31977         /**
31978          * @event headercontextmenu
31979          * Fires when a header is right clicked
31980          * @param {Grid} this
31981          * @param {Number} columnIndex
31982          * @param {Roo.EventObject} e
31983          */
31984         "headercontextmenu" : true,
31985         /**
31986          * @event bodyscroll
31987          * Fires when the body element is scrolled
31988          * @param {Number} scrollLeft
31989          * @param {Number} scrollTop
31990          */
31991         "bodyscroll" : true,
31992         /**
31993          * @event columnresize
31994          * Fires when the user resizes a column
31995          * @param {Number} columnIndex
31996          * @param {Number} newSize
31997          */
31998         "columnresize" : true,
31999         /**
32000          * @event columnmove
32001          * Fires when the user moves a column
32002          * @param {Number} oldIndex
32003          * @param {Number} newIndex
32004          */
32005         "columnmove" : true,
32006         /**
32007          * @event startdrag
32008          * Fires when row(s) start being dragged
32009          * @param {Grid} this
32010          * @param {Roo.GridDD} dd The drag drop object
32011          * @param {event} e The raw browser event
32012          */
32013         "startdrag" : true,
32014         /**
32015          * @event enddrag
32016          * Fires when a drag operation is complete
32017          * @param {Grid} this
32018          * @param {Roo.GridDD} dd The drag drop object
32019          * @param {event} e The raw browser event
32020          */
32021         "enddrag" : true,
32022         /**
32023          * @event dragdrop
32024          * Fires when dragged row(s) are dropped on a valid DD target
32025          * @param {Grid} this
32026          * @param {Roo.GridDD} dd The drag drop object
32027          * @param {String} targetId The target drag drop object
32028          * @param {event} e The raw browser event
32029          */
32030         "dragdrop" : true,
32031         /**
32032          * @event dragover
32033          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32034          * @param {Grid} this
32035          * @param {Roo.GridDD} dd The drag drop object
32036          * @param {String} targetId The target drag drop object
32037          * @param {event} e The raw browser event
32038          */
32039         "dragover" : true,
32040         /**
32041          * @event dragenter
32042          *  Fires when the dragged row(s) first cross another DD target while being dragged
32043          * @param {Grid} this
32044          * @param {Roo.GridDD} dd The drag drop object
32045          * @param {String} targetId The target drag drop object
32046          * @param {event} e The raw browser event
32047          */
32048         "dragenter" : true,
32049         /**
32050          * @event dragout
32051          * Fires when the dragged row(s) leave another DD target while being dragged
32052          * @param {Grid} this
32053          * @param {Roo.GridDD} dd The drag drop object
32054          * @param {String} targetId The target drag drop object
32055          * @param {event} e The raw browser event
32056          */
32057         "dragout" : true,
32058         /**
32059          * @event rowclass
32060          * Fires when a row is rendered, so you can change add a style to it.
32061          * @param {GridView} gridview   The grid view
32062          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32063          */
32064         'rowclass' : true,
32065
32066         /**
32067          * @event render
32068          * Fires when the grid is rendered
32069          * @param {Grid} grid
32070          */
32071         'render' : true
32072     });
32073
32074     Roo.grid.Grid.superclass.constructor.call(this);
32075 };
32076 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32077     
32078     /**
32079      * @cfg {String} ddGroup - drag drop group.
32080      */
32081
32082     /**
32083      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32084      */
32085     minColumnWidth : 25,
32086
32087     /**
32088      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32089      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32090      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32091      */
32092     autoSizeColumns : false,
32093
32094     /**
32095      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32096      */
32097     autoSizeHeaders : true,
32098
32099     /**
32100      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32101      */
32102     monitorWindowResize : true,
32103
32104     /**
32105      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32106      * rows measured to get a columns size. Default is 0 (all rows).
32107      */
32108     maxRowsToMeasure : 0,
32109
32110     /**
32111      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32112      */
32113     trackMouseOver : true,
32114
32115     /**
32116     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32117     */
32118     
32119     /**
32120     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32121     */
32122     enableDragDrop : false,
32123     
32124     /**
32125     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32126     */
32127     enableColumnMove : true,
32128     
32129     /**
32130     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32131     */
32132     enableColumnHide : true,
32133     
32134     /**
32135     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32136     */
32137     enableRowHeightSync : false,
32138     
32139     /**
32140     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32141     */
32142     stripeRows : true,
32143     
32144     /**
32145     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32146     */
32147     autoHeight : false,
32148
32149     /**
32150      * @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.
32151      */
32152     autoExpandColumn : false,
32153
32154     /**
32155     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32156     * Default is 50.
32157     */
32158     autoExpandMin : 50,
32159
32160     /**
32161     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32162     */
32163     autoExpandMax : 1000,
32164
32165     /**
32166     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32167     */
32168     view : null,
32169
32170     /**
32171     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32172     */
32173     loadMask : false,
32174     /**
32175     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32176     */
32177     dropTarget: false,
32178     
32179    
32180     
32181     // private
32182     rendered : false,
32183
32184     /**
32185     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32186     * of a fixed width. Default is false.
32187     */
32188     /**
32189     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32190     */
32191     /**
32192      * Called once after all setup has been completed and the grid is ready to be rendered.
32193      * @return {Roo.grid.Grid} this
32194      */
32195     render : function()
32196     {
32197         var c = this.container;
32198         // try to detect autoHeight/width mode
32199         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32200             this.autoHeight = true;
32201         }
32202         var view = this.getView();
32203         view.init(this);
32204
32205         c.on("click", this.onClick, this);
32206         c.on("dblclick", this.onDblClick, this);
32207         c.on("contextmenu", this.onContextMenu, this);
32208         c.on("keydown", this.onKeyDown, this);
32209         if (Roo.isTouch) {
32210             c.on("touchstart", this.onTouchStart, this);
32211         }
32212
32213         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32214
32215         this.getSelectionModel().init(this);
32216
32217         view.render();
32218
32219         if(this.loadMask){
32220             this.loadMask = new Roo.LoadMask(this.container,
32221                     Roo.apply({store:this.dataSource}, this.loadMask));
32222         }
32223         
32224         
32225         if (this.toolbar && this.toolbar.xtype) {
32226             this.toolbar.container = this.getView().getHeaderPanel(true);
32227             this.toolbar = new Roo.Toolbar(this.toolbar);
32228         }
32229         if (this.footer && this.footer.xtype) {
32230             this.footer.dataSource = this.getDataSource();
32231             this.footer.container = this.getView().getFooterPanel(true);
32232             this.footer = Roo.factory(this.footer, Roo);
32233         }
32234         if (this.dropTarget && this.dropTarget.xtype) {
32235             delete this.dropTarget.xtype;
32236             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32237         }
32238         
32239         
32240         this.rendered = true;
32241         this.fireEvent('render', this);
32242         return this;
32243     },
32244
32245         /**
32246          * Reconfigures the grid to use a different Store and Column Model.
32247          * The View will be bound to the new objects and refreshed.
32248          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32249          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32250          */
32251     reconfigure : function(dataSource, colModel){
32252         if(this.loadMask){
32253             this.loadMask.destroy();
32254             this.loadMask = new Roo.LoadMask(this.container,
32255                     Roo.apply({store:dataSource}, this.loadMask));
32256         }
32257         this.view.bind(dataSource, colModel);
32258         this.dataSource = dataSource;
32259         this.colModel = colModel;
32260         this.view.refresh(true);
32261     },
32262
32263     // private
32264     onKeyDown : function(e){
32265         this.fireEvent("keydown", e);
32266     },
32267
32268     /**
32269      * Destroy this grid.
32270      * @param {Boolean} removeEl True to remove the element
32271      */
32272     destroy : function(removeEl, keepListeners){
32273         if(this.loadMask){
32274             this.loadMask.destroy();
32275         }
32276         var c = this.container;
32277         c.removeAllListeners();
32278         this.view.destroy();
32279         this.colModel.purgeListeners();
32280         if(!keepListeners){
32281             this.purgeListeners();
32282         }
32283         c.update("");
32284         if(removeEl === true){
32285             c.remove();
32286         }
32287     },
32288
32289     // private
32290     processEvent : function(name, e){
32291         // does this fire select???
32292         //Roo.log('grid:processEvent '  + name);
32293         
32294         if (name != 'touchstart' ) {
32295             this.fireEvent(name, e);    
32296         }
32297         
32298         var t = e.getTarget();
32299         var v = this.view;
32300         var header = v.findHeaderIndex(t);
32301         if(header !== false){
32302             var ename = name == 'touchstart' ? 'click' : name;
32303              
32304             this.fireEvent("header" + ename, this, header, e);
32305         }else{
32306             var row = v.findRowIndex(t);
32307             var cell = v.findCellIndex(t);
32308             if (name == 'touchstart') {
32309                 // first touch is always a click.
32310                 // hopefull this happens after selection is updated.?
32311                 name = false;
32312                 
32313                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32314                     var cs = this.selModel.getSelectedCell();
32315                     if (row == cs[0] && cell == cs[1]){
32316                         name = 'dblclick';
32317                     }
32318                 }
32319                 if (typeof(this.selModel.getSelections) != 'undefined') {
32320                     var cs = this.selModel.getSelections();
32321                     var ds = this.dataSource;
32322                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32323                         name = 'dblclick';
32324                     }
32325                 }
32326                 if (!name) {
32327                     return;
32328                 }
32329             }
32330             
32331             
32332             if(row !== false){
32333                 this.fireEvent("row" + name, this, row, e);
32334                 if(cell !== false){
32335                     this.fireEvent("cell" + name, this, row, cell, e);
32336                 }
32337             }
32338         }
32339     },
32340
32341     // private
32342     onClick : function(e){
32343         this.processEvent("click", e);
32344     },
32345    // private
32346     onTouchStart : function(e){
32347         this.processEvent("touchstart", e);
32348     },
32349
32350     // private
32351     onContextMenu : function(e, t){
32352         this.processEvent("contextmenu", e);
32353     },
32354
32355     // private
32356     onDblClick : function(e){
32357         this.processEvent("dblclick", e);
32358     },
32359
32360     // private
32361     walkCells : function(row, col, step, fn, scope){
32362         var cm = this.colModel, clen = cm.getColumnCount();
32363         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32364         if(step < 0){
32365             if(col < 0){
32366                 row--;
32367                 first = false;
32368             }
32369             while(row >= 0){
32370                 if(!first){
32371                     col = clen-1;
32372                 }
32373                 first = false;
32374                 while(col >= 0){
32375                     if(fn.call(scope || this, row, col, cm) === true){
32376                         return [row, col];
32377                     }
32378                     col--;
32379                 }
32380                 row--;
32381             }
32382         } else {
32383             if(col >= clen){
32384                 row++;
32385                 first = false;
32386             }
32387             while(row < rlen){
32388                 if(!first){
32389                     col = 0;
32390                 }
32391                 first = false;
32392                 while(col < clen){
32393                     if(fn.call(scope || this, row, col, cm) === true){
32394                         return [row, col];
32395                     }
32396                     col++;
32397                 }
32398                 row++;
32399             }
32400         }
32401         return null;
32402     },
32403
32404     // private
32405     getSelections : function(){
32406         return this.selModel.getSelections();
32407     },
32408
32409     /**
32410      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32411      * but if manual update is required this method will initiate it.
32412      */
32413     autoSize : function(){
32414         if(this.rendered){
32415             this.view.layout();
32416             if(this.view.adjustForScroll){
32417                 this.view.adjustForScroll();
32418             }
32419         }
32420     },
32421
32422     /**
32423      * Returns the grid's underlying element.
32424      * @return {Element} The element
32425      */
32426     getGridEl : function(){
32427         return this.container;
32428     },
32429
32430     // private for compatibility, overridden by editor grid
32431     stopEditing : function(){},
32432
32433     /**
32434      * Returns the grid's SelectionModel.
32435      * @return {SelectionModel}
32436      */
32437     getSelectionModel : function(){
32438         if(!this.selModel){
32439             this.selModel = new Roo.grid.RowSelectionModel();
32440         }
32441         return this.selModel;
32442     },
32443
32444     /**
32445      * Returns the grid's DataSource.
32446      * @return {DataSource}
32447      */
32448     getDataSource : function(){
32449         return this.dataSource;
32450     },
32451
32452     /**
32453      * Returns the grid's ColumnModel.
32454      * @return {ColumnModel}
32455      */
32456     getColumnModel : function(){
32457         return this.colModel;
32458     },
32459
32460     /**
32461      * Returns the grid's GridView object.
32462      * @return {GridView}
32463      */
32464     getView : function(){
32465         if(!this.view){
32466             this.view = new Roo.grid.GridView(this.viewConfig);
32467         }
32468         return this.view;
32469     },
32470     /**
32471      * Called to get grid's drag proxy text, by default returns this.ddText.
32472      * @return {String}
32473      */
32474     getDragDropText : function(){
32475         var count = this.selModel.getCount();
32476         return String.format(this.ddText, count, count == 1 ? '' : 's');
32477     }
32478 });
32479 /**
32480  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32481  * %0 is replaced with the number of selected rows.
32482  * @type String
32483  */
32484 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32485  * Based on:
32486  * Ext JS Library 1.1.1
32487  * Copyright(c) 2006-2007, Ext JS, LLC.
32488  *
32489  * Originally Released Under LGPL - original licence link has changed is not relivant.
32490  *
32491  * Fork - LGPL
32492  * <script type="text/javascript">
32493  */
32494  
32495 Roo.grid.AbstractGridView = function(){
32496         this.grid = null;
32497         
32498         this.events = {
32499             "beforerowremoved" : true,
32500             "beforerowsinserted" : true,
32501             "beforerefresh" : true,
32502             "rowremoved" : true,
32503             "rowsinserted" : true,
32504             "rowupdated" : true,
32505             "refresh" : true
32506         };
32507     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32508 };
32509
32510 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32511     rowClass : "x-grid-row",
32512     cellClass : "x-grid-cell",
32513     tdClass : "x-grid-td",
32514     hdClass : "x-grid-hd",
32515     splitClass : "x-grid-hd-split",
32516     
32517     init: function(grid){
32518         this.grid = grid;
32519                 var cid = this.grid.getGridEl().id;
32520         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32521         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32522         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32523         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32524         },
32525         
32526     getColumnRenderers : function(){
32527         var renderers = [];
32528         var cm = this.grid.colModel;
32529         var colCount = cm.getColumnCount();
32530         for(var i = 0; i < colCount; i++){
32531             renderers[i] = cm.getRenderer(i);
32532         }
32533         return renderers;
32534     },
32535     
32536     getColumnIds : function(){
32537         var ids = [];
32538         var cm = this.grid.colModel;
32539         var colCount = cm.getColumnCount();
32540         for(var i = 0; i < colCount; i++){
32541             ids[i] = cm.getColumnId(i);
32542         }
32543         return ids;
32544     },
32545     
32546     getDataIndexes : function(){
32547         if(!this.indexMap){
32548             this.indexMap = this.buildIndexMap();
32549         }
32550         return this.indexMap.colToData;
32551     },
32552     
32553     getColumnIndexByDataIndex : function(dataIndex){
32554         if(!this.indexMap){
32555             this.indexMap = this.buildIndexMap();
32556         }
32557         return this.indexMap.dataToCol[dataIndex];
32558     },
32559     
32560     /**
32561      * Set a css style for a column dynamically. 
32562      * @param {Number} colIndex The index of the column
32563      * @param {String} name The css property name
32564      * @param {String} value The css value
32565      */
32566     setCSSStyle : function(colIndex, name, value){
32567         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32568         Roo.util.CSS.updateRule(selector, name, value);
32569     },
32570     
32571     generateRules : function(cm){
32572         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32573         Roo.util.CSS.removeStyleSheet(rulesId);
32574         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32575             var cid = cm.getColumnId(i);
32576             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32577                          this.tdSelector, cid, " {\n}\n",
32578                          this.hdSelector, cid, " {\n}\n",
32579                          this.splitSelector, cid, " {\n}\n");
32580         }
32581         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32582     }
32583 });/*
32584  * Based on:
32585  * Ext JS Library 1.1.1
32586  * Copyright(c) 2006-2007, Ext JS, LLC.
32587  *
32588  * Originally Released Under LGPL - original licence link has changed is not relivant.
32589  *
32590  * Fork - LGPL
32591  * <script type="text/javascript">
32592  */
32593
32594 // private
32595 // This is a support class used internally by the Grid components
32596 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32597     this.grid = grid;
32598     this.view = grid.getView();
32599     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32600     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32601     if(hd2){
32602         this.setHandleElId(Roo.id(hd));
32603         this.setOuterHandleElId(Roo.id(hd2));
32604     }
32605     this.scroll = false;
32606 };
32607 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32608     maxDragWidth: 120,
32609     getDragData : function(e){
32610         var t = Roo.lib.Event.getTarget(e);
32611         var h = this.view.findHeaderCell(t);
32612         if(h){
32613             return {ddel: h.firstChild, header:h};
32614         }
32615         return false;
32616     },
32617
32618     onInitDrag : function(e){
32619         this.view.headersDisabled = true;
32620         var clone = this.dragData.ddel.cloneNode(true);
32621         clone.id = Roo.id();
32622         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32623         this.proxy.update(clone);
32624         return true;
32625     },
32626
32627     afterValidDrop : function(){
32628         var v = this.view;
32629         setTimeout(function(){
32630             v.headersDisabled = false;
32631         }, 50);
32632     },
32633
32634     afterInvalidDrop : function(){
32635         var v = this.view;
32636         setTimeout(function(){
32637             v.headersDisabled = false;
32638         }, 50);
32639     }
32640 });
32641 /*
32642  * Based on:
32643  * Ext JS Library 1.1.1
32644  * Copyright(c) 2006-2007, Ext JS, LLC.
32645  *
32646  * Originally Released Under LGPL - original licence link has changed is not relivant.
32647  *
32648  * Fork - LGPL
32649  * <script type="text/javascript">
32650  */
32651 // private
32652 // This is a support class used internally by the Grid components
32653 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32654     this.grid = grid;
32655     this.view = grid.getView();
32656     // split the proxies so they don't interfere with mouse events
32657     this.proxyTop = Roo.DomHelper.append(document.body, {
32658         cls:"col-move-top", html:"&#160;"
32659     }, true);
32660     this.proxyBottom = Roo.DomHelper.append(document.body, {
32661         cls:"col-move-bottom", html:"&#160;"
32662     }, true);
32663     this.proxyTop.hide = this.proxyBottom.hide = function(){
32664         this.setLeftTop(-100,-100);
32665         this.setStyle("visibility", "hidden");
32666     };
32667     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32668     // temporarily disabled
32669     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32670     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32671 };
32672 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32673     proxyOffsets : [-4, -9],
32674     fly: Roo.Element.fly,
32675
32676     getTargetFromEvent : function(e){
32677         var t = Roo.lib.Event.getTarget(e);
32678         var cindex = this.view.findCellIndex(t);
32679         if(cindex !== false){
32680             return this.view.getHeaderCell(cindex);
32681         }
32682         return null;
32683     },
32684
32685     nextVisible : function(h){
32686         var v = this.view, cm = this.grid.colModel;
32687         h = h.nextSibling;
32688         while(h){
32689             if(!cm.isHidden(v.getCellIndex(h))){
32690                 return h;
32691             }
32692             h = h.nextSibling;
32693         }
32694         return null;
32695     },
32696
32697     prevVisible : function(h){
32698         var v = this.view, cm = this.grid.colModel;
32699         h = h.prevSibling;
32700         while(h){
32701             if(!cm.isHidden(v.getCellIndex(h))){
32702                 return h;
32703             }
32704             h = h.prevSibling;
32705         }
32706         return null;
32707     },
32708
32709     positionIndicator : function(h, n, e){
32710         var x = Roo.lib.Event.getPageX(e);
32711         var r = Roo.lib.Dom.getRegion(n.firstChild);
32712         var px, pt, py = r.top + this.proxyOffsets[1];
32713         if((r.right - x) <= (r.right-r.left)/2){
32714             px = r.right+this.view.borderWidth;
32715             pt = "after";
32716         }else{
32717             px = r.left;
32718             pt = "before";
32719         }
32720         var oldIndex = this.view.getCellIndex(h);
32721         var newIndex = this.view.getCellIndex(n);
32722
32723         if(this.grid.colModel.isFixed(newIndex)){
32724             return false;
32725         }
32726
32727         var locked = this.grid.colModel.isLocked(newIndex);
32728
32729         if(pt == "after"){
32730             newIndex++;
32731         }
32732         if(oldIndex < newIndex){
32733             newIndex--;
32734         }
32735         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32736             return false;
32737         }
32738         px +=  this.proxyOffsets[0];
32739         this.proxyTop.setLeftTop(px, py);
32740         this.proxyTop.show();
32741         if(!this.bottomOffset){
32742             this.bottomOffset = this.view.mainHd.getHeight();
32743         }
32744         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32745         this.proxyBottom.show();
32746         return pt;
32747     },
32748
32749     onNodeEnter : function(n, dd, e, data){
32750         if(data.header != n){
32751             this.positionIndicator(data.header, n, e);
32752         }
32753     },
32754
32755     onNodeOver : function(n, dd, e, data){
32756         var result = false;
32757         if(data.header != n){
32758             result = this.positionIndicator(data.header, n, e);
32759         }
32760         if(!result){
32761             this.proxyTop.hide();
32762             this.proxyBottom.hide();
32763         }
32764         return result ? this.dropAllowed : this.dropNotAllowed;
32765     },
32766
32767     onNodeOut : function(n, dd, e, data){
32768         this.proxyTop.hide();
32769         this.proxyBottom.hide();
32770     },
32771
32772     onNodeDrop : function(n, dd, e, data){
32773         var h = data.header;
32774         if(h != n){
32775             var cm = this.grid.colModel;
32776             var x = Roo.lib.Event.getPageX(e);
32777             var r = Roo.lib.Dom.getRegion(n.firstChild);
32778             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32779             var oldIndex = this.view.getCellIndex(h);
32780             var newIndex = this.view.getCellIndex(n);
32781             var locked = cm.isLocked(newIndex);
32782             if(pt == "after"){
32783                 newIndex++;
32784             }
32785             if(oldIndex < newIndex){
32786                 newIndex--;
32787             }
32788             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32789                 return false;
32790             }
32791             cm.setLocked(oldIndex, locked, true);
32792             cm.moveColumn(oldIndex, newIndex);
32793             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32794             return true;
32795         }
32796         return false;
32797     }
32798 });
32799 /*
32800  * Based on:
32801  * Ext JS Library 1.1.1
32802  * Copyright(c) 2006-2007, Ext JS, LLC.
32803  *
32804  * Originally Released Under LGPL - original licence link has changed is not relivant.
32805  *
32806  * Fork - LGPL
32807  * <script type="text/javascript">
32808  */
32809   
32810 /**
32811  * @class Roo.grid.GridView
32812  * @extends Roo.util.Observable
32813  *
32814  * @constructor
32815  * @param {Object} config
32816  */
32817 Roo.grid.GridView = function(config){
32818     Roo.grid.GridView.superclass.constructor.call(this);
32819     this.el = null;
32820
32821     Roo.apply(this, config);
32822 };
32823
32824 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32825
32826     unselectable :  'unselectable="on"',
32827     unselectableCls :  'x-unselectable',
32828     
32829     
32830     rowClass : "x-grid-row",
32831
32832     cellClass : "x-grid-col",
32833
32834     tdClass : "x-grid-td",
32835
32836     hdClass : "x-grid-hd",
32837
32838     splitClass : "x-grid-split",
32839
32840     sortClasses : ["sort-asc", "sort-desc"],
32841
32842     enableMoveAnim : false,
32843
32844     hlColor: "C3DAF9",
32845
32846     dh : Roo.DomHelper,
32847
32848     fly : Roo.Element.fly,
32849
32850     css : Roo.util.CSS,
32851
32852     borderWidth: 1,
32853
32854     splitOffset: 3,
32855
32856     scrollIncrement : 22,
32857
32858     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32859
32860     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32861
32862     bind : function(ds, cm){
32863         if(this.ds){
32864             this.ds.un("load", this.onLoad, this);
32865             this.ds.un("datachanged", this.onDataChange, this);
32866             this.ds.un("add", this.onAdd, this);
32867             this.ds.un("remove", this.onRemove, this);
32868             this.ds.un("update", this.onUpdate, this);
32869             this.ds.un("clear", this.onClear, this);
32870         }
32871         if(ds){
32872             ds.on("load", this.onLoad, this);
32873             ds.on("datachanged", this.onDataChange, this);
32874             ds.on("add", this.onAdd, this);
32875             ds.on("remove", this.onRemove, this);
32876             ds.on("update", this.onUpdate, this);
32877             ds.on("clear", this.onClear, this);
32878         }
32879         this.ds = ds;
32880
32881         if(this.cm){
32882             this.cm.un("widthchange", this.onColWidthChange, this);
32883             this.cm.un("headerchange", this.onHeaderChange, this);
32884             this.cm.un("hiddenchange", this.onHiddenChange, this);
32885             this.cm.un("columnmoved", this.onColumnMove, this);
32886             this.cm.un("columnlockchange", this.onColumnLock, this);
32887         }
32888         if(cm){
32889             this.generateRules(cm);
32890             cm.on("widthchange", this.onColWidthChange, this);
32891             cm.on("headerchange", this.onHeaderChange, this);
32892             cm.on("hiddenchange", this.onHiddenChange, this);
32893             cm.on("columnmoved", this.onColumnMove, this);
32894             cm.on("columnlockchange", this.onColumnLock, this);
32895         }
32896         this.cm = cm;
32897     },
32898
32899     init: function(grid){
32900         Roo.grid.GridView.superclass.init.call(this, grid);
32901
32902         this.bind(grid.dataSource, grid.colModel);
32903
32904         grid.on("headerclick", this.handleHeaderClick, this);
32905
32906         if(grid.trackMouseOver){
32907             grid.on("mouseover", this.onRowOver, this);
32908             grid.on("mouseout", this.onRowOut, this);
32909         }
32910         grid.cancelTextSelection = function(){};
32911         this.gridId = grid.id;
32912
32913         var tpls = this.templates || {};
32914
32915         if(!tpls.master){
32916             tpls.master = new Roo.Template(
32917                '<div class="x-grid" hidefocus="true">',
32918                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32919                   '<div class="x-grid-topbar"></div>',
32920                   '<div class="x-grid-scroller"><div></div></div>',
32921                   '<div class="x-grid-locked">',
32922                       '<div class="x-grid-header">{lockedHeader}</div>',
32923                       '<div class="x-grid-body">{lockedBody}</div>',
32924                   "</div>",
32925                   '<div class="x-grid-viewport">',
32926                       '<div class="x-grid-header">{header}</div>',
32927                       '<div class="x-grid-body">{body}</div>',
32928                   "</div>",
32929                   '<div class="x-grid-bottombar"></div>',
32930                  
32931                   '<div class="x-grid-resize-proxy">&#160;</div>',
32932                "</div>"
32933             );
32934             tpls.master.disableformats = true;
32935         }
32936
32937         if(!tpls.header){
32938             tpls.header = new Roo.Template(
32939                '<table border="0" cellspacing="0" cellpadding="0">',
32940                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32941                "</table>{splits}"
32942             );
32943             tpls.header.disableformats = true;
32944         }
32945         tpls.header.compile();
32946
32947         if(!tpls.hcell){
32948             tpls.hcell = new Roo.Template(
32949                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32950                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32951                 "</div></td>"
32952              );
32953              tpls.hcell.disableFormats = true;
32954         }
32955         tpls.hcell.compile();
32956
32957         if(!tpls.hsplit){
32958             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32959                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32960             tpls.hsplit.disableFormats = true;
32961         }
32962         tpls.hsplit.compile();
32963
32964         if(!tpls.body){
32965             tpls.body = new Roo.Template(
32966                '<table border="0" cellspacing="0" cellpadding="0">',
32967                "<tbody>{rows}</tbody>",
32968                "</table>"
32969             );
32970             tpls.body.disableFormats = true;
32971         }
32972         tpls.body.compile();
32973
32974         if(!tpls.row){
32975             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32976             tpls.row.disableFormats = true;
32977         }
32978         tpls.row.compile();
32979
32980         if(!tpls.cell){
32981             tpls.cell = new Roo.Template(
32982                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32983                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32984                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32985                 "</td>"
32986             );
32987             tpls.cell.disableFormats = true;
32988         }
32989         tpls.cell.compile();
32990
32991         this.templates = tpls;
32992     },
32993
32994     // remap these for backwards compat
32995     onColWidthChange : function(){
32996         this.updateColumns.apply(this, arguments);
32997     },
32998     onHeaderChange : function(){
32999         this.updateHeaders.apply(this, arguments);
33000     }, 
33001     onHiddenChange : function(){
33002         this.handleHiddenChange.apply(this, arguments);
33003     },
33004     onColumnMove : function(){
33005         this.handleColumnMove.apply(this, arguments);
33006     },
33007     onColumnLock : function(){
33008         this.handleLockChange.apply(this, arguments);
33009     },
33010
33011     onDataChange : function(){
33012         this.refresh();
33013         this.updateHeaderSortState();
33014     },
33015
33016     onClear : function(){
33017         this.refresh();
33018     },
33019
33020     onUpdate : function(ds, record){
33021         this.refreshRow(record);
33022     },
33023
33024     refreshRow : function(record){
33025         var ds = this.ds, index;
33026         if(typeof record == 'number'){
33027             index = record;
33028             record = ds.getAt(index);
33029         }else{
33030             index = ds.indexOf(record);
33031         }
33032         this.insertRows(ds, index, index, true);
33033         this.onRemove(ds, record, index+1, true);
33034         this.syncRowHeights(index, index);
33035         this.layout();
33036         this.fireEvent("rowupdated", this, index, record);
33037     },
33038
33039     onAdd : function(ds, records, index){
33040         this.insertRows(ds, index, index + (records.length-1));
33041     },
33042
33043     onRemove : function(ds, record, index, isUpdate){
33044         if(isUpdate !== true){
33045             this.fireEvent("beforerowremoved", this, index, record);
33046         }
33047         var bt = this.getBodyTable(), lt = this.getLockedTable();
33048         if(bt.rows[index]){
33049             bt.firstChild.removeChild(bt.rows[index]);
33050         }
33051         if(lt.rows[index]){
33052             lt.firstChild.removeChild(lt.rows[index]);
33053         }
33054         if(isUpdate !== true){
33055             this.stripeRows(index);
33056             this.syncRowHeights(index, index);
33057             this.layout();
33058             this.fireEvent("rowremoved", this, index, record);
33059         }
33060     },
33061
33062     onLoad : function(){
33063         this.scrollToTop();
33064     },
33065
33066     /**
33067      * Scrolls the grid to the top
33068      */
33069     scrollToTop : function(){
33070         if(this.scroller){
33071             this.scroller.dom.scrollTop = 0;
33072             this.syncScroll();
33073         }
33074     },
33075
33076     /**
33077      * Gets a panel in the header of the grid that can be used for toolbars etc.
33078      * After modifying the contents of this panel a call to grid.autoSize() may be
33079      * required to register any changes in size.
33080      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33081      * @return Roo.Element
33082      */
33083     getHeaderPanel : function(doShow){
33084         if(doShow){
33085             this.headerPanel.show();
33086         }
33087         return this.headerPanel;
33088     },
33089
33090     /**
33091      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33092      * After modifying the contents of this panel a call to grid.autoSize() may be
33093      * required to register any changes in size.
33094      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33095      * @return Roo.Element
33096      */
33097     getFooterPanel : function(doShow){
33098         if(doShow){
33099             this.footerPanel.show();
33100         }
33101         return this.footerPanel;
33102     },
33103
33104     initElements : function(){
33105         var E = Roo.Element;
33106         var el = this.grid.getGridEl().dom.firstChild;
33107         var cs = el.childNodes;
33108
33109         this.el = new E(el);
33110         
33111          this.focusEl = new E(el.firstChild);
33112         this.focusEl.swallowEvent("click", true);
33113         
33114         this.headerPanel = new E(cs[1]);
33115         this.headerPanel.enableDisplayMode("block");
33116
33117         this.scroller = new E(cs[2]);
33118         this.scrollSizer = new E(this.scroller.dom.firstChild);
33119
33120         this.lockedWrap = new E(cs[3]);
33121         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33122         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33123
33124         this.mainWrap = new E(cs[4]);
33125         this.mainHd = new E(this.mainWrap.dom.firstChild);
33126         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33127
33128         this.footerPanel = new E(cs[5]);
33129         this.footerPanel.enableDisplayMode("block");
33130
33131         this.resizeProxy = new E(cs[6]);
33132
33133         this.headerSelector = String.format(
33134            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33135            this.lockedHd.id, this.mainHd.id
33136         );
33137
33138         this.splitterSelector = String.format(
33139            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33140            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33141         );
33142     },
33143     idToCssName : function(s)
33144     {
33145         return s.replace(/[^a-z0-9]+/ig, '-');
33146     },
33147
33148     getHeaderCell : function(index){
33149         return Roo.DomQuery.select(this.headerSelector)[index];
33150     },
33151
33152     getHeaderCellMeasure : function(index){
33153         return this.getHeaderCell(index).firstChild;
33154     },
33155
33156     getHeaderCellText : function(index){
33157         return this.getHeaderCell(index).firstChild.firstChild;
33158     },
33159
33160     getLockedTable : function(){
33161         return this.lockedBody.dom.firstChild;
33162     },
33163
33164     getBodyTable : function(){
33165         return this.mainBody.dom.firstChild;
33166     },
33167
33168     getLockedRow : function(index){
33169         return this.getLockedTable().rows[index];
33170     },
33171
33172     getRow : function(index){
33173         return this.getBodyTable().rows[index];
33174     },
33175
33176     getRowComposite : function(index){
33177         if(!this.rowEl){
33178             this.rowEl = new Roo.CompositeElementLite();
33179         }
33180         var els = [], lrow, mrow;
33181         if(lrow = this.getLockedRow(index)){
33182             els.push(lrow);
33183         }
33184         if(mrow = this.getRow(index)){
33185             els.push(mrow);
33186         }
33187         this.rowEl.elements = els;
33188         return this.rowEl;
33189     },
33190     /**
33191      * Gets the 'td' of the cell
33192      * 
33193      * @param {Integer} rowIndex row to select
33194      * @param {Integer} colIndex column to select
33195      * 
33196      * @return {Object} 
33197      */
33198     getCell : function(rowIndex, colIndex){
33199         var locked = this.cm.getLockedCount();
33200         var source;
33201         if(colIndex < locked){
33202             source = this.lockedBody.dom.firstChild;
33203         }else{
33204             source = this.mainBody.dom.firstChild;
33205             colIndex -= locked;
33206         }
33207         return source.rows[rowIndex].childNodes[colIndex];
33208     },
33209
33210     getCellText : function(rowIndex, colIndex){
33211         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33212     },
33213
33214     getCellBox : function(cell){
33215         var b = this.fly(cell).getBox();
33216         if(Roo.isOpera){ // opera fails to report the Y
33217             b.y = cell.offsetTop + this.mainBody.getY();
33218         }
33219         return b;
33220     },
33221
33222     getCellIndex : function(cell){
33223         var id = String(cell.className).match(this.cellRE);
33224         if(id){
33225             return parseInt(id[1], 10);
33226         }
33227         return 0;
33228     },
33229
33230     findHeaderIndex : function(n){
33231         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33232         return r ? this.getCellIndex(r) : false;
33233     },
33234
33235     findHeaderCell : function(n){
33236         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33237         return r ? r : false;
33238     },
33239
33240     findRowIndex : function(n){
33241         if(!n){
33242             return false;
33243         }
33244         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33245         return r ? r.rowIndex : false;
33246     },
33247
33248     findCellIndex : function(node){
33249         var stop = this.el.dom;
33250         while(node && node != stop){
33251             if(this.findRE.test(node.className)){
33252                 return this.getCellIndex(node);
33253             }
33254             node = node.parentNode;
33255         }
33256         return false;
33257     },
33258
33259     getColumnId : function(index){
33260         return this.cm.getColumnId(index);
33261     },
33262
33263     getSplitters : function()
33264     {
33265         if(this.splitterSelector){
33266            return Roo.DomQuery.select(this.splitterSelector);
33267         }else{
33268             return null;
33269       }
33270     },
33271
33272     getSplitter : function(index){
33273         return this.getSplitters()[index];
33274     },
33275
33276     onRowOver : function(e, t){
33277         var row;
33278         if((row = this.findRowIndex(t)) !== false){
33279             this.getRowComposite(row).addClass("x-grid-row-over");
33280         }
33281     },
33282
33283     onRowOut : function(e, t){
33284         var row;
33285         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33286             this.getRowComposite(row).removeClass("x-grid-row-over");
33287         }
33288     },
33289
33290     renderHeaders : function(){
33291         var cm = this.cm;
33292         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33293         var cb = [], lb = [], sb = [], lsb = [], p = {};
33294         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33295             p.cellId = "x-grid-hd-0-" + i;
33296             p.splitId = "x-grid-csplit-0-" + i;
33297             p.id = cm.getColumnId(i);
33298             p.value = cm.getColumnHeader(i) || "";
33299             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33300             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33301             if(!cm.isLocked(i)){
33302                 cb[cb.length] = ct.apply(p);
33303                 sb[sb.length] = st.apply(p);
33304             }else{
33305                 lb[lb.length] = ct.apply(p);
33306                 lsb[lsb.length] = st.apply(p);
33307             }
33308         }
33309         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33310                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33311     },
33312
33313     updateHeaders : function(){
33314         var html = this.renderHeaders();
33315         this.lockedHd.update(html[0]);
33316         this.mainHd.update(html[1]);
33317     },
33318
33319     /**
33320      * Focuses the specified row.
33321      * @param {Number} row The row index
33322      */
33323     focusRow : function(row)
33324     {
33325         //Roo.log('GridView.focusRow');
33326         var x = this.scroller.dom.scrollLeft;
33327         this.focusCell(row, 0, false);
33328         this.scroller.dom.scrollLeft = x;
33329     },
33330
33331     /**
33332      * Focuses the specified cell.
33333      * @param {Number} row The row index
33334      * @param {Number} col The column index
33335      * @param {Boolean} hscroll false to disable horizontal scrolling
33336      */
33337     focusCell : function(row, col, hscroll)
33338     {
33339         //Roo.log('GridView.focusCell');
33340         var el = this.ensureVisible(row, col, hscroll);
33341         this.focusEl.alignTo(el, "tl-tl");
33342         if(Roo.isGecko){
33343             this.focusEl.focus();
33344         }else{
33345             this.focusEl.focus.defer(1, this.focusEl);
33346         }
33347     },
33348
33349     /**
33350      * Scrolls the specified cell into view
33351      * @param {Number} row The row index
33352      * @param {Number} col The column index
33353      * @param {Boolean} hscroll false to disable horizontal scrolling
33354      */
33355     ensureVisible : function(row, col, hscroll)
33356     {
33357         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33358         //return null; //disable for testing.
33359         if(typeof row != "number"){
33360             row = row.rowIndex;
33361         }
33362         if(row < 0 && row >= this.ds.getCount()){
33363             return  null;
33364         }
33365         col = (col !== undefined ? col : 0);
33366         var cm = this.grid.colModel;
33367         while(cm.isHidden(col)){
33368             col++;
33369         }
33370
33371         var el = this.getCell(row, col);
33372         if(!el){
33373             return null;
33374         }
33375         var c = this.scroller.dom;
33376
33377         var ctop = parseInt(el.offsetTop, 10);
33378         var cleft = parseInt(el.offsetLeft, 10);
33379         var cbot = ctop + el.offsetHeight;
33380         var cright = cleft + el.offsetWidth;
33381         
33382         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33383         var stop = parseInt(c.scrollTop, 10);
33384         var sleft = parseInt(c.scrollLeft, 10);
33385         var sbot = stop + ch;
33386         var sright = sleft + c.clientWidth;
33387         /*
33388         Roo.log('GridView.ensureVisible:' +
33389                 ' ctop:' + ctop +
33390                 ' c.clientHeight:' + c.clientHeight +
33391                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33392                 ' stop:' + stop +
33393                 ' cbot:' + cbot +
33394                 ' sbot:' + sbot +
33395                 ' ch:' + ch  
33396                 );
33397         */
33398         if(ctop < stop){
33399              c.scrollTop = ctop;
33400             //Roo.log("set scrolltop to ctop DISABLE?");
33401         }else if(cbot > sbot){
33402             //Roo.log("set scrolltop to cbot-ch");
33403             c.scrollTop = cbot-ch;
33404         }
33405         
33406         if(hscroll !== false){
33407             if(cleft < sleft){
33408                 c.scrollLeft = cleft;
33409             }else if(cright > sright){
33410                 c.scrollLeft = cright-c.clientWidth;
33411             }
33412         }
33413          
33414         return el;
33415     },
33416
33417     updateColumns : function(){
33418         this.grid.stopEditing();
33419         var cm = this.grid.colModel, colIds = this.getColumnIds();
33420         //var totalWidth = cm.getTotalWidth();
33421         var pos = 0;
33422         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33423             //if(cm.isHidden(i)) continue;
33424             var w = cm.getColumnWidth(i);
33425             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33426             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33427         }
33428         this.updateSplitters();
33429     },
33430
33431     generateRules : function(cm){
33432         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33433         Roo.util.CSS.removeStyleSheet(rulesId);
33434         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33435             var cid = cm.getColumnId(i);
33436             var align = '';
33437             if(cm.config[i].align){
33438                 align = 'text-align:'+cm.config[i].align+';';
33439             }
33440             var hidden = '';
33441             if(cm.isHidden(i)){
33442                 hidden = 'display:none;';
33443             }
33444             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33445             ruleBuf.push(
33446                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33447                     this.hdSelector, cid, " {\n", align, width, "}\n",
33448                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33449                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33450         }
33451         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33452     },
33453
33454     updateSplitters : function(){
33455         var cm = this.cm, s = this.getSplitters();
33456         if(s){ // splitters not created yet
33457             var pos = 0, locked = true;
33458             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33459                 if(cm.isHidden(i)) {
33460                     continue;
33461                 }
33462                 var w = cm.getColumnWidth(i); // make sure it's a number
33463                 if(!cm.isLocked(i) && locked){
33464                     pos = 0;
33465                     locked = false;
33466                 }
33467                 pos += w;
33468                 s[i].style.left = (pos-this.splitOffset) + "px";
33469             }
33470         }
33471     },
33472
33473     handleHiddenChange : function(colModel, colIndex, hidden){
33474         if(hidden){
33475             this.hideColumn(colIndex);
33476         }else{
33477             this.unhideColumn(colIndex);
33478         }
33479     },
33480
33481     hideColumn : function(colIndex){
33482         var cid = this.getColumnId(colIndex);
33483         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33484         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33485         if(Roo.isSafari){
33486             this.updateHeaders();
33487         }
33488         this.updateSplitters();
33489         this.layout();
33490     },
33491
33492     unhideColumn : function(colIndex){
33493         var cid = this.getColumnId(colIndex);
33494         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33495         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33496
33497         if(Roo.isSafari){
33498             this.updateHeaders();
33499         }
33500         this.updateSplitters();
33501         this.layout();
33502     },
33503
33504     insertRows : function(dm, firstRow, lastRow, isUpdate){
33505         if(firstRow == 0 && lastRow == dm.getCount()-1){
33506             this.refresh();
33507         }else{
33508             if(!isUpdate){
33509                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33510             }
33511             var s = this.getScrollState();
33512             var markup = this.renderRows(firstRow, lastRow);
33513             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33514             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33515             this.restoreScroll(s);
33516             if(!isUpdate){
33517                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33518                 this.syncRowHeights(firstRow, lastRow);
33519                 this.stripeRows(firstRow);
33520                 this.layout();
33521             }
33522         }
33523     },
33524
33525     bufferRows : function(markup, target, index){
33526         var before = null, trows = target.rows, tbody = target.tBodies[0];
33527         if(index < trows.length){
33528             before = trows[index];
33529         }
33530         var b = document.createElement("div");
33531         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33532         var rows = b.firstChild.rows;
33533         for(var i = 0, len = rows.length; i < len; i++){
33534             if(before){
33535                 tbody.insertBefore(rows[0], before);
33536             }else{
33537                 tbody.appendChild(rows[0]);
33538             }
33539         }
33540         b.innerHTML = "";
33541         b = null;
33542     },
33543
33544     deleteRows : function(dm, firstRow, lastRow){
33545         if(dm.getRowCount()<1){
33546             this.fireEvent("beforerefresh", this);
33547             this.mainBody.update("");
33548             this.lockedBody.update("");
33549             this.fireEvent("refresh", this);
33550         }else{
33551             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33552             var bt = this.getBodyTable();
33553             var tbody = bt.firstChild;
33554             var rows = bt.rows;
33555             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33556                 tbody.removeChild(rows[firstRow]);
33557             }
33558             this.stripeRows(firstRow);
33559             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33560         }
33561     },
33562
33563     updateRows : function(dataSource, firstRow, lastRow){
33564         var s = this.getScrollState();
33565         this.refresh();
33566         this.restoreScroll(s);
33567     },
33568
33569     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33570         if(!noRefresh){
33571            this.refresh();
33572         }
33573         this.updateHeaderSortState();
33574     },
33575
33576     getScrollState : function(){
33577         
33578         var sb = this.scroller.dom;
33579         return {left: sb.scrollLeft, top: sb.scrollTop};
33580     },
33581
33582     stripeRows : function(startRow){
33583         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33584             return;
33585         }
33586         startRow = startRow || 0;
33587         var rows = this.getBodyTable().rows;
33588         var lrows = this.getLockedTable().rows;
33589         var cls = ' x-grid-row-alt ';
33590         for(var i = startRow, len = rows.length; i < len; i++){
33591             var row = rows[i], lrow = lrows[i];
33592             var isAlt = ((i+1) % 2 == 0);
33593             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33594             if(isAlt == hasAlt){
33595                 continue;
33596             }
33597             if(isAlt){
33598                 row.className += " x-grid-row-alt";
33599             }else{
33600                 row.className = row.className.replace("x-grid-row-alt", "");
33601             }
33602             if(lrow){
33603                 lrow.className = row.className;
33604             }
33605         }
33606     },
33607
33608     restoreScroll : function(state){
33609         //Roo.log('GridView.restoreScroll');
33610         var sb = this.scroller.dom;
33611         sb.scrollLeft = state.left;
33612         sb.scrollTop = state.top;
33613         this.syncScroll();
33614     },
33615
33616     syncScroll : function(){
33617         //Roo.log('GridView.syncScroll');
33618         var sb = this.scroller.dom;
33619         var sh = this.mainHd.dom;
33620         var bs = this.mainBody.dom;
33621         var lv = this.lockedBody.dom;
33622         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33623         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33624     },
33625
33626     handleScroll : function(e){
33627         this.syncScroll();
33628         var sb = this.scroller.dom;
33629         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33630         e.stopEvent();
33631     },
33632
33633     handleWheel : function(e){
33634         var d = e.getWheelDelta();
33635         this.scroller.dom.scrollTop -= d*22;
33636         // set this here to prevent jumpy scrolling on large tables
33637         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33638         e.stopEvent();
33639     },
33640
33641     renderRows : function(startRow, endRow){
33642         // pull in all the crap needed to render rows
33643         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33644         var colCount = cm.getColumnCount();
33645
33646         if(ds.getCount() < 1){
33647             return ["", ""];
33648         }
33649
33650         // build a map for all the columns
33651         var cs = [];
33652         for(var i = 0; i < colCount; i++){
33653             var name = cm.getDataIndex(i);
33654             cs[i] = {
33655                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33656                 renderer : cm.getRenderer(i),
33657                 id : cm.getColumnId(i),
33658                 locked : cm.isLocked(i),
33659                 has_editor : cm.isCellEditable(i)
33660             };
33661         }
33662
33663         startRow = startRow || 0;
33664         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33665
33666         // records to render
33667         var rs = ds.getRange(startRow, endRow);
33668
33669         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33670     },
33671
33672     // As much as I hate to duplicate code, this was branched because FireFox really hates
33673     // [].join("") on strings. The performance difference was substantial enough to
33674     // branch this function
33675     doRender : Roo.isGecko ?
33676             function(cs, rs, ds, startRow, colCount, stripe){
33677                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33678                 // buffers
33679                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33680                 
33681                 var hasListener = this.grid.hasListener('rowclass');
33682                 var rowcfg = {};
33683                 for(var j = 0, len = rs.length; j < len; j++){
33684                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33685                     for(var i = 0; i < colCount; i++){
33686                         c = cs[i];
33687                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33688                         p.id = c.id;
33689                         p.css = p.attr = "";
33690                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33691                         if(p.value == undefined || p.value === "") {
33692                             p.value = "&#160;";
33693                         }
33694                         if(c.has_editor){
33695                             p.css += ' x-grid-editable-cell';
33696                         }
33697                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33698                             p.css +=  ' x-grid-dirty-cell';
33699                         }
33700                         var markup = ct.apply(p);
33701                         if(!c.locked){
33702                             cb+= markup;
33703                         }else{
33704                             lcb+= markup;
33705                         }
33706                     }
33707                     var alt = [];
33708                     if(stripe && ((rowIndex+1) % 2 == 0)){
33709                         alt.push("x-grid-row-alt")
33710                     }
33711                     if(r.dirty){
33712                         alt.push(  " x-grid-dirty-row");
33713                     }
33714                     rp.cells = lcb;
33715                     if(this.getRowClass){
33716                         alt.push(this.getRowClass(r, rowIndex));
33717                     }
33718                     if (hasListener) {
33719                         rowcfg = {
33720                              
33721                             record: r,
33722                             rowIndex : rowIndex,
33723                             rowClass : ''
33724                         };
33725                         this.grid.fireEvent('rowclass', this, rowcfg);
33726                         alt.push(rowcfg.rowClass);
33727                     }
33728                     rp.alt = alt.join(" ");
33729                     lbuf+= rt.apply(rp);
33730                     rp.cells = cb;
33731                     buf+=  rt.apply(rp);
33732                 }
33733                 return [lbuf, buf];
33734             } :
33735             function(cs, rs, ds, startRow, colCount, stripe){
33736                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33737                 // buffers
33738                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33739                 var hasListener = this.grid.hasListener('rowclass');
33740  
33741                 var rowcfg = {};
33742                 for(var j = 0, len = rs.length; j < len; j++){
33743                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33744                     for(var i = 0; i < colCount; i++){
33745                         c = cs[i];
33746                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33747                         p.id = c.id;
33748                         p.css = p.attr = "";
33749                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33750                         if(p.value == undefined || p.value === "") {
33751                             p.value = "&#160;";
33752                         }
33753                         //Roo.log(c);
33754                          if(c.has_editor){
33755                             p.css += ' x-grid-editable-cell';
33756                         }
33757                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33758                             p.css += ' x-grid-dirty-cell' 
33759                         }
33760                         
33761                         var markup = ct.apply(p);
33762                         if(!c.locked){
33763                             cb[cb.length] = markup;
33764                         }else{
33765                             lcb[lcb.length] = markup;
33766                         }
33767                     }
33768                     var alt = [];
33769                     if(stripe && ((rowIndex+1) % 2 == 0)){
33770                         alt.push( "x-grid-row-alt");
33771                     }
33772                     if(r.dirty){
33773                         alt.push(" x-grid-dirty-row");
33774                     }
33775                     rp.cells = lcb;
33776                     if(this.getRowClass){
33777                         alt.push( this.getRowClass(r, rowIndex));
33778                     }
33779                     if (hasListener) {
33780                         rowcfg = {
33781                              
33782                             record: r,
33783                             rowIndex : rowIndex,
33784                             rowClass : ''
33785                         };
33786                         this.grid.fireEvent('rowclass', this, rowcfg);
33787                         alt.push(rowcfg.rowClass);
33788                     }
33789                     
33790                     rp.alt = alt.join(" ");
33791                     rp.cells = lcb.join("");
33792                     lbuf[lbuf.length] = rt.apply(rp);
33793                     rp.cells = cb.join("");
33794                     buf[buf.length] =  rt.apply(rp);
33795                 }
33796                 return [lbuf.join(""), buf.join("")];
33797             },
33798
33799     renderBody : function(){
33800         var markup = this.renderRows();
33801         var bt = this.templates.body;
33802         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33803     },
33804
33805     /**
33806      * Refreshes the grid
33807      * @param {Boolean} headersToo
33808      */
33809     refresh : function(headersToo){
33810         this.fireEvent("beforerefresh", this);
33811         this.grid.stopEditing();
33812         var result = this.renderBody();
33813         this.lockedBody.update(result[0]);
33814         this.mainBody.update(result[1]);
33815         if(headersToo === true){
33816             this.updateHeaders();
33817             this.updateColumns();
33818             this.updateSplitters();
33819             this.updateHeaderSortState();
33820         }
33821         this.syncRowHeights();
33822         this.layout();
33823         this.fireEvent("refresh", this);
33824     },
33825
33826     handleColumnMove : function(cm, oldIndex, newIndex){
33827         this.indexMap = null;
33828         var s = this.getScrollState();
33829         this.refresh(true);
33830         this.restoreScroll(s);
33831         this.afterMove(newIndex);
33832     },
33833
33834     afterMove : function(colIndex){
33835         if(this.enableMoveAnim && Roo.enableFx){
33836             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33837         }
33838         // if multisort - fix sortOrder, and reload..
33839         if (this.grid.dataSource.multiSort) {
33840             // the we can call sort again..
33841             var dm = this.grid.dataSource;
33842             var cm = this.grid.colModel;
33843             var so = [];
33844             for(var i = 0; i < cm.config.length; i++ ) {
33845                 
33846                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33847                     continue; // dont' bother, it's not in sort list or being set.
33848                 }
33849                 
33850                 so.push(cm.config[i].dataIndex);
33851             };
33852             dm.sortOrder = so;
33853             dm.load(dm.lastOptions);
33854             
33855             
33856         }
33857         
33858     },
33859
33860     updateCell : function(dm, rowIndex, dataIndex){
33861         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33862         if(typeof colIndex == "undefined"){ // not present in grid
33863             return;
33864         }
33865         var cm = this.grid.colModel;
33866         var cell = this.getCell(rowIndex, colIndex);
33867         var cellText = this.getCellText(rowIndex, colIndex);
33868
33869         var p = {
33870             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33871             id : cm.getColumnId(colIndex),
33872             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33873         };
33874         var renderer = cm.getRenderer(colIndex);
33875         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33876         if(typeof val == "undefined" || val === "") {
33877             val = "&#160;";
33878         }
33879         cellText.innerHTML = val;
33880         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33881         this.syncRowHeights(rowIndex, rowIndex);
33882     },
33883
33884     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33885         var maxWidth = 0;
33886         if(this.grid.autoSizeHeaders){
33887             var h = this.getHeaderCellMeasure(colIndex);
33888             maxWidth = Math.max(maxWidth, h.scrollWidth);
33889         }
33890         var tb, index;
33891         if(this.cm.isLocked(colIndex)){
33892             tb = this.getLockedTable();
33893             index = colIndex;
33894         }else{
33895             tb = this.getBodyTable();
33896             index = colIndex - this.cm.getLockedCount();
33897         }
33898         if(tb && tb.rows){
33899             var rows = tb.rows;
33900             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33901             for(var i = 0; i < stopIndex; i++){
33902                 var cell = rows[i].childNodes[index].firstChild;
33903                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33904             }
33905         }
33906         return maxWidth + /*margin for error in IE*/ 5;
33907     },
33908     /**
33909      * Autofit a column to its content.
33910      * @param {Number} colIndex
33911      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33912      */
33913      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33914          if(this.cm.isHidden(colIndex)){
33915              return; // can't calc a hidden column
33916          }
33917         if(forceMinSize){
33918             var cid = this.cm.getColumnId(colIndex);
33919             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33920            if(this.grid.autoSizeHeaders){
33921                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33922            }
33923         }
33924         var newWidth = this.calcColumnWidth(colIndex);
33925         this.cm.setColumnWidth(colIndex,
33926             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33927         if(!suppressEvent){
33928             this.grid.fireEvent("columnresize", colIndex, newWidth);
33929         }
33930     },
33931
33932     /**
33933      * Autofits all columns to their content and then expands to fit any extra space in the grid
33934      */
33935      autoSizeColumns : function(){
33936         var cm = this.grid.colModel;
33937         var colCount = cm.getColumnCount();
33938         for(var i = 0; i < colCount; i++){
33939             this.autoSizeColumn(i, true, true);
33940         }
33941         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33942             this.fitColumns();
33943         }else{
33944             this.updateColumns();
33945             this.layout();
33946         }
33947     },
33948
33949     /**
33950      * Autofits all columns to the grid's width proportionate with their current size
33951      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33952      */
33953     fitColumns : function(reserveScrollSpace){
33954         var cm = this.grid.colModel;
33955         var colCount = cm.getColumnCount();
33956         var cols = [];
33957         var width = 0;
33958         var i, w;
33959         for (i = 0; i < colCount; i++){
33960             if(!cm.isHidden(i) && !cm.isFixed(i)){
33961                 w = cm.getColumnWidth(i);
33962                 cols.push(i);
33963                 cols.push(w);
33964                 width += w;
33965             }
33966         }
33967         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33968         if(reserveScrollSpace){
33969             avail -= 17;
33970         }
33971         var frac = (avail - cm.getTotalWidth())/width;
33972         while (cols.length){
33973             w = cols.pop();
33974             i = cols.pop();
33975             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33976         }
33977         this.updateColumns();
33978         this.layout();
33979     },
33980
33981     onRowSelect : function(rowIndex){
33982         var row = this.getRowComposite(rowIndex);
33983         row.addClass("x-grid-row-selected");
33984     },
33985
33986     onRowDeselect : function(rowIndex){
33987         var row = this.getRowComposite(rowIndex);
33988         row.removeClass("x-grid-row-selected");
33989     },
33990
33991     onCellSelect : function(row, col){
33992         var cell = this.getCell(row, col);
33993         if(cell){
33994             Roo.fly(cell).addClass("x-grid-cell-selected");
33995         }
33996     },
33997
33998     onCellDeselect : function(row, col){
33999         var cell = this.getCell(row, col);
34000         if(cell){
34001             Roo.fly(cell).removeClass("x-grid-cell-selected");
34002         }
34003     },
34004
34005     updateHeaderSortState : function(){
34006         
34007         // sort state can be single { field: xxx, direction : yyy}
34008         // or   { xxx=>ASC , yyy : DESC ..... }
34009         
34010         var mstate = {};
34011         if (!this.ds.multiSort) { 
34012             var state = this.ds.getSortState();
34013             if(!state){
34014                 return;
34015             }
34016             mstate[state.field] = state.direction;
34017             // FIXME... - this is not used here.. but might be elsewhere..
34018             this.sortState = state;
34019             
34020         } else {
34021             mstate = this.ds.sortToggle;
34022         }
34023         //remove existing sort classes..
34024         
34025         var sc = this.sortClasses;
34026         var hds = this.el.select(this.headerSelector).removeClass(sc);
34027         
34028         for(var f in mstate) {
34029         
34030             var sortColumn = this.cm.findColumnIndex(f);
34031             
34032             if(sortColumn != -1){
34033                 var sortDir = mstate[f];        
34034                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34035             }
34036         }
34037         
34038          
34039         
34040     },
34041
34042
34043     handleHeaderClick : function(g, index,e){
34044         
34045         Roo.log("header click");
34046         
34047         if (Roo.isTouch) {
34048             // touch events on header are handled by context
34049             this.handleHdCtx(g,index,e);
34050             return;
34051         }
34052         
34053         
34054         if(this.headersDisabled){
34055             return;
34056         }
34057         var dm = g.dataSource, cm = g.colModel;
34058         if(!cm.isSortable(index)){
34059             return;
34060         }
34061         g.stopEditing();
34062         
34063         if (dm.multiSort) {
34064             // update the sortOrder
34065             var so = [];
34066             for(var i = 0; i < cm.config.length; i++ ) {
34067                 
34068                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34069                     continue; // dont' bother, it's not in sort list or being set.
34070                 }
34071                 
34072                 so.push(cm.config[i].dataIndex);
34073             };
34074             dm.sortOrder = so;
34075         }
34076         
34077         
34078         dm.sort(cm.getDataIndex(index));
34079     },
34080
34081
34082     destroy : function(){
34083         if(this.colMenu){
34084             this.colMenu.removeAll();
34085             Roo.menu.MenuMgr.unregister(this.colMenu);
34086             this.colMenu.getEl().remove();
34087             delete this.colMenu;
34088         }
34089         if(this.hmenu){
34090             this.hmenu.removeAll();
34091             Roo.menu.MenuMgr.unregister(this.hmenu);
34092             this.hmenu.getEl().remove();
34093             delete this.hmenu;
34094         }
34095         if(this.grid.enableColumnMove){
34096             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34097             if(dds){
34098                 for(var dd in dds){
34099                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34100                         var elid = dds[dd].dragElId;
34101                         dds[dd].unreg();
34102                         Roo.get(elid).remove();
34103                     } else if(dds[dd].config.isTarget){
34104                         dds[dd].proxyTop.remove();
34105                         dds[dd].proxyBottom.remove();
34106                         dds[dd].unreg();
34107                     }
34108                     if(Roo.dd.DDM.locationCache[dd]){
34109                         delete Roo.dd.DDM.locationCache[dd];
34110                     }
34111                 }
34112                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34113             }
34114         }
34115         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34116         this.bind(null, null);
34117         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34118     },
34119
34120     handleLockChange : function(){
34121         this.refresh(true);
34122     },
34123
34124     onDenyColumnLock : function(){
34125
34126     },
34127
34128     onDenyColumnHide : function(){
34129
34130     },
34131
34132     handleHdMenuClick : function(item){
34133         var index = this.hdCtxIndex;
34134         var cm = this.cm, ds = this.ds;
34135         switch(item.id){
34136             case "asc":
34137                 ds.sort(cm.getDataIndex(index), "ASC");
34138                 break;
34139             case "desc":
34140                 ds.sort(cm.getDataIndex(index), "DESC");
34141                 break;
34142             case "lock":
34143                 var lc = cm.getLockedCount();
34144                 if(cm.getColumnCount(true) <= lc+1){
34145                     this.onDenyColumnLock();
34146                     return;
34147                 }
34148                 if(lc != index){
34149                     cm.setLocked(index, true, true);
34150                     cm.moveColumn(index, lc);
34151                     this.grid.fireEvent("columnmove", index, lc);
34152                 }else{
34153                     cm.setLocked(index, true);
34154                 }
34155             break;
34156             case "unlock":
34157                 var lc = cm.getLockedCount();
34158                 if((lc-1) != index){
34159                     cm.setLocked(index, false, true);
34160                     cm.moveColumn(index, lc-1);
34161                     this.grid.fireEvent("columnmove", index, lc-1);
34162                 }else{
34163                     cm.setLocked(index, false);
34164                 }
34165             break;
34166             case 'wider': // used to expand cols on touch..
34167             case 'narrow':
34168                 var cw = cm.getColumnWidth(index);
34169                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34170                 cw = Math.max(0, cw);
34171                 cw = Math.min(cw,4000);
34172                 cm.setColumnWidth(index, cw);
34173                 break;
34174                 
34175             default:
34176                 index = cm.getIndexById(item.id.substr(4));
34177                 if(index != -1){
34178                     if(item.checked && cm.getColumnCount(true) <= 1){
34179                         this.onDenyColumnHide();
34180                         return false;
34181                     }
34182                     cm.setHidden(index, item.checked);
34183                 }
34184         }
34185         return true;
34186     },
34187
34188     beforeColMenuShow : function(){
34189         var cm = this.cm,  colCount = cm.getColumnCount();
34190         this.colMenu.removeAll();
34191         for(var i = 0; i < colCount; i++){
34192             this.colMenu.add(new Roo.menu.CheckItem({
34193                 id: "col-"+cm.getColumnId(i),
34194                 text: cm.getColumnHeader(i),
34195                 checked: !cm.isHidden(i),
34196                 hideOnClick:false
34197             }));
34198         }
34199     },
34200
34201     handleHdCtx : function(g, index, e){
34202         e.stopEvent();
34203         var hd = this.getHeaderCell(index);
34204         this.hdCtxIndex = index;
34205         var ms = this.hmenu.items, cm = this.cm;
34206         ms.get("asc").setDisabled(!cm.isSortable(index));
34207         ms.get("desc").setDisabled(!cm.isSortable(index));
34208         if(this.grid.enableColLock !== false){
34209             ms.get("lock").setDisabled(cm.isLocked(index));
34210             ms.get("unlock").setDisabled(!cm.isLocked(index));
34211         }
34212         this.hmenu.show(hd, "tl-bl");
34213     },
34214
34215     handleHdOver : function(e){
34216         var hd = this.findHeaderCell(e.getTarget());
34217         if(hd && !this.headersDisabled){
34218             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34219                this.fly(hd).addClass("x-grid-hd-over");
34220             }
34221         }
34222     },
34223
34224     handleHdOut : function(e){
34225         var hd = this.findHeaderCell(e.getTarget());
34226         if(hd){
34227             this.fly(hd).removeClass("x-grid-hd-over");
34228         }
34229     },
34230
34231     handleSplitDblClick : function(e, t){
34232         var i = this.getCellIndex(t);
34233         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34234             this.autoSizeColumn(i, true);
34235             this.layout();
34236         }
34237     },
34238
34239     render : function(){
34240
34241         var cm = this.cm;
34242         var colCount = cm.getColumnCount();
34243
34244         if(this.grid.monitorWindowResize === true){
34245             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34246         }
34247         var header = this.renderHeaders();
34248         var body = this.templates.body.apply({rows:""});
34249         var html = this.templates.master.apply({
34250             lockedBody: body,
34251             body: body,
34252             lockedHeader: header[0],
34253             header: header[1]
34254         });
34255
34256         //this.updateColumns();
34257
34258         this.grid.getGridEl().dom.innerHTML = html;
34259
34260         this.initElements();
34261         
34262         // a kludge to fix the random scolling effect in webkit
34263         this.el.on("scroll", function() {
34264             this.el.dom.scrollTop=0; // hopefully not recursive..
34265         },this);
34266
34267         this.scroller.on("scroll", this.handleScroll, this);
34268         this.lockedBody.on("mousewheel", this.handleWheel, this);
34269         this.mainBody.on("mousewheel", this.handleWheel, this);
34270
34271         this.mainHd.on("mouseover", this.handleHdOver, this);
34272         this.mainHd.on("mouseout", this.handleHdOut, this);
34273         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34274                 {delegate: "."+this.splitClass});
34275
34276         this.lockedHd.on("mouseover", this.handleHdOver, this);
34277         this.lockedHd.on("mouseout", this.handleHdOut, this);
34278         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34279                 {delegate: "."+this.splitClass});
34280
34281         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34282             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34283         }
34284
34285         this.updateSplitters();
34286
34287         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34288             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34289             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34290         }
34291
34292         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34293             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34294             this.hmenu.add(
34295                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34296                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34297             );
34298             if(this.grid.enableColLock !== false){
34299                 this.hmenu.add('-',
34300                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34301                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34302                 );
34303             }
34304             if (Roo.isTouch) {
34305                  this.hmenu.add('-',
34306                     {id:"wider", text: this.columnsWiderText},
34307                     {id:"narrow", text: this.columnsNarrowText }
34308                 );
34309                 
34310                  
34311             }
34312             
34313             if(this.grid.enableColumnHide !== false){
34314
34315                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34316                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34317                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34318
34319                 this.hmenu.add('-',
34320                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34321                 );
34322             }
34323             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34324
34325             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34326         }
34327
34328         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34329             this.dd = new Roo.grid.GridDragZone(this.grid, {
34330                 ddGroup : this.grid.ddGroup || 'GridDD'
34331             });
34332             
34333         }
34334
34335         /*
34336         for(var i = 0; i < colCount; i++){
34337             if(cm.isHidden(i)){
34338                 this.hideColumn(i);
34339             }
34340             if(cm.config[i].align){
34341                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34342                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34343             }
34344         }*/
34345         
34346         this.updateHeaderSortState();
34347
34348         this.beforeInitialResize();
34349         this.layout(true);
34350
34351         // two part rendering gives faster view to the user
34352         this.renderPhase2.defer(1, this);
34353     },
34354
34355     renderPhase2 : function(){
34356         // render the rows now
34357         this.refresh();
34358         if(this.grid.autoSizeColumns){
34359             this.autoSizeColumns();
34360         }
34361     },
34362
34363     beforeInitialResize : function(){
34364
34365     },
34366
34367     onColumnSplitterMoved : function(i, w){
34368         this.userResized = true;
34369         var cm = this.grid.colModel;
34370         cm.setColumnWidth(i, w, true);
34371         var cid = cm.getColumnId(i);
34372         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34373         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34374         this.updateSplitters();
34375         this.layout();
34376         this.grid.fireEvent("columnresize", i, w);
34377     },
34378
34379     syncRowHeights : function(startIndex, endIndex){
34380         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34381             startIndex = startIndex || 0;
34382             var mrows = this.getBodyTable().rows;
34383             var lrows = this.getLockedTable().rows;
34384             var len = mrows.length-1;
34385             endIndex = Math.min(endIndex || len, len);
34386             for(var i = startIndex; i <= endIndex; i++){
34387                 var m = mrows[i], l = lrows[i];
34388                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34389                 m.style.height = l.style.height = h + "px";
34390             }
34391         }
34392     },
34393
34394     layout : function(initialRender, is2ndPass){
34395         var g = this.grid;
34396         var auto = g.autoHeight;
34397         var scrollOffset = 16;
34398         var c = g.getGridEl(), cm = this.cm,
34399                 expandCol = g.autoExpandColumn,
34400                 gv = this;
34401         //c.beginMeasure();
34402
34403         if(!c.dom.offsetWidth){ // display:none?
34404             if(initialRender){
34405                 this.lockedWrap.show();
34406                 this.mainWrap.show();
34407             }
34408             return;
34409         }
34410
34411         var hasLock = this.cm.isLocked(0);
34412
34413         var tbh = this.headerPanel.getHeight();
34414         var bbh = this.footerPanel.getHeight();
34415
34416         if(auto){
34417             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34418             var newHeight = ch + c.getBorderWidth("tb");
34419             if(g.maxHeight){
34420                 newHeight = Math.min(g.maxHeight, newHeight);
34421             }
34422             c.setHeight(newHeight);
34423         }
34424
34425         if(g.autoWidth){
34426             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34427         }
34428
34429         var s = this.scroller;
34430
34431         var csize = c.getSize(true);
34432
34433         this.el.setSize(csize.width, csize.height);
34434
34435         this.headerPanel.setWidth(csize.width);
34436         this.footerPanel.setWidth(csize.width);
34437
34438         var hdHeight = this.mainHd.getHeight();
34439         var vw = csize.width;
34440         var vh = csize.height - (tbh + bbh);
34441
34442         s.setSize(vw, vh);
34443
34444         var bt = this.getBodyTable();
34445         
34446         if(cm.getLockedCount() == cm.config.length){
34447             bt = this.getLockedTable();
34448         }
34449         
34450         var ltWidth = hasLock ?
34451                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34452
34453         var scrollHeight = bt.offsetHeight;
34454         var scrollWidth = ltWidth + bt.offsetWidth;
34455         var vscroll = false, hscroll = false;
34456
34457         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34458
34459         var lw = this.lockedWrap, mw = this.mainWrap;
34460         var lb = this.lockedBody, mb = this.mainBody;
34461
34462         setTimeout(function(){
34463             var t = s.dom.offsetTop;
34464             var w = s.dom.clientWidth,
34465                 h = s.dom.clientHeight;
34466
34467             lw.setTop(t);
34468             lw.setSize(ltWidth, h);
34469
34470             mw.setLeftTop(ltWidth, t);
34471             mw.setSize(w-ltWidth, h);
34472
34473             lb.setHeight(h-hdHeight);
34474             mb.setHeight(h-hdHeight);
34475
34476             if(is2ndPass !== true && !gv.userResized && expandCol){
34477                 // high speed resize without full column calculation
34478                 
34479                 var ci = cm.getIndexById(expandCol);
34480                 if (ci < 0) {
34481                     ci = cm.findColumnIndex(expandCol);
34482                 }
34483                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34484                 var expandId = cm.getColumnId(ci);
34485                 var  tw = cm.getTotalWidth(false);
34486                 var currentWidth = cm.getColumnWidth(ci);
34487                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34488                 if(currentWidth != cw){
34489                     cm.setColumnWidth(ci, cw, true);
34490                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34491                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34492                     gv.updateSplitters();
34493                     gv.layout(false, true);
34494                 }
34495             }
34496
34497             if(initialRender){
34498                 lw.show();
34499                 mw.show();
34500             }
34501             //c.endMeasure();
34502         }, 10);
34503     },
34504
34505     onWindowResize : function(){
34506         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34507             return;
34508         }
34509         this.layout();
34510     },
34511
34512     appendFooter : function(parentEl){
34513         return null;
34514     },
34515
34516     sortAscText : "Sort Ascending",
34517     sortDescText : "Sort Descending",
34518     lockText : "Lock Column",
34519     unlockText : "Unlock Column",
34520     columnsText : "Columns",
34521  
34522     columnsWiderText : "Wider",
34523     columnsNarrowText : "Thinner"
34524 });
34525
34526
34527 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34528     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34529     this.proxy.el.addClass('x-grid3-col-dd');
34530 };
34531
34532 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34533     handleMouseDown : function(e){
34534
34535     },
34536
34537     callHandleMouseDown : function(e){
34538         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34539     }
34540 });
34541 /*
34542  * Based on:
34543  * Ext JS Library 1.1.1
34544  * Copyright(c) 2006-2007, Ext JS, LLC.
34545  *
34546  * Originally Released Under LGPL - original licence link has changed is not relivant.
34547  *
34548  * Fork - LGPL
34549  * <script type="text/javascript">
34550  */
34551  
34552 // private
34553 // This is a support class used internally by the Grid components
34554 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34555     this.grid = grid;
34556     this.view = grid.getView();
34557     this.proxy = this.view.resizeProxy;
34558     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34559         "gridSplitters" + this.grid.getGridEl().id, {
34560         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34561     });
34562     this.setHandleElId(Roo.id(hd));
34563     this.setOuterHandleElId(Roo.id(hd2));
34564     this.scroll = false;
34565 };
34566 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34567     fly: Roo.Element.fly,
34568
34569     b4StartDrag : function(x, y){
34570         this.view.headersDisabled = true;
34571         this.proxy.setHeight(this.view.mainWrap.getHeight());
34572         var w = this.cm.getColumnWidth(this.cellIndex);
34573         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34574         this.resetConstraints();
34575         this.setXConstraint(minw, 1000);
34576         this.setYConstraint(0, 0);
34577         this.minX = x - minw;
34578         this.maxX = x + 1000;
34579         this.startPos = x;
34580         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34581     },
34582
34583
34584     handleMouseDown : function(e){
34585         ev = Roo.EventObject.setEvent(e);
34586         var t = this.fly(ev.getTarget());
34587         if(t.hasClass("x-grid-split")){
34588             this.cellIndex = this.view.getCellIndex(t.dom);
34589             this.split = t.dom;
34590             this.cm = this.grid.colModel;
34591             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34592                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34593             }
34594         }
34595     },
34596
34597     endDrag : function(e){
34598         this.view.headersDisabled = false;
34599         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34600         var diff = endX - this.startPos;
34601         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34602     },
34603
34604     autoOffset : function(){
34605         this.setDelta(0,0);
34606     }
34607 });/*
34608  * Based on:
34609  * Ext JS Library 1.1.1
34610  * Copyright(c) 2006-2007, Ext JS, LLC.
34611  *
34612  * Originally Released Under LGPL - original licence link has changed is not relivant.
34613  *
34614  * Fork - LGPL
34615  * <script type="text/javascript">
34616  */
34617  
34618 // private
34619 // This is a support class used internally by the Grid components
34620 Roo.grid.GridDragZone = function(grid, config){
34621     this.view = grid.getView();
34622     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34623     if(this.view.lockedBody){
34624         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34625         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34626     }
34627     this.scroll = false;
34628     this.grid = grid;
34629     this.ddel = document.createElement('div');
34630     this.ddel.className = 'x-grid-dd-wrap';
34631 };
34632
34633 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34634     ddGroup : "GridDD",
34635
34636     getDragData : function(e){
34637         var t = Roo.lib.Event.getTarget(e);
34638         var rowIndex = this.view.findRowIndex(t);
34639         var sm = this.grid.selModel;
34640             
34641         //Roo.log(rowIndex);
34642         
34643         if (sm.getSelectedCell) {
34644             // cell selection..
34645             if (!sm.getSelectedCell()) {
34646                 return false;
34647             }
34648             if (rowIndex != sm.getSelectedCell()[0]) {
34649                 return false;
34650             }
34651         
34652         }
34653         
34654         if(rowIndex !== false){
34655             
34656             // if editorgrid.. 
34657             
34658             
34659             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34660                
34661             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34662               //  
34663             //}
34664             if (e.hasModifier()){
34665                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34666             }
34667             
34668             Roo.log("getDragData");
34669             
34670             return {
34671                 grid: this.grid,
34672                 ddel: this.ddel,
34673                 rowIndex: rowIndex,
34674                 selections:sm.getSelections ? sm.getSelections() : (
34675                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34676                 )
34677             };
34678         }
34679         return false;
34680     },
34681
34682     onInitDrag : function(e){
34683         var data = this.dragData;
34684         this.ddel.innerHTML = this.grid.getDragDropText();
34685         this.proxy.update(this.ddel);
34686         // fire start drag?
34687     },
34688
34689     afterRepair : function(){
34690         this.dragging = false;
34691     },
34692
34693     getRepairXY : function(e, data){
34694         return false;
34695     },
34696
34697     onEndDrag : function(data, e){
34698         // fire end drag?
34699     },
34700
34701     onValidDrop : function(dd, e, id){
34702         // fire drag drop?
34703         this.hideProxy();
34704     },
34705
34706     beforeInvalidDrop : function(e, id){
34707
34708     }
34709 });/*
34710  * Based on:
34711  * Ext JS Library 1.1.1
34712  * Copyright(c) 2006-2007, Ext JS, LLC.
34713  *
34714  * Originally Released Under LGPL - original licence link has changed is not relivant.
34715  *
34716  * Fork - LGPL
34717  * <script type="text/javascript">
34718  */
34719  
34720
34721 /**
34722  * @class Roo.grid.ColumnModel
34723  * @extends Roo.util.Observable
34724  * This is the default implementation of a ColumnModel used by the Grid. It defines
34725  * the columns in the grid.
34726  * <br>Usage:<br>
34727  <pre><code>
34728  var colModel = new Roo.grid.ColumnModel([
34729         {header: "Ticker", width: 60, sortable: true, locked: true},
34730         {header: "Company Name", width: 150, sortable: true},
34731         {header: "Market Cap.", width: 100, sortable: true},
34732         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34733         {header: "Employees", width: 100, sortable: true, resizable: false}
34734  ]);
34735  </code></pre>
34736  * <p>
34737  
34738  * The config options listed for this class are options which may appear in each
34739  * individual column definition.
34740  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34741  * @constructor
34742  * @param {Object} config An Array of column config objects. See this class's
34743  * config objects for details.
34744 */
34745 Roo.grid.ColumnModel = function(config){
34746         /**
34747      * The config passed into the constructor
34748      */
34749     this.config = config;
34750     this.lookup = {};
34751
34752     // if no id, create one
34753     // if the column does not have a dataIndex mapping,
34754     // map it to the order it is in the config
34755     for(var i = 0, len = config.length; i < len; i++){
34756         var c = config[i];
34757         if(typeof c.dataIndex == "undefined"){
34758             c.dataIndex = i;
34759         }
34760         if(typeof c.renderer == "string"){
34761             c.renderer = Roo.util.Format[c.renderer];
34762         }
34763         if(typeof c.id == "undefined"){
34764             c.id = Roo.id();
34765         }
34766         if(c.editor && c.editor.xtype){
34767             c.editor  = Roo.factory(c.editor, Roo.grid);
34768         }
34769         if(c.editor && c.editor.isFormField){
34770             c.editor = new Roo.grid.GridEditor(c.editor);
34771         }
34772         this.lookup[c.id] = c;
34773     }
34774
34775     /**
34776      * The width of columns which have no width specified (defaults to 100)
34777      * @type Number
34778      */
34779     this.defaultWidth = 100;
34780
34781     /**
34782      * Default sortable of columns which have no sortable specified (defaults to false)
34783      * @type Boolean
34784      */
34785     this.defaultSortable = false;
34786
34787     this.addEvents({
34788         /**
34789              * @event widthchange
34790              * Fires when the width of a column changes.
34791              * @param {ColumnModel} this
34792              * @param {Number} columnIndex The column index
34793              * @param {Number} newWidth The new width
34794              */
34795             "widthchange": true,
34796         /**
34797              * @event headerchange
34798              * Fires when the text of a header changes.
34799              * @param {ColumnModel} this
34800              * @param {Number} columnIndex The column index
34801              * @param {Number} newText The new header text
34802              */
34803             "headerchange": true,
34804         /**
34805              * @event hiddenchange
34806              * Fires when a column is hidden or "unhidden".
34807              * @param {ColumnModel} this
34808              * @param {Number} columnIndex The column index
34809              * @param {Boolean} hidden true if hidden, false otherwise
34810              */
34811             "hiddenchange": true,
34812             /**
34813          * @event columnmoved
34814          * Fires when a column is moved.
34815          * @param {ColumnModel} this
34816          * @param {Number} oldIndex
34817          * @param {Number} newIndex
34818          */
34819         "columnmoved" : true,
34820         /**
34821          * @event columlockchange
34822          * Fires when a column's locked state is changed
34823          * @param {ColumnModel} this
34824          * @param {Number} colIndex
34825          * @param {Boolean} locked true if locked
34826          */
34827         "columnlockchange" : true
34828     });
34829     Roo.grid.ColumnModel.superclass.constructor.call(this);
34830 };
34831 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34832     /**
34833      * @cfg {String} header The header text to display in the Grid view.
34834      */
34835     /**
34836      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34837      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34838      * specified, the column's index is used as an index into the Record's data Array.
34839      */
34840     /**
34841      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34842      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34843      */
34844     /**
34845      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34846      * Defaults to the value of the {@link #defaultSortable} property.
34847      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34848      */
34849     /**
34850      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34851      */
34852     /**
34853      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34854      */
34855     /**
34856      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34857      */
34858     /**
34859      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34860      */
34861     /**
34862      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34863      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34864      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34865      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34866      */
34867        /**
34868      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34869      */
34870     /**
34871      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34872      */
34873     /**
34874      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
34875      */
34876     /**
34877      * @cfg {String} cursor (Optional)
34878      */
34879     /**
34880      * @cfg {String} tooltip (Optional)
34881      */
34882     /**
34883      * @cfg {Number} xs (Optional)
34884      */
34885     /**
34886      * @cfg {Number} sm (Optional)
34887      */
34888     /**
34889      * @cfg {Number} md (Optional)
34890      */
34891     /**
34892      * @cfg {Number} lg (Optional)
34893      */
34894     /**
34895      * Returns the id of the column at the specified index.
34896      * @param {Number} index The column index
34897      * @return {String} the id
34898      */
34899     getColumnId : function(index){
34900         return this.config[index].id;
34901     },
34902
34903     /**
34904      * Returns the column for a specified id.
34905      * @param {String} id The column id
34906      * @return {Object} the column
34907      */
34908     getColumnById : function(id){
34909         return this.lookup[id];
34910     },
34911
34912     
34913     /**
34914      * Returns the column for a specified dataIndex.
34915      * @param {String} dataIndex The column dataIndex
34916      * @return {Object|Boolean} the column or false if not found
34917      */
34918     getColumnByDataIndex: function(dataIndex){
34919         var index = this.findColumnIndex(dataIndex);
34920         return index > -1 ? this.config[index] : false;
34921     },
34922     
34923     /**
34924      * Returns the index for a specified column id.
34925      * @param {String} id The column id
34926      * @return {Number} the index, or -1 if not found
34927      */
34928     getIndexById : function(id){
34929         for(var i = 0, len = this.config.length; i < len; i++){
34930             if(this.config[i].id == id){
34931                 return i;
34932             }
34933         }
34934         return -1;
34935     },
34936     
34937     /**
34938      * Returns the index for a specified column dataIndex.
34939      * @param {String} dataIndex The column dataIndex
34940      * @return {Number} the index, or -1 if not found
34941      */
34942     
34943     findColumnIndex : function(dataIndex){
34944         for(var i = 0, len = this.config.length; i < len; i++){
34945             if(this.config[i].dataIndex == dataIndex){
34946                 return i;
34947             }
34948         }
34949         return -1;
34950     },
34951     
34952     
34953     moveColumn : function(oldIndex, newIndex){
34954         var c = this.config[oldIndex];
34955         this.config.splice(oldIndex, 1);
34956         this.config.splice(newIndex, 0, c);
34957         this.dataMap = null;
34958         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34959     },
34960
34961     isLocked : function(colIndex){
34962         return this.config[colIndex].locked === true;
34963     },
34964
34965     setLocked : function(colIndex, value, suppressEvent){
34966         if(this.isLocked(colIndex) == value){
34967             return;
34968         }
34969         this.config[colIndex].locked = value;
34970         if(!suppressEvent){
34971             this.fireEvent("columnlockchange", this, colIndex, value);
34972         }
34973     },
34974
34975     getTotalLockedWidth : function(){
34976         var totalWidth = 0;
34977         for(var i = 0; i < this.config.length; i++){
34978             if(this.isLocked(i) && !this.isHidden(i)){
34979                 this.totalWidth += this.getColumnWidth(i);
34980             }
34981         }
34982         return totalWidth;
34983     },
34984
34985     getLockedCount : function(){
34986         for(var i = 0, len = this.config.length; i < len; i++){
34987             if(!this.isLocked(i)){
34988                 return i;
34989             }
34990         }
34991         
34992         return this.config.length;
34993     },
34994
34995     /**
34996      * Returns the number of columns.
34997      * @return {Number}
34998      */
34999     getColumnCount : function(visibleOnly){
35000         if(visibleOnly === true){
35001             var c = 0;
35002             for(var i = 0, len = this.config.length; i < len; i++){
35003                 if(!this.isHidden(i)){
35004                     c++;
35005                 }
35006             }
35007             return c;
35008         }
35009         return this.config.length;
35010     },
35011
35012     /**
35013      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35014      * @param {Function} fn
35015      * @param {Object} scope (optional)
35016      * @return {Array} result
35017      */
35018     getColumnsBy : function(fn, scope){
35019         var r = [];
35020         for(var i = 0, len = this.config.length; i < len; i++){
35021             var c = this.config[i];
35022             if(fn.call(scope||this, c, i) === true){
35023                 r[r.length] = c;
35024             }
35025         }
35026         return r;
35027     },
35028
35029     /**
35030      * Returns true if the specified column is sortable.
35031      * @param {Number} col The column index
35032      * @return {Boolean}
35033      */
35034     isSortable : function(col){
35035         if(typeof this.config[col].sortable == "undefined"){
35036             return this.defaultSortable;
35037         }
35038         return this.config[col].sortable;
35039     },
35040
35041     /**
35042      * Returns the rendering (formatting) function defined for the column.
35043      * @param {Number} col The column index.
35044      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35045      */
35046     getRenderer : function(col){
35047         if(!this.config[col].renderer){
35048             return Roo.grid.ColumnModel.defaultRenderer;
35049         }
35050         return this.config[col].renderer;
35051     },
35052
35053     /**
35054      * Sets the rendering (formatting) function for a column.
35055      * @param {Number} col The column index
35056      * @param {Function} fn The function to use to process the cell's raw data
35057      * to return HTML markup for the grid view. The render function is called with
35058      * the following parameters:<ul>
35059      * <li>Data value.</li>
35060      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35061      * <li>css A CSS style string to apply to the table cell.</li>
35062      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35063      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35064      * <li>Row index</li>
35065      * <li>Column index</li>
35066      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35067      */
35068     setRenderer : function(col, fn){
35069         this.config[col].renderer = fn;
35070     },
35071
35072     /**
35073      * Returns the width for the specified column.
35074      * @param {Number} col The column index
35075      * @return {Number}
35076      */
35077     getColumnWidth : function(col){
35078         return this.config[col].width * 1 || this.defaultWidth;
35079     },
35080
35081     /**
35082      * Sets the width for a column.
35083      * @param {Number} col The column index
35084      * @param {Number} width The new width
35085      */
35086     setColumnWidth : function(col, width, suppressEvent){
35087         this.config[col].width = width;
35088         this.totalWidth = null;
35089         if(!suppressEvent){
35090              this.fireEvent("widthchange", this, col, width);
35091         }
35092     },
35093
35094     /**
35095      * Returns the total width of all columns.
35096      * @param {Boolean} includeHidden True to include hidden column widths
35097      * @return {Number}
35098      */
35099     getTotalWidth : function(includeHidden){
35100         if(!this.totalWidth){
35101             this.totalWidth = 0;
35102             for(var i = 0, len = this.config.length; i < len; i++){
35103                 if(includeHidden || !this.isHidden(i)){
35104                     this.totalWidth += this.getColumnWidth(i);
35105                 }
35106             }
35107         }
35108         return this.totalWidth;
35109     },
35110
35111     /**
35112      * Returns the header for the specified column.
35113      * @param {Number} col The column index
35114      * @return {String}
35115      */
35116     getColumnHeader : function(col){
35117         return this.config[col].header;
35118     },
35119
35120     /**
35121      * Sets the header for a column.
35122      * @param {Number} col The column index
35123      * @param {String} header The new header
35124      */
35125     setColumnHeader : function(col, header){
35126         this.config[col].header = header;
35127         this.fireEvent("headerchange", this, col, header);
35128     },
35129
35130     /**
35131      * Returns the tooltip for the specified column.
35132      * @param {Number} col The column index
35133      * @return {String}
35134      */
35135     getColumnTooltip : function(col){
35136             return this.config[col].tooltip;
35137     },
35138     /**
35139      * Sets the tooltip for a column.
35140      * @param {Number} col The column index
35141      * @param {String} tooltip The new tooltip
35142      */
35143     setColumnTooltip : function(col, tooltip){
35144             this.config[col].tooltip = tooltip;
35145     },
35146
35147     /**
35148      * Returns the dataIndex for the specified column.
35149      * @param {Number} col The column index
35150      * @return {Number}
35151      */
35152     getDataIndex : function(col){
35153         return this.config[col].dataIndex;
35154     },
35155
35156     /**
35157      * Sets the dataIndex for a column.
35158      * @param {Number} col The column index
35159      * @param {Number} dataIndex The new dataIndex
35160      */
35161     setDataIndex : function(col, dataIndex){
35162         this.config[col].dataIndex = dataIndex;
35163     },
35164
35165     
35166     
35167     /**
35168      * Returns true if the cell is editable.
35169      * @param {Number} colIndex The column index
35170      * @param {Number} rowIndex The row index - this is nto actually used..?
35171      * @return {Boolean}
35172      */
35173     isCellEditable : function(colIndex, rowIndex){
35174         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35175     },
35176
35177     /**
35178      * Returns the editor defined for the cell/column.
35179      * return false or null to disable editing.
35180      * @param {Number} colIndex The column index
35181      * @param {Number} rowIndex The row index
35182      * @return {Object}
35183      */
35184     getCellEditor : function(colIndex, rowIndex){
35185         return this.config[colIndex].editor;
35186     },
35187
35188     /**
35189      * Sets if a column is editable.
35190      * @param {Number} col The column index
35191      * @param {Boolean} editable True if the column is editable
35192      */
35193     setEditable : function(col, editable){
35194         this.config[col].editable = editable;
35195     },
35196
35197
35198     /**
35199      * Returns true if the column is hidden.
35200      * @param {Number} colIndex The column index
35201      * @return {Boolean}
35202      */
35203     isHidden : function(colIndex){
35204         return this.config[colIndex].hidden;
35205     },
35206
35207
35208     /**
35209      * Returns true if the column width cannot be changed
35210      */
35211     isFixed : function(colIndex){
35212         return this.config[colIndex].fixed;
35213     },
35214
35215     /**
35216      * Returns true if the column can be resized
35217      * @return {Boolean}
35218      */
35219     isResizable : function(colIndex){
35220         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35221     },
35222     /**
35223      * Sets if a column is hidden.
35224      * @param {Number} colIndex The column index
35225      * @param {Boolean} hidden True if the column is hidden
35226      */
35227     setHidden : function(colIndex, hidden){
35228         this.config[colIndex].hidden = hidden;
35229         this.totalWidth = null;
35230         this.fireEvent("hiddenchange", this, colIndex, hidden);
35231     },
35232
35233     /**
35234      * Sets the editor for a column.
35235      * @param {Number} col The column index
35236      * @param {Object} editor The editor object
35237      */
35238     setEditor : function(col, editor){
35239         this.config[col].editor = editor;
35240     }
35241 });
35242
35243 Roo.grid.ColumnModel.defaultRenderer = function(value)
35244 {
35245     if(typeof value == "object") {
35246         return value;
35247     }
35248         if(typeof value == "string" && value.length < 1){
35249             return "&#160;";
35250         }
35251     
35252         return String.format("{0}", value);
35253 };
35254
35255 // Alias for backwards compatibility
35256 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35257 /*
35258  * Based on:
35259  * Ext JS Library 1.1.1
35260  * Copyright(c) 2006-2007, Ext JS, LLC.
35261  *
35262  * Originally Released Under LGPL - original licence link has changed is not relivant.
35263  *
35264  * Fork - LGPL
35265  * <script type="text/javascript">
35266  */
35267
35268 /**
35269  * @class Roo.grid.AbstractSelectionModel
35270  * @extends Roo.util.Observable
35271  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35272  * implemented by descendant classes.  This class should not be directly instantiated.
35273  * @constructor
35274  */
35275 Roo.grid.AbstractSelectionModel = function(){
35276     this.locked = false;
35277     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35278 };
35279
35280 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35281     /** @ignore Called by the grid automatically. Do not call directly. */
35282     init : function(grid){
35283         this.grid = grid;
35284         this.initEvents();
35285     },
35286
35287     /**
35288      * Locks the selections.
35289      */
35290     lock : function(){
35291         this.locked = true;
35292     },
35293
35294     /**
35295      * Unlocks the selections.
35296      */
35297     unlock : function(){
35298         this.locked = false;
35299     },
35300
35301     /**
35302      * Returns true if the selections are locked.
35303      * @return {Boolean}
35304      */
35305     isLocked : function(){
35306         return this.locked;
35307     }
35308 });/*
35309  * Based on:
35310  * Ext JS Library 1.1.1
35311  * Copyright(c) 2006-2007, Ext JS, LLC.
35312  *
35313  * Originally Released Under LGPL - original licence link has changed is not relivant.
35314  *
35315  * Fork - LGPL
35316  * <script type="text/javascript">
35317  */
35318 /**
35319  * @extends Roo.grid.AbstractSelectionModel
35320  * @class Roo.grid.RowSelectionModel
35321  * The default SelectionModel used by {@link Roo.grid.Grid}.
35322  * It supports multiple selections and keyboard selection/navigation. 
35323  * @constructor
35324  * @param {Object} config
35325  */
35326 Roo.grid.RowSelectionModel = function(config){
35327     Roo.apply(this, config);
35328     this.selections = new Roo.util.MixedCollection(false, function(o){
35329         return o.id;
35330     });
35331
35332     this.last = false;
35333     this.lastActive = false;
35334
35335     this.addEvents({
35336         /**
35337              * @event selectionchange
35338              * Fires when the selection changes
35339              * @param {SelectionModel} this
35340              */
35341             "selectionchange" : true,
35342         /**
35343              * @event afterselectionchange
35344              * Fires after the selection changes (eg. by key press or clicking)
35345              * @param {SelectionModel} this
35346              */
35347             "afterselectionchange" : true,
35348         /**
35349              * @event beforerowselect
35350              * Fires when a row is selected being selected, return false to cancel.
35351              * @param {SelectionModel} this
35352              * @param {Number} rowIndex The selected index
35353              * @param {Boolean} keepExisting False if other selections will be cleared
35354              */
35355             "beforerowselect" : true,
35356         /**
35357              * @event rowselect
35358              * Fires when a row is selected.
35359              * @param {SelectionModel} this
35360              * @param {Number} rowIndex The selected index
35361              * @param {Roo.data.Record} r The record
35362              */
35363             "rowselect" : true,
35364         /**
35365              * @event rowdeselect
35366              * Fires when a row is deselected.
35367              * @param {SelectionModel} this
35368              * @param {Number} rowIndex The selected index
35369              */
35370         "rowdeselect" : true
35371     });
35372     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35373     this.locked = false;
35374 };
35375
35376 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35377     /**
35378      * @cfg {Boolean} singleSelect
35379      * True to allow selection of only one row at a time (defaults to false)
35380      */
35381     singleSelect : false,
35382
35383     // private
35384     initEvents : function(){
35385
35386         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35387             this.grid.on("mousedown", this.handleMouseDown, this);
35388         }else{ // allow click to work like normal
35389             this.grid.on("rowclick", this.handleDragableRowClick, this);
35390         }
35391
35392         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35393             "up" : function(e){
35394                 if(!e.shiftKey){
35395                     this.selectPrevious(e.shiftKey);
35396                 }else if(this.last !== false && this.lastActive !== false){
35397                     var last = this.last;
35398                     this.selectRange(this.last,  this.lastActive-1);
35399                     this.grid.getView().focusRow(this.lastActive);
35400                     if(last !== false){
35401                         this.last = last;
35402                     }
35403                 }else{
35404                     this.selectFirstRow();
35405                 }
35406                 this.fireEvent("afterselectionchange", this);
35407             },
35408             "down" : function(e){
35409                 if(!e.shiftKey){
35410                     this.selectNext(e.shiftKey);
35411                 }else if(this.last !== false && this.lastActive !== false){
35412                     var last = this.last;
35413                     this.selectRange(this.last,  this.lastActive+1);
35414                     this.grid.getView().focusRow(this.lastActive);
35415                     if(last !== false){
35416                         this.last = last;
35417                     }
35418                 }else{
35419                     this.selectFirstRow();
35420                 }
35421                 this.fireEvent("afterselectionchange", this);
35422             },
35423             scope: this
35424         });
35425
35426         var view = this.grid.view;
35427         view.on("refresh", this.onRefresh, this);
35428         view.on("rowupdated", this.onRowUpdated, this);
35429         view.on("rowremoved", this.onRemove, this);
35430     },
35431
35432     // private
35433     onRefresh : function(){
35434         var ds = this.grid.dataSource, i, v = this.grid.view;
35435         var s = this.selections;
35436         s.each(function(r){
35437             if((i = ds.indexOfId(r.id)) != -1){
35438                 v.onRowSelect(i);
35439                 s.add(ds.getAt(i)); // updating the selection relate data
35440             }else{
35441                 s.remove(r);
35442             }
35443         });
35444     },
35445
35446     // private
35447     onRemove : function(v, index, r){
35448         this.selections.remove(r);
35449     },
35450
35451     // private
35452     onRowUpdated : function(v, index, r){
35453         if(this.isSelected(r)){
35454             v.onRowSelect(index);
35455         }
35456     },
35457
35458     /**
35459      * Select records.
35460      * @param {Array} records The records to select
35461      * @param {Boolean} keepExisting (optional) True to keep existing selections
35462      */
35463     selectRecords : function(records, keepExisting){
35464         if(!keepExisting){
35465             this.clearSelections();
35466         }
35467         var ds = this.grid.dataSource;
35468         for(var i = 0, len = records.length; i < len; i++){
35469             this.selectRow(ds.indexOf(records[i]), true);
35470         }
35471     },
35472
35473     /**
35474      * Gets the number of selected rows.
35475      * @return {Number}
35476      */
35477     getCount : function(){
35478         return this.selections.length;
35479     },
35480
35481     /**
35482      * Selects the first row in the grid.
35483      */
35484     selectFirstRow : function(){
35485         this.selectRow(0);
35486     },
35487
35488     /**
35489      * Select the last row.
35490      * @param {Boolean} keepExisting (optional) True to keep existing selections
35491      */
35492     selectLastRow : function(keepExisting){
35493         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35494     },
35495
35496     /**
35497      * Selects the row immediately following the last selected row.
35498      * @param {Boolean} keepExisting (optional) True to keep existing selections
35499      */
35500     selectNext : function(keepExisting){
35501         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35502             this.selectRow(this.last+1, keepExisting);
35503             this.grid.getView().focusRow(this.last);
35504         }
35505     },
35506
35507     /**
35508      * Selects the row that precedes the last selected row.
35509      * @param {Boolean} keepExisting (optional) True to keep existing selections
35510      */
35511     selectPrevious : function(keepExisting){
35512         if(this.last){
35513             this.selectRow(this.last-1, keepExisting);
35514             this.grid.getView().focusRow(this.last);
35515         }
35516     },
35517
35518     /**
35519      * Returns the selected records
35520      * @return {Array} Array of selected records
35521      */
35522     getSelections : function(){
35523         return [].concat(this.selections.items);
35524     },
35525
35526     /**
35527      * Returns the first selected record.
35528      * @return {Record}
35529      */
35530     getSelected : function(){
35531         return this.selections.itemAt(0);
35532     },
35533
35534
35535     /**
35536      * Clears all selections.
35537      */
35538     clearSelections : function(fast){
35539         if(this.locked) {
35540             return;
35541         }
35542         if(fast !== true){
35543             var ds = this.grid.dataSource;
35544             var s = this.selections;
35545             s.each(function(r){
35546                 this.deselectRow(ds.indexOfId(r.id));
35547             }, this);
35548             s.clear();
35549         }else{
35550             this.selections.clear();
35551         }
35552         this.last = false;
35553     },
35554
35555
35556     /**
35557      * Selects all rows.
35558      */
35559     selectAll : function(){
35560         if(this.locked) {
35561             return;
35562         }
35563         this.selections.clear();
35564         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35565             this.selectRow(i, true);
35566         }
35567     },
35568
35569     /**
35570      * Returns True if there is a selection.
35571      * @return {Boolean}
35572      */
35573     hasSelection : function(){
35574         return this.selections.length > 0;
35575     },
35576
35577     /**
35578      * Returns True if the specified row is selected.
35579      * @param {Number/Record} record The record or index of the record to check
35580      * @return {Boolean}
35581      */
35582     isSelected : function(index){
35583         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35584         return (r && this.selections.key(r.id) ? true : false);
35585     },
35586
35587     /**
35588      * Returns True if the specified record id is selected.
35589      * @param {String} id The id of record to check
35590      * @return {Boolean}
35591      */
35592     isIdSelected : function(id){
35593         return (this.selections.key(id) ? true : false);
35594     },
35595
35596     // private
35597     handleMouseDown : function(e, t){
35598         var view = this.grid.getView(), rowIndex;
35599         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35600             return;
35601         };
35602         if(e.shiftKey && this.last !== false){
35603             var last = this.last;
35604             this.selectRange(last, rowIndex, e.ctrlKey);
35605             this.last = last; // reset the last
35606             view.focusRow(rowIndex);
35607         }else{
35608             var isSelected = this.isSelected(rowIndex);
35609             if(e.button !== 0 && isSelected){
35610                 view.focusRow(rowIndex);
35611             }else if(e.ctrlKey && isSelected){
35612                 this.deselectRow(rowIndex);
35613             }else if(!isSelected){
35614                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35615                 view.focusRow(rowIndex);
35616             }
35617         }
35618         this.fireEvent("afterselectionchange", this);
35619     },
35620     // private
35621     handleDragableRowClick :  function(grid, rowIndex, e) 
35622     {
35623         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35624             this.selectRow(rowIndex, false);
35625             grid.view.focusRow(rowIndex);
35626              this.fireEvent("afterselectionchange", this);
35627         }
35628     },
35629     
35630     /**
35631      * Selects multiple rows.
35632      * @param {Array} rows Array of the indexes of the row to select
35633      * @param {Boolean} keepExisting (optional) True to keep existing selections
35634      */
35635     selectRows : function(rows, keepExisting){
35636         if(!keepExisting){
35637             this.clearSelections();
35638         }
35639         for(var i = 0, len = rows.length; i < len; i++){
35640             this.selectRow(rows[i], true);
35641         }
35642     },
35643
35644     /**
35645      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35646      * @param {Number} startRow The index of the first row in the range
35647      * @param {Number} endRow The index of the last row in the range
35648      * @param {Boolean} keepExisting (optional) True to retain existing selections
35649      */
35650     selectRange : function(startRow, endRow, keepExisting){
35651         if(this.locked) {
35652             return;
35653         }
35654         if(!keepExisting){
35655             this.clearSelections();
35656         }
35657         if(startRow <= endRow){
35658             for(var i = startRow; i <= endRow; i++){
35659                 this.selectRow(i, true);
35660             }
35661         }else{
35662             for(var i = startRow; i >= endRow; i--){
35663                 this.selectRow(i, true);
35664             }
35665         }
35666     },
35667
35668     /**
35669      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35670      * @param {Number} startRow The index of the first row in the range
35671      * @param {Number} endRow The index of the last row in the range
35672      */
35673     deselectRange : function(startRow, endRow, preventViewNotify){
35674         if(this.locked) {
35675             return;
35676         }
35677         for(var i = startRow; i <= endRow; i++){
35678             this.deselectRow(i, preventViewNotify);
35679         }
35680     },
35681
35682     /**
35683      * Selects a row.
35684      * @param {Number} row The index of the row to select
35685      * @param {Boolean} keepExisting (optional) True to keep existing selections
35686      */
35687     selectRow : function(index, keepExisting, preventViewNotify){
35688         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35689             return;
35690         }
35691         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35692             if(!keepExisting || this.singleSelect){
35693                 this.clearSelections();
35694             }
35695             var r = this.grid.dataSource.getAt(index);
35696             this.selections.add(r);
35697             this.last = this.lastActive = index;
35698             if(!preventViewNotify){
35699                 this.grid.getView().onRowSelect(index);
35700             }
35701             this.fireEvent("rowselect", this, index, r);
35702             this.fireEvent("selectionchange", this);
35703         }
35704     },
35705
35706     /**
35707      * Deselects a row.
35708      * @param {Number} row The index of the row to deselect
35709      */
35710     deselectRow : function(index, preventViewNotify){
35711         if(this.locked) {
35712             return;
35713         }
35714         if(this.last == index){
35715             this.last = false;
35716         }
35717         if(this.lastActive == index){
35718             this.lastActive = false;
35719         }
35720         var r = this.grid.dataSource.getAt(index);
35721         this.selections.remove(r);
35722         if(!preventViewNotify){
35723             this.grid.getView().onRowDeselect(index);
35724         }
35725         this.fireEvent("rowdeselect", this, index);
35726         this.fireEvent("selectionchange", this);
35727     },
35728
35729     // private
35730     restoreLast : function(){
35731         if(this._last){
35732             this.last = this._last;
35733         }
35734     },
35735
35736     // private
35737     acceptsNav : function(row, col, cm){
35738         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35739     },
35740
35741     // private
35742     onEditorKey : function(field, e){
35743         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35744         if(k == e.TAB){
35745             e.stopEvent();
35746             ed.completeEdit();
35747             if(e.shiftKey){
35748                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35749             }else{
35750                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35751             }
35752         }else if(k == e.ENTER && !e.ctrlKey){
35753             e.stopEvent();
35754             ed.completeEdit();
35755             if(e.shiftKey){
35756                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35757             }else{
35758                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35759             }
35760         }else if(k == e.ESC){
35761             ed.cancelEdit();
35762         }
35763         if(newCell){
35764             g.startEditing(newCell[0], newCell[1]);
35765         }
35766     }
35767 });/*
35768  * Based on:
35769  * Ext JS Library 1.1.1
35770  * Copyright(c) 2006-2007, Ext JS, LLC.
35771  *
35772  * Originally Released Under LGPL - original licence link has changed is not relivant.
35773  *
35774  * Fork - LGPL
35775  * <script type="text/javascript">
35776  */
35777 /**
35778  * @class Roo.grid.CellSelectionModel
35779  * @extends Roo.grid.AbstractSelectionModel
35780  * This class provides the basic implementation for cell selection in a grid.
35781  * @constructor
35782  * @param {Object} config The object containing the configuration of this model.
35783  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35784  */
35785 Roo.grid.CellSelectionModel = function(config){
35786     Roo.apply(this, config);
35787
35788     this.selection = null;
35789
35790     this.addEvents({
35791         /**
35792              * @event beforerowselect
35793              * Fires before a cell is selected.
35794              * @param {SelectionModel} this
35795              * @param {Number} rowIndex The selected row index
35796              * @param {Number} colIndex The selected cell index
35797              */
35798             "beforecellselect" : true,
35799         /**
35800              * @event cellselect
35801              * Fires when a cell is selected.
35802              * @param {SelectionModel} this
35803              * @param {Number} rowIndex The selected row index
35804              * @param {Number} colIndex The selected cell index
35805              */
35806             "cellselect" : true,
35807         /**
35808              * @event selectionchange
35809              * Fires when the active selection changes.
35810              * @param {SelectionModel} this
35811              * @param {Object} selection null for no selection or an object (o) with two properties
35812                 <ul>
35813                 <li>o.record: the record object for the row the selection is in</li>
35814                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35815                 </ul>
35816              */
35817             "selectionchange" : true,
35818         /**
35819              * @event tabend
35820              * Fires when the tab (or enter) was pressed on the last editable cell
35821              * You can use this to trigger add new row.
35822              * @param {SelectionModel} this
35823              */
35824             "tabend" : true,
35825          /**
35826              * @event beforeeditnext
35827              * Fires before the next editable sell is made active
35828              * You can use this to skip to another cell or fire the tabend
35829              *    if you set cell to false
35830              * @param {Object} eventdata object : { cell : [ row, col ] } 
35831              */
35832             "beforeeditnext" : true
35833     });
35834     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35835 };
35836
35837 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35838     
35839     enter_is_tab: false,
35840
35841     /** @ignore */
35842     initEvents : function(){
35843         this.grid.on("mousedown", this.handleMouseDown, this);
35844         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35845         var view = this.grid.view;
35846         view.on("refresh", this.onViewChange, this);
35847         view.on("rowupdated", this.onRowUpdated, this);
35848         view.on("beforerowremoved", this.clearSelections, this);
35849         view.on("beforerowsinserted", this.clearSelections, this);
35850         if(this.grid.isEditor){
35851             this.grid.on("beforeedit", this.beforeEdit,  this);
35852         }
35853     },
35854
35855         //private
35856     beforeEdit : function(e){
35857         this.select(e.row, e.column, false, true, e.record);
35858     },
35859
35860         //private
35861     onRowUpdated : function(v, index, r){
35862         if(this.selection && this.selection.record == r){
35863             v.onCellSelect(index, this.selection.cell[1]);
35864         }
35865     },
35866
35867         //private
35868     onViewChange : function(){
35869         this.clearSelections(true);
35870     },
35871
35872         /**
35873          * Returns the currently selected cell,.
35874          * @return {Array} The selected cell (row, column) or null if none selected.
35875          */
35876     getSelectedCell : function(){
35877         return this.selection ? this.selection.cell : null;
35878     },
35879
35880     /**
35881      * Clears all selections.
35882      * @param {Boolean} true to prevent the gridview from being notified about the change.
35883      */
35884     clearSelections : function(preventNotify){
35885         var s = this.selection;
35886         if(s){
35887             if(preventNotify !== true){
35888                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35889             }
35890             this.selection = null;
35891             this.fireEvent("selectionchange", this, null);
35892         }
35893     },
35894
35895     /**
35896      * Returns true if there is a selection.
35897      * @return {Boolean}
35898      */
35899     hasSelection : function(){
35900         return this.selection ? true : false;
35901     },
35902
35903     /** @ignore */
35904     handleMouseDown : function(e, t){
35905         var v = this.grid.getView();
35906         if(this.isLocked()){
35907             return;
35908         };
35909         var row = v.findRowIndex(t);
35910         var cell = v.findCellIndex(t);
35911         if(row !== false && cell !== false){
35912             this.select(row, cell);
35913         }
35914     },
35915
35916     /**
35917      * Selects a cell.
35918      * @param {Number} rowIndex
35919      * @param {Number} collIndex
35920      */
35921     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35922         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35923             this.clearSelections();
35924             r = r || this.grid.dataSource.getAt(rowIndex);
35925             this.selection = {
35926                 record : r,
35927                 cell : [rowIndex, colIndex]
35928             };
35929             if(!preventViewNotify){
35930                 var v = this.grid.getView();
35931                 v.onCellSelect(rowIndex, colIndex);
35932                 if(preventFocus !== true){
35933                     v.focusCell(rowIndex, colIndex);
35934                 }
35935             }
35936             this.fireEvent("cellselect", this, rowIndex, colIndex);
35937             this.fireEvent("selectionchange", this, this.selection);
35938         }
35939     },
35940
35941         //private
35942     isSelectable : function(rowIndex, colIndex, cm){
35943         return !cm.isHidden(colIndex);
35944     },
35945
35946     /** @ignore */
35947     handleKeyDown : function(e){
35948         //Roo.log('Cell Sel Model handleKeyDown');
35949         if(!e.isNavKeyPress()){
35950             return;
35951         }
35952         var g = this.grid, s = this.selection;
35953         if(!s){
35954             e.stopEvent();
35955             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35956             if(cell){
35957                 this.select(cell[0], cell[1]);
35958             }
35959             return;
35960         }
35961         var sm = this;
35962         var walk = function(row, col, step){
35963             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35964         };
35965         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35966         var newCell;
35967
35968       
35969
35970         switch(k){
35971             case e.TAB:
35972                 // handled by onEditorKey
35973                 if (g.isEditor && g.editing) {
35974                     return;
35975                 }
35976                 if(e.shiftKey) {
35977                     newCell = walk(r, c-1, -1);
35978                 } else {
35979                     newCell = walk(r, c+1, 1);
35980                 }
35981                 break;
35982             
35983             case e.DOWN:
35984                newCell = walk(r+1, c, 1);
35985                 break;
35986             
35987             case e.UP:
35988                 newCell = walk(r-1, c, -1);
35989                 break;
35990             
35991             case e.RIGHT:
35992                 newCell = walk(r, c+1, 1);
35993                 break;
35994             
35995             case e.LEFT:
35996                 newCell = walk(r, c-1, -1);
35997                 break;
35998             
35999             case e.ENTER:
36000                 
36001                 if(g.isEditor && !g.editing){
36002                    g.startEditing(r, c);
36003                    e.stopEvent();
36004                    return;
36005                 }
36006                 
36007                 
36008              break;
36009         };
36010         if(newCell){
36011             this.select(newCell[0], newCell[1]);
36012             e.stopEvent();
36013             
36014         }
36015     },
36016
36017     acceptsNav : function(row, col, cm){
36018         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36019     },
36020     /**
36021      * Selects a cell.
36022      * @param {Number} field (not used) - as it's normally used as a listener
36023      * @param {Number} e - event - fake it by using
36024      *
36025      * var e = Roo.EventObjectImpl.prototype;
36026      * e.keyCode = e.TAB
36027      *
36028      * 
36029      */
36030     onEditorKey : function(field, e){
36031         
36032         var k = e.getKey(),
36033             newCell,
36034             g = this.grid,
36035             ed = g.activeEditor,
36036             forward = false;
36037         ///Roo.log('onEditorKey' + k);
36038         
36039         
36040         if (this.enter_is_tab && k == e.ENTER) {
36041             k = e.TAB;
36042         }
36043         
36044         if(k == e.TAB){
36045             if(e.shiftKey){
36046                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36047             }else{
36048                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36049                 forward = true;
36050             }
36051             
36052             e.stopEvent();
36053             
36054         } else if(k == e.ENTER &&  !e.ctrlKey){
36055             ed.completeEdit();
36056             e.stopEvent();
36057             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36058         
36059                 } else if(k == e.ESC){
36060             ed.cancelEdit();
36061         }
36062                 
36063         if (newCell) {
36064             var ecall = { cell : newCell, forward : forward };
36065             this.fireEvent('beforeeditnext', ecall );
36066             newCell = ecall.cell;
36067                         forward = ecall.forward;
36068         }
36069                 
36070         if(newCell){
36071             //Roo.log('next cell after edit');
36072             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36073         } else if (forward) {
36074             // tabbed past last
36075             this.fireEvent.defer(100, this, ['tabend',this]);
36076         }
36077     }
36078 });/*
36079  * Based on:
36080  * Ext JS Library 1.1.1
36081  * Copyright(c) 2006-2007, Ext JS, LLC.
36082  *
36083  * Originally Released Under LGPL - original licence link has changed is not relivant.
36084  *
36085  * Fork - LGPL
36086  * <script type="text/javascript">
36087  */
36088  
36089 /**
36090  * @class Roo.grid.EditorGrid
36091  * @extends Roo.grid.Grid
36092  * Class for creating and editable grid.
36093  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36094  * The container MUST have some type of size defined for the grid to fill. The container will be 
36095  * automatically set to position relative if it isn't already.
36096  * @param {Object} dataSource The data model to bind to
36097  * @param {Object} colModel The column model with info about this grid's columns
36098  */
36099 Roo.grid.EditorGrid = function(container, config){
36100     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36101     this.getGridEl().addClass("xedit-grid");
36102
36103     if(!this.selModel){
36104         this.selModel = new Roo.grid.CellSelectionModel();
36105     }
36106
36107     this.activeEditor = null;
36108
36109         this.addEvents({
36110             /**
36111              * @event beforeedit
36112              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36113              * <ul style="padding:5px;padding-left:16px;">
36114              * <li>grid - This grid</li>
36115              * <li>record - The record being edited</li>
36116              * <li>field - The field name being edited</li>
36117              * <li>value - The value for the field being edited.</li>
36118              * <li>row - The grid row index</li>
36119              * <li>column - The grid column index</li>
36120              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36121              * </ul>
36122              * @param {Object} e An edit event (see above for description)
36123              */
36124             "beforeedit" : true,
36125             /**
36126              * @event afteredit
36127              * Fires after a cell is edited. <br />
36128              * <ul style="padding:5px;padding-left:16px;">
36129              * <li>grid - This grid</li>
36130              * <li>record - The record being edited</li>
36131              * <li>field - The field name being edited</li>
36132              * <li>value - The value being set</li>
36133              * <li>originalValue - The original value for the field, before the edit.</li>
36134              * <li>row - The grid row index</li>
36135              * <li>column - The grid column index</li>
36136              * </ul>
36137              * @param {Object} e An edit event (see above for description)
36138              */
36139             "afteredit" : true,
36140             /**
36141              * @event validateedit
36142              * Fires after a cell is edited, but before the value is set in the record. 
36143          * You can use this to modify the value being set in the field, Return false
36144              * to cancel the change. The edit event object has the following properties <br />
36145              * <ul style="padding:5px;padding-left:16px;">
36146          * <li>editor - This editor</li>
36147              * <li>grid - This grid</li>
36148              * <li>record - The record being edited</li>
36149              * <li>field - The field name being edited</li>
36150              * <li>value - The value being set</li>
36151              * <li>originalValue - The original value for the field, before the edit.</li>
36152              * <li>row - The grid row index</li>
36153              * <li>column - The grid column index</li>
36154              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36155              * </ul>
36156              * @param {Object} e An edit event (see above for description)
36157              */
36158             "validateedit" : true
36159         });
36160     this.on("bodyscroll", this.stopEditing,  this);
36161     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36162 };
36163
36164 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36165     /**
36166      * @cfg {Number} clicksToEdit
36167      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36168      */
36169     clicksToEdit: 2,
36170
36171     // private
36172     isEditor : true,
36173     // private
36174     trackMouseOver: false, // causes very odd FF errors
36175
36176     onCellDblClick : function(g, row, col){
36177         this.startEditing(row, col);
36178     },
36179
36180     onEditComplete : function(ed, value, startValue){
36181         this.editing = false;
36182         this.activeEditor = null;
36183         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36184         var r = ed.record;
36185         var field = this.colModel.getDataIndex(ed.col);
36186         var e = {
36187             grid: this,
36188             record: r,
36189             field: field,
36190             originalValue: startValue,
36191             value: value,
36192             row: ed.row,
36193             column: ed.col,
36194             cancel:false,
36195             editor: ed
36196         };
36197         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36198         cell.show();
36199           
36200         if(String(value) !== String(startValue)){
36201             
36202             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36203                 r.set(field, e.value);
36204                 // if we are dealing with a combo box..
36205                 // then we also set the 'name' colum to be the displayField
36206                 if (ed.field.displayField && ed.field.name) {
36207                     r.set(ed.field.name, ed.field.el.dom.value);
36208                 }
36209                 
36210                 delete e.cancel; //?? why!!!
36211                 this.fireEvent("afteredit", e);
36212             }
36213         } else {
36214             this.fireEvent("afteredit", e); // always fire it!
36215         }
36216         this.view.focusCell(ed.row, ed.col);
36217     },
36218
36219     /**
36220      * Starts editing the specified for the specified row/column
36221      * @param {Number} rowIndex
36222      * @param {Number} colIndex
36223      */
36224     startEditing : function(row, col){
36225         this.stopEditing();
36226         if(this.colModel.isCellEditable(col, row)){
36227             this.view.ensureVisible(row, col, true);
36228           
36229             var r = this.dataSource.getAt(row);
36230             var field = this.colModel.getDataIndex(col);
36231             var cell = Roo.get(this.view.getCell(row,col));
36232             var e = {
36233                 grid: this,
36234                 record: r,
36235                 field: field,
36236                 value: r.data[field],
36237                 row: row,
36238                 column: col,
36239                 cancel:false 
36240             };
36241             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36242                 this.editing = true;
36243                 var ed = this.colModel.getCellEditor(col, row);
36244                 
36245                 if (!ed) {
36246                     return;
36247                 }
36248                 if(!ed.rendered){
36249                     ed.render(ed.parentEl || document.body);
36250                 }
36251                 ed.field.reset();
36252                
36253                 cell.hide();
36254                 
36255                 (function(){ // complex but required for focus issues in safari, ie and opera
36256                     ed.row = row;
36257                     ed.col = col;
36258                     ed.record = r;
36259                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36260                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36261                     this.activeEditor = ed;
36262                     var v = r.data[field];
36263                     ed.startEdit(this.view.getCell(row, col), v);
36264                     // combo's with 'displayField and name set
36265                     if (ed.field.displayField && ed.field.name) {
36266                         ed.field.el.dom.value = r.data[ed.field.name];
36267                     }
36268                     
36269                     
36270                 }).defer(50, this);
36271             }
36272         }
36273     },
36274         
36275     /**
36276      * Stops any active editing
36277      */
36278     stopEditing : function(){
36279         if(this.activeEditor){
36280             this.activeEditor.completeEdit();
36281         }
36282         this.activeEditor = null;
36283     },
36284         
36285          /**
36286      * Called to get grid's drag proxy text, by default returns this.ddText.
36287      * @return {String}
36288      */
36289     getDragDropText : function(){
36290         var count = this.selModel.getSelectedCell() ? 1 : 0;
36291         return String.format(this.ddText, count, count == 1 ? '' : 's');
36292     }
36293         
36294 });/*
36295  * Based on:
36296  * Ext JS Library 1.1.1
36297  * Copyright(c) 2006-2007, Ext JS, LLC.
36298  *
36299  * Originally Released Under LGPL - original licence link has changed is not relivant.
36300  *
36301  * Fork - LGPL
36302  * <script type="text/javascript">
36303  */
36304
36305 // private - not really -- you end up using it !
36306 // This is a support class used internally by the Grid components
36307
36308 /**
36309  * @class Roo.grid.GridEditor
36310  * @extends Roo.Editor
36311  * Class for creating and editable grid elements.
36312  * @param {Object} config any settings (must include field)
36313  */
36314 Roo.grid.GridEditor = function(field, config){
36315     if (!config && field.field) {
36316         config = field;
36317         field = Roo.factory(config.field, Roo.form);
36318     }
36319     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36320     field.monitorTab = false;
36321 };
36322
36323 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36324     
36325     /**
36326      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36327      */
36328     
36329     alignment: "tl-tl",
36330     autoSize: "width",
36331     hideEl : false,
36332     cls: "x-small-editor x-grid-editor",
36333     shim:false,
36334     shadow:"frame"
36335 });/*
36336  * Based on:
36337  * Ext JS Library 1.1.1
36338  * Copyright(c) 2006-2007, Ext JS, LLC.
36339  *
36340  * Originally Released Under LGPL - original licence link has changed is not relivant.
36341  *
36342  * Fork - LGPL
36343  * <script type="text/javascript">
36344  */
36345   
36346
36347   
36348 Roo.grid.PropertyRecord = Roo.data.Record.create([
36349     {name:'name',type:'string'},  'value'
36350 ]);
36351
36352
36353 Roo.grid.PropertyStore = function(grid, source){
36354     this.grid = grid;
36355     this.store = new Roo.data.Store({
36356         recordType : Roo.grid.PropertyRecord
36357     });
36358     this.store.on('update', this.onUpdate,  this);
36359     if(source){
36360         this.setSource(source);
36361     }
36362     Roo.grid.PropertyStore.superclass.constructor.call(this);
36363 };
36364
36365
36366
36367 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36368     setSource : function(o){
36369         this.source = o;
36370         this.store.removeAll();
36371         var data = [];
36372         for(var k in o){
36373             if(this.isEditableValue(o[k])){
36374                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36375             }
36376         }
36377         this.store.loadRecords({records: data}, {}, true);
36378     },
36379
36380     onUpdate : function(ds, record, type){
36381         if(type == Roo.data.Record.EDIT){
36382             var v = record.data['value'];
36383             var oldValue = record.modified['value'];
36384             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36385                 this.source[record.id] = v;
36386                 record.commit();
36387                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36388             }else{
36389                 record.reject();
36390             }
36391         }
36392     },
36393
36394     getProperty : function(row){
36395        return this.store.getAt(row);
36396     },
36397
36398     isEditableValue: function(val){
36399         if(val && val instanceof Date){
36400             return true;
36401         }else if(typeof val == 'object' || typeof val == 'function'){
36402             return false;
36403         }
36404         return true;
36405     },
36406
36407     setValue : function(prop, value){
36408         this.source[prop] = value;
36409         this.store.getById(prop).set('value', value);
36410     },
36411
36412     getSource : function(){
36413         return this.source;
36414     }
36415 });
36416
36417 Roo.grid.PropertyColumnModel = function(grid, store){
36418     this.grid = grid;
36419     var g = Roo.grid;
36420     g.PropertyColumnModel.superclass.constructor.call(this, [
36421         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36422         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36423     ]);
36424     this.store = store;
36425     this.bselect = Roo.DomHelper.append(document.body, {
36426         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36427             {tag: 'option', value: 'true', html: 'true'},
36428             {tag: 'option', value: 'false', html: 'false'}
36429         ]
36430     });
36431     Roo.id(this.bselect);
36432     var f = Roo.form;
36433     this.editors = {
36434         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36435         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36436         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36437         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36438         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36439     };
36440     this.renderCellDelegate = this.renderCell.createDelegate(this);
36441     this.renderPropDelegate = this.renderProp.createDelegate(this);
36442 };
36443
36444 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36445     
36446     
36447     nameText : 'Name',
36448     valueText : 'Value',
36449     
36450     dateFormat : 'm/j/Y',
36451     
36452     
36453     renderDate : function(dateVal){
36454         return dateVal.dateFormat(this.dateFormat);
36455     },
36456
36457     renderBool : function(bVal){
36458         return bVal ? 'true' : 'false';
36459     },
36460
36461     isCellEditable : function(colIndex, rowIndex){
36462         return colIndex == 1;
36463     },
36464
36465     getRenderer : function(col){
36466         return col == 1 ?
36467             this.renderCellDelegate : this.renderPropDelegate;
36468     },
36469
36470     renderProp : function(v){
36471         return this.getPropertyName(v);
36472     },
36473
36474     renderCell : function(val){
36475         var rv = val;
36476         if(val instanceof Date){
36477             rv = this.renderDate(val);
36478         }else if(typeof val == 'boolean'){
36479             rv = this.renderBool(val);
36480         }
36481         return Roo.util.Format.htmlEncode(rv);
36482     },
36483
36484     getPropertyName : function(name){
36485         var pn = this.grid.propertyNames;
36486         return pn && pn[name] ? pn[name] : name;
36487     },
36488
36489     getCellEditor : function(colIndex, rowIndex){
36490         var p = this.store.getProperty(rowIndex);
36491         var n = p.data['name'], val = p.data['value'];
36492         
36493         if(typeof(this.grid.customEditors[n]) == 'string'){
36494             return this.editors[this.grid.customEditors[n]];
36495         }
36496         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36497             return this.grid.customEditors[n];
36498         }
36499         if(val instanceof Date){
36500             return this.editors['date'];
36501         }else if(typeof val == 'number'){
36502             return this.editors['number'];
36503         }else if(typeof val == 'boolean'){
36504             return this.editors['boolean'];
36505         }else{
36506             return this.editors['string'];
36507         }
36508     }
36509 });
36510
36511 /**
36512  * @class Roo.grid.PropertyGrid
36513  * @extends Roo.grid.EditorGrid
36514  * This class represents the  interface of a component based property grid control.
36515  * <br><br>Usage:<pre><code>
36516  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36517       
36518  });
36519  // set any options
36520  grid.render();
36521  * </code></pre>
36522   
36523  * @constructor
36524  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36525  * The container MUST have some type of size defined for the grid to fill. The container will be
36526  * automatically set to position relative if it isn't already.
36527  * @param {Object} config A config object that sets properties on this grid.
36528  */
36529 Roo.grid.PropertyGrid = function(container, config){
36530     config = config || {};
36531     var store = new Roo.grid.PropertyStore(this);
36532     this.store = store;
36533     var cm = new Roo.grid.PropertyColumnModel(this, store);
36534     store.store.sort('name', 'ASC');
36535     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36536         ds: store.store,
36537         cm: cm,
36538         enableColLock:false,
36539         enableColumnMove:false,
36540         stripeRows:false,
36541         trackMouseOver: false,
36542         clicksToEdit:1
36543     }, config));
36544     this.getGridEl().addClass('x-props-grid');
36545     this.lastEditRow = null;
36546     this.on('columnresize', this.onColumnResize, this);
36547     this.addEvents({
36548          /**
36549              * @event beforepropertychange
36550              * Fires before a property changes (return false to stop?)
36551              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36552              * @param {String} id Record Id
36553              * @param {String} newval New Value
36554          * @param {String} oldval Old Value
36555              */
36556         "beforepropertychange": true,
36557         /**
36558              * @event propertychange
36559              * Fires after a property changes
36560              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36561              * @param {String} id Record Id
36562              * @param {String} newval New Value
36563          * @param {String} oldval Old Value
36564              */
36565         "propertychange": true
36566     });
36567     this.customEditors = this.customEditors || {};
36568 };
36569 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36570     
36571      /**
36572      * @cfg {Object} customEditors map of colnames=> custom editors.
36573      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36574      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36575      * false disables editing of the field.
36576          */
36577     
36578       /**
36579      * @cfg {Object} propertyNames map of property Names to their displayed value
36580          */
36581     
36582     render : function(){
36583         Roo.grid.PropertyGrid.superclass.render.call(this);
36584         this.autoSize.defer(100, this);
36585     },
36586
36587     autoSize : function(){
36588         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36589         if(this.view){
36590             this.view.fitColumns();
36591         }
36592     },
36593
36594     onColumnResize : function(){
36595         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36596         this.autoSize();
36597     },
36598     /**
36599      * Sets the data for the Grid
36600      * accepts a Key => Value object of all the elements avaiable.
36601      * @param {Object} data  to appear in grid.
36602      */
36603     setSource : function(source){
36604         this.store.setSource(source);
36605         //this.autoSize();
36606     },
36607     /**
36608      * Gets all the data from the grid.
36609      * @return {Object} data  data stored in grid
36610      */
36611     getSource : function(){
36612         return this.store.getSource();
36613     }
36614 });/*
36615   
36616  * Licence LGPL
36617  
36618  */
36619  
36620 /**
36621  * @class Roo.grid.Calendar
36622  * @extends Roo.util.Grid
36623  * This class extends the Grid to provide a calendar widget
36624  * <br><br>Usage:<pre><code>
36625  var grid = new Roo.grid.Calendar("my-container-id", {
36626      ds: myDataStore,
36627      cm: myColModel,
36628      selModel: mySelectionModel,
36629      autoSizeColumns: true,
36630      monitorWindowResize: false,
36631      trackMouseOver: true
36632      eventstore : real data store..
36633  });
36634  // set any options
36635  grid.render();
36636   
36637   * @constructor
36638  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36639  * The container MUST have some type of size defined for the grid to fill. The container will be
36640  * automatically set to position relative if it isn't already.
36641  * @param {Object} config A config object that sets properties on this grid.
36642  */
36643 Roo.grid.Calendar = function(container, config){
36644         // initialize the container
36645         this.container = Roo.get(container);
36646         this.container.update("");
36647         this.container.setStyle("overflow", "hidden");
36648     this.container.addClass('x-grid-container');
36649
36650     this.id = this.container.id;
36651
36652     Roo.apply(this, config);
36653     // check and correct shorthanded configs
36654     
36655     var rows = [];
36656     var d =1;
36657     for (var r = 0;r < 6;r++) {
36658         
36659         rows[r]=[];
36660         for (var c =0;c < 7;c++) {
36661             rows[r][c]= '';
36662         }
36663     }
36664     if (this.eventStore) {
36665         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36666         this.eventStore.on('load',this.onLoad, this);
36667         this.eventStore.on('beforeload',this.clearEvents, this);
36668          
36669     }
36670     
36671     this.dataSource = new Roo.data.Store({
36672             proxy: new Roo.data.MemoryProxy(rows),
36673             reader: new Roo.data.ArrayReader({}, [
36674                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36675     });
36676
36677     this.dataSource.load();
36678     this.ds = this.dataSource;
36679     this.ds.xmodule = this.xmodule || false;
36680     
36681     
36682     var cellRender = function(v,x,r)
36683     {
36684         return String.format(
36685             '<div class="fc-day  fc-widget-content"><div>' +
36686                 '<div class="fc-event-container"></div>' +
36687                 '<div class="fc-day-number">{0}</div>'+
36688                 
36689                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36690             '</div></div>', v);
36691     
36692     }
36693     
36694     
36695     this.colModel = new Roo.grid.ColumnModel( [
36696         {
36697             xtype: 'ColumnModel',
36698             xns: Roo.grid,
36699             dataIndex : 'weekday0',
36700             header : 'Sunday',
36701             renderer : cellRender
36702         },
36703         {
36704             xtype: 'ColumnModel',
36705             xns: Roo.grid,
36706             dataIndex : 'weekday1',
36707             header : 'Monday',
36708             renderer : cellRender
36709         },
36710         {
36711             xtype: 'ColumnModel',
36712             xns: Roo.grid,
36713             dataIndex : 'weekday2',
36714             header : 'Tuesday',
36715             renderer : cellRender
36716         },
36717         {
36718             xtype: 'ColumnModel',
36719             xns: Roo.grid,
36720             dataIndex : 'weekday3',
36721             header : 'Wednesday',
36722             renderer : cellRender
36723         },
36724         {
36725             xtype: 'ColumnModel',
36726             xns: Roo.grid,
36727             dataIndex : 'weekday4',
36728             header : 'Thursday',
36729             renderer : cellRender
36730         },
36731         {
36732             xtype: 'ColumnModel',
36733             xns: Roo.grid,
36734             dataIndex : 'weekday5',
36735             header : 'Friday',
36736             renderer : cellRender
36737         },
36738         {
36739             xtype: 'ColumnModel',
36740             xns: Roo.grid,
36741             dataIndex : 'weekday6',
36742             header : 'Saturday',
36743             renderer : cellRender
36744         }
36745     ]);
36746     this.cm = this.colModel;
36747     this.cm.xmodule = this.xmodule || false;
36748  
36749         
36750           
36751     //this.selModel = new Roo.grid.CellSelectionModel();
36752     //this.sm = this.selModel;
36753     //this.selModel.init(this);
36754     
36755     
36756     if(this.width){
36757         this.container.setWidth(this.width);
36758     }
36759
36760     if(this.height){
36761         this.container.setHeight(this.height);
36762     }
36763     /** @private */
36764         this.addEvents({
36765         // raw events
36766         /**
36767          * @event click
36768          * The raw click event for the entire grid.
36769          * @param {Roo.EventObject} e
36770          */
36771         "click" : true,
36772         /**
36773          * @event dblclick
36774          * The raw dblclick event for the entire grid.
36775          * @param {Roo.EventObject} e
36776          */
36777         "dblclick" : true,
36778         /**
36779          * @event contextmenu
36780          * The raw contextmenu event for the entire grid.
36781          * @param {Roo.EventObject} e
36782          */
36783         "contextmenu" : true,
36784         /**
36785          * @event mousedown
36786          * The raw mousedown event for the entire grid.
36787          * @param {Roo.EventObject} e
36788          */
36789         "mousedown" : true,
36790         /**
36791          * @event mouseup
36792          * The raw mouseup event for the entire grid.
36793          * @param {Roo.EventObject} e
36794          */
36795         "mouseup" : true,
36796         /**
36797          * @event mouseover
36798          * The raw mouseover event for the entire grid.
36799          * @param {Roo.EventObject} e
36800          */
36801         "mouseover" : true,
36802         /**
36803          * @event mouseout
36804          * The raw mouseout event for the entire grid.
36805          * @param {Roo.EventObject} e
36806          */
36807         "mouseout" : true,
36808         /**
36809          * @event keypress
36810          * The raw keypress event for the entire grid.
36811          * @param {Roo.EventObject} e
36812          */
36813         "keypress" : true,
36814         /**
36815          * @event keydown
36816          * The raw keydown event for the entire grid.
36817          * @param {Roo.EventObject} e
36818          */
36819         "keydown" : true,
36820
36821         // custom events
36822
36823         /**
36824          * @event cellclick
36825          * Fires when a cell is clicked
36826          * @param {Grid} this
36827          * @param {Number} rowIndex
36828          * @param {Number} columnIndex
36829          * @param {Roo.EventObject} e
36830          */
36831         "cellclick" : true,
36832         /**
36833          * @event celldblclick
36834          * Fires when a cell is double clicked
36835          * @param {Grid} this
36836          * @param {Number} rowIndex
36837          * @param {Number} columnIndex
36838          * @param {Roo.EventObject} e
36839          */
36840         "celldblclick" : true,
36841         /**
36842          * @event rowclick
36843          * Fires when a row is clicked
36844          * @param {Grid} this
36845          * @param {Number} rowIndex
36846          * @param {Roo.EventObject} e
36847          */
36848         "rowclick" : true,
36849         /**
36850          * @event rowdblclick
36851          * Fires when a row is double clicked
36852          * @param {Grid} this
36853          * @param {Number} rowIndex
36854          * @param {Roo.EventObject} e
36855          */
36856         "rowdblclick" : true,
36857         /**
36858          * @event headerclick
36859          * Fires when a header is clicked
36860          * @param {Grid} this
36861          * @param {Number} columnIndex
36862          * @param {Roo.EventObject} e
36863          */
36864         "headerclick" : true,
36865         /**
36866          * @event headerdblclick
36867          * Fires when a header cell is double clicked
36868          * @param {Grid} this
36869          * @param {Number} columnIndex
36870          * @param {Roo.EventObject} e
36871          */
36872         "headerdblclick" : true,
36873         /**
36874          * @event rowcontextmenu
36875          * Fires when a row is right clicked
36876          * @param {Grid} this
36877          * @param {Number} rowIndex
36878          * @param {Roo.EventObject} e
36879          */
36880         "rowcontextmenu" : true,
36881         /**
36882          * @event cellcontextmenu
36883          * Fires when a cell is right clicked
36884          * @param {Grid} this
36885          * @param {Number} rowIndex
36886          * @param {Number} cellIndex
36887          * @param {Roo.EventObject} e
36888          */
36889          "cellcontextmenu" : true,
36890         /**
36891          * @event headercontextmenu
36892          * Fires when a header is right clicked
36893          * @param {Grid} this
36894          * @param {Number} columnIndex
36895          * @param {Roo.EventObject} e
36896          */
36897         "headercontextmenu" : true,
36898         /**
36899          * @event bodyscroll
36900          * Fires when the body element is scrolled
36901          * @param {Number} scrollLeft
36902          * @param {Number} scrollTop
36903          */
36904         "bodyscroll" : true,
36905         /**
36906          * @event columnresize
36907          * Fires when the user resizes a column
36908          * @param {Number} columnIndex
36909          * @param {Number} newSize
36910          */
36911         "columnresize" : true,
36912         /**
36913          * @event columnmove
36914          * Fires when the user moves a column
36915          * @param {Number} oldIndex
36916          * @param {Number} newIndex
36917          */
36918         "columnmove" : true,
36919         /**
36920          * @event startdrag
36921          * Fires when row(s) start being dragged
36922          * @param {Grid} this
36923          * @param {Roo.GridDD} dd The drag drop object
36924          * @param {event} e The raw browser event
36925          */
36926         "startdrag" : true,
36927         /**
36928          * @event enddrag
36929          * Fires when a drag operation is complete
36930          * @param {Grid} this
36931          * @param {Roo.GridDD} dd The drag drop object
36932          * @param {event} e The raw browser event
36933          */
36934         "enddrag" : true,
36935         /**
36936          * @event dragdrop
36937          * Fires when dragged row(s) are dropped on a valid DD target
36938          * @param {Grid} this
36939          * @param {Roo.GridDD} dd The drag drop object
36940          * @param {String} targetId The target drag drop object
36941          * @param {event} e The raw browser event
36942          */
36943         "dragdrop" : true,
36944         /**
36945          * @event dragover
36946          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36947          * @param {Grid} this
36948          * @param {Roo.GridDD} dd The drag drop object
36949          * @param {String} targetId The target drag drop object
36950          * @param {event} e The raw browser event
36951          */
36952         "dragover" : true,
36953         /**
36954          * @event dragenter
36955          *  Fires when the dragged row(s) first cross another DD target while being dragged
36956          * @param {Grid} this
36957          * @param {Roo.GridDD} dd The drag drop object
36958          * @param {String} targetId The target drag drop object
36959          * @param {event} e The raw browser event
36960          */
36961         "dragenter" : true,
36962         /**
36963          * @event dragout
36964          * Fires when the dragged row(s) leave another DD target while being dragged
36965          * @param {Grid} this
36966          * @param {Roo.GridDD} dd The drag drop object
36967          * @param {String} targetId The target drag drop object
36968          * @param {event} e The raw browser event
36969          */
36970         "dragout" : true,
36971         /**
36972          * @event rowclass
36973          * Fires when a row is rendered, so you can change add a style to it.
36974          * @param {GridView} gridview   The grid view
36975          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36976          */
36977         'rowclass' : true,
36978
36979         /**
36980          * @event render
36981          * Fires when the grid is rendered
36982          * @param {Grid} grid
36983          */
36984         'render' : true,
36985             /**
36986              * @event select
36987              * Fires when a date is selected
36988              * @param {DatePicker} this
36989              * @param {Date} date The selected date
36990              */
36991         'select': true,
36992         /**
36993              * @event monthchange
36994              * Fires when the displayed month changes 
36995              * @param {DatePicker} this
36996              * @param {Date} date The selected month
36997              */
36998         'monthchange': true,
36999         /**
37000              * @event evententer
37001              * Fires when mouse over an event
37002              * @param {Calendar} this
37003              * @param {event} Event
37004              */
37005         'evententer': true,
37006         /**
37007              * @event eventleave
37008              * Fires when the mouse leaves an
37009              * @param {Calendar} this
37010              * @param {event}
37011              */
37012         'eventleave': true,
37013         /**
37014              * @event eventclick
37015              * Fires when the mouse click an
37016              * @param {Calendar} this
37017              * @param {event}
37018              */
37019         'eventclick': true,
37020         /**
37021              * @event eventrender
37022              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37023              * @param {Calendar} this
37024              * @param {data} data to be modified
37025              */
37026         'eventrender': true
37027         
37028     });
37029
37030     Roo.grid.Grid.superclass.constructor.call(this);
37031     this.on('render', function() {
37032         this.view.el.addClass('x-grid-cal'); 
37033         
37034         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37035
37036     },this);
37037     
37038     if (!Roo.grid.Calendar.style) {
37039         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37040             
37041             
37042             '.x-grid-cal .x-grid-col' :  {
37043                 height: 'auto !important',
37044                 'vertical-align': 'top'
37045             },
37046             '.x-grid-cal  .fc-event-hori' : {
37047                 height: '14px'
37048             }
37049              
37050             
37051         }, Roo.id());
37052     }
37053
37054     
37055     
37056 };
37057 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37058     /**
37059      * @cfg {Store} eventStore The store that loads events.
37060      */
37061     eventStore : 25,
37062
37063      
37064     activeDate : false,
37065     startDay : 0,
37066     autoWidth : true,
37067     monitorWindowResize : false,
37068
37069     
37070     resizeColumns : function() {
37071         var col = (this.view.el.getWidth() / 7) - 3;
37072         // loop through cols, and setWidth
37073         for(var i =0 ; i < 7 ; i++){
37074             this.cm.setColumnWidth(i, col);
37075         }
37076     },
37077      setDate :function(date) {
37078         
37079         Roo.log('setDate?');
37080         
37081         this.resizeColumns();
37082         var vd = this.activeDate;
37083         this.activeDate = date;
37084 //        if(vd && this.el){
37085 //            var t = date.getTime();
37086 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37087 //                Roo.log('using add remove');
37088 //                
37089 //                this.fireEvent('monthchange', this, date);
37090 //                
37091 //                this.cells.removeClass("fc-state-highlight");
37092 //                this.cells.each(function(c){
37093 //                   if(c.dateValue == t){
37094 //                       c.addClass("fc-state-highlight");
37095 //                       setTimeout(function(){
37096 //                            try{c.dom.firstChild.focus();}catch(e){}
37097 //                       }, 50);
37098 //                       return false;
37099 //                   }
37100 //                   return true;
37101 //                });
37102 //                return;
37103 //            }
37104 //        }
37105         
37106         var days = date.getDaysInMonth();
37107         
37108         var firstOfMonth = date.getFirstDateOfMonth();
37109         var startingPos = firstOfMonth.getDay()-this.startDay;
37110         
37111         if(startingPos < this.startDay){
37112             startingPos += 7;
37113         }
37114         
37115         var pm = date.add(Date.MONTH, -1);
37116         var prevStart = pm.getDaysInMonth()-startingPos;
37117 //        
37118         
37119         
37120         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37121         
37122         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37123         //this.cells.addClassOnOver('fc-state-hover');
37124         
37125         var cells = this.cells.elements;
37126         var textEls = this.textNodes;
37127         
37128         //Roo.each(cells, function(cell){
37129         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37130         //});
37131         
37132         days += startingPos;
37133
37134         // convert everything to numbers so it's fast
37135         var day = 86400000;
37136         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37137         //Roo.log(d);
37138         //Roo.log(pm);
37139         //Roo.log(prevStart);
37140         
37141         var today = new Date().clearTime().getTime();
37142         var sel = date.clearTime().getTime();
37143         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37144         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37145         var ddMatch = this.disabledDatesRE;
37146         var ddText = this.disabledDatesText;
37147         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37148         var ddaysText = this.disabledDaysText;
37149         var format = this.format;
37150         
37151         var setCellClass = function(cal, cell){
37152             
37153             //Roo.log('set Cell Class');
37154             cell.title = "";
37155             var t = d.getTime();
37156             
37157             //Roo.log(d);
37158             
37159             
37160             cell.dateValue = t;
37161             if(t == today){
37162                 cell.className += " fc-today";
37163                 cell.className += " fc-state-highlight";
37164                 cell.title = cal.todayText;
37165             }
37166             if(t == sel){
37167                 // disable highlight in other month..
37168                 cell.className += " fc-state-highlight";
37169                 
37170             }
37171             // disabling
37172             if(t < min) {
37173                 //cell.className = " fc-state-disabled";
37174                 cell.title = cal.minText;
37175                 return;
37176             }
37177             if(t > max) {
37178                 //cell.className = " fc-state-disabled";
37179                 cell.title = cal.maxText;
37180                 return;
37181             }
37182             if(ddays){
37183                 if(ddays.indexOf(d.getDay()) != -1){
37184                     // cell.title = ddaysText;
37185                    // cell.className = " fc-state-disabled";
37186                 }
37187             }
37188             if(ddMatch && format){
37189                 var fvalue = d.dateFormat(format);
37190                 if(ddMatch.test(fvalue)){
37191                     cell.title = ddText.replace("%0", fvalue);
37192                    cell.className = " fc-state-disabled";
37193                 }
37194             }
37195             
37196             if (!cell.initialClassName) {
37197                 cell.initialClassName = cell.dom.className;
37198             }
37199             
37200             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37201         };
37202
37203         var i = 0;
37204         
37205         for(; i < startingPos; i++) {
37206             cells[i].dayName =  (++prevStart);
37207             Roo.log(textEls[i]);
37208             d.setDate(d.getDate()+1);
37209             
37210             //cells[i].className = "fc-past fc-other-month";
37211             setCellClass(this, cells[i]);
37212         }
37213         
37214         var intDay = 0;
37215         
37216         for(; i < days; i++){
37217             intDay = i - startingPos + 1;
37218             cells[i].dayName =  (intDay);
37219             d.setDate(d.getDate()+1);
37220             
37221             cells[i].className = ''; // "x-date-active";
37222             setCellClass(this, cells[i]);
37223         }
37224         var extraDays = 0;
37225         
37226         for(; i < 42; i++) {
37227             //textEls[i].innerHTML = (++extraDays);
37228             
37229             d.setDate(d.getDate()+1);
37230             cells[i].dayName = (++extraDays);
37231             cells[i].className = "fc-future fc-other-month";
37232             setCellClass(this, cells[i]);
37233         }
37234         
37235         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37236         
37237         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37238         
37239         // this will cause all the cells to mis
37240         var rows= [];
37241         var i =0;
37242         for (var r = 0;r < 6;r++) {
37243             for (var c =0;c < 7;c++) {
37244                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37245             }    
37246         }
37247         
37248         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37249         for(i=0;i<cells.length;i++) {
37250             
37251             this.cells.elements[i].dayName = cells[i].dayName ;
37252             this.cells.elements[i].className = cells[i].className;
37253             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37254             this.cells.elements[i].title = cells[i].title ;
37255             this.cells.elements[i].dateValue = cells[i].dateValue ;
37256         }
37257         
37258         
37259         
37260         
37261         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37262         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37263         
37264         ////if(totalRows != 6){
37265             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37266            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37267        // }
37268         
37269         this.fireEvent('monthchange', this, date);
37270         
37271         
37272     },
37273  /**
37274      * Returns the grid's SelectionModel.
37275      * @return {SelectionModel}
37276      */
37277     getSelectionModel : function(){
37278         if(!this.selModel){
37279             this.selModel = new Roo.grid.CellSelectionModel();
37280         }
37281         return this.selModel;
37282     },
37283
37284     load: function() {
37285         this.eventStore.load()
37286         
37287         
37288         
37289     },
37290     
37291     findCell : function(dt) {
37292         dt = dt.clearTime().getTime();
37293         var ret = false;
37294         this.cells.each(function(c){
37295             //Roo.log("check " +c.dateValue + '?=' + dt);
37296             if(c.dateValue == dt){
37297                 ret = c;
37298                 return false;
37299             }
37300             return true;
37301         });
37302         
37303         return ret;
37304     },
37305     
37306     findCells : function(rec) {
37307         var s = rec.data.start_dt.clone().clearTime().getTime();
37308        // Roo.log(s);
37309         var e= rec.data.end_dt.clone().clearTime().getTime();
37310        // Roo.log(e);
37311         var ret = [];
37312         this.cells.each(function(c){
37313              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37314             
37315             if(c.dateValue > e){
37316                 return ;
37317             }
37318             if(c.dateValue < s){
37319                 return ;
37320             }
37321             ret.push(c);
37322         });
37323         
37324         return ret;    
37325     },
37326     
37327     findBestRow: function(cells)
37328     {
37329         var ret = 0;
37330         
37331         for (var i =0 ; i < cells.length;i++) {
37332             ret  = Math.max(cells[i].rows || 0,ret);
37333         }
37334         return ret;
37335         
37336     },
37337     
37338     
37339     addItem : function(rec)
37340     {
37341         // look for vertical location slot in
37342         var cells = this.findCells(rec);
37343         
37344         rec.row = this.findBestRow(cells);
37345         
37346         // work out the location.
37347         
37348         var crow = false;
37349         var rows = [];
37350         for(var i =0; i < cells.length; i++) {
37351             if (!crow) {
37352                 crow = {
37353                     start : cells[i],
37354                     end :  cells[i]
37355                 };
37356                 continue;
37357             }
37358             if (crow.start.getY() == cells[i].getY()) {
37359                 // on same row.
37360                 crow.end = cells[i];
37361                 continue;
37362             }
37363             // different row.
37364             rows.push(crow);
37365             crow = {
37366                 start: cells[i],
37367                 end : cells[i]
37368             };
37369             
37370         }
37371         
37372         rows.push(crow);
37373         rec.els = [];
37374         rec.rows = rows;
37375         rec.cells = cells;
37376         for (var i = 0; i < cells.length;i++) {
37377             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37378             
37379         }
37380         
37381         
37382     },
37383     
37384     clearEvents: function() {
37385         
37386         if (!this.eventStore.getCount()) {
37387             return;
37388         }
37389         // reset number of rows in cells.
37390         Roo.each(this.cells.elements, function(c){
37391             c.rows = 0;
37392         });
37393         
37394         this.eventStore.each(function(e) {
37395             this.clearEvent(e);
37396         },this);
37397         
37398     },
37399     
37400     clearEvent : function(ev)
37401     {
37402         if (ev.els) {
37403             Roo.each(ev.els, function(el) {
37404                 el.un('mouseenter' ,this.onEventEnter, this);
37405                 el.un('mouseleave' ,this.onEventLeave, this);
37406                 el.remove();
37407             },this);
37408             ev.els = [];
37409         }
37410     },
37411     
37412     
37413     renderEvent : function(ev,ctr) {
37414         if (!ctr) {
37415              ctr = this.view.el.select('.fc-event-container',true).first();
37416         }
37417         
37418          
37419         this.clearEvent(ev);
37420             //code
37421        
37422         
37423         
37424         ev.els = [];
37425         var cells = ev.cells;
37426         var rows = ev.rows;
37427         this.fireEvent('eventrender', this, ev);
37428         
37429         for(var i =0; i < rows.length; i++) {
37430             
37431             cls = '';
37432             if (i == 0) {
37433                 cls += ' fc-event-start';
37434             }
37435             if ((i+1) == rows.length) {
37436                 cls += ' fc-event-end';
37437             }
37438             
37439             //Roo.log(ev.data);
37440             // how many rows should it span..
37441             var cg = this.eventTmpl.append(ctr,Roo.apply({
37442                 fccls : cls
37443                 
37444             }, ev.data) , true);
37445             
37446             
37447             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37448             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37449             cg.on('click', this.onEventClick, this, ev);
37450             
37451             ev.els.push(cg);
37452             
37453             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37454             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37455             //Roo.log(cg);
37456              
37457             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37458             cg.setWidth(ebox.right - sbox.x -2);
37459         }
37460     },
37461     
37462     renderEvents: function()
37463     {   
37464         // first make sure there is enough space..
37465         
37466         if (!this.eventTmpl) {
37467             this.eventTmpl = new Roo.Template(
37468                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37469                     '<div class="fc-event-inner">' +
37470                         '<span class="fc-event-time">{time}</span>' +
37471                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37472                     '</div>' +
37473                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37474                 '</div>'
37475             );
37476                 
37477         }
37478                
37479         
37480         
37481         this.cells.each(function(c) {
37482             //Roo.log(c.select('.fc-day-content div',true).first());
37483             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37484         });
37485         
37486         var ctr = this.view.el.select('.fc-event-container',true).first();
37487         
37488         var cls;
37489         this.eventStore.each(function(ev){
37490             
37491             this.renderEvent(ev);
37492              
37493              
37494         }, this);
37495         this.view.layout();
37496         
37497     },
37498     
37499     onEventEnter: function (e, el,event,d) {
37500         this.fireEvent('evententer', this, el, event);
37501     },
37502     
37503     onEventLeave: function (e, el,event,d) {
37504         this.fireEvent('eventleave', this, el, event);
37505     },
37506     
37507     onEventClick: function (e, el,event,d) {
37508         this.fireEvent('eventclick', this, el, event);
37509     },
37510     
37511     onMonthChange: function () {
37512         this.store.load();
37513     },
37514     
37515     onLoad: function () {
37516         
37517         //Roo.log('calendar onload');
37518 //         
37519         if(this.eventStore.getCount() > 0){
37520             
37521            
37522             
37523             this.eventStore.each(function(d){
37524                 
37525                 
37526                 // FIXME..
37527                 var add =   d.data;
37528                 if (typeof(add.end_dt) == 'undefined')  {
37529                     Roo.log("Missing End time in calendar data: ");
37530                     Roo.log(d);
37531                     return;
37532                 }
37533                 if (typeof(add.start_dt) == 'undefined')  {
37534                     Roo.log("Missing Start time in calendar data: ");
37535                     Roo.log(d);
37536                     return;
37537                 }
37538                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37539                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37540                 add.id = add.id || d.id;
37541                 add.title = add.title || '??';
37542                 
37543                 this.addItem(d);
37544                 
37545              
37546             },this);
37547         }
37548         
37549         this.renderEvents();
37550     }
37551     
37552
37553 });
37554 /*
37555  grid : {
37556                 xtype: 'Grid',
37557                 xns: Roo.grid,
37558                 listeners : {
37559                     render : function ()
37560                     {
37561                         _this.grid = this;
37562                         
37563                         if (!this.view.el.hasClass('course-timesheet')) {
37564                             this.view.el.addClass('course-timesheet');
37565                         }
37566                         if (this.tsStyle) {
37567                             this.ds.load({});
37568                             return; 
37569                         }
37570                         Roo.log('width');
37571                         Roo.log(_this.grid.view.el.getWidth());
37572                         
37573                         
37574                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37575                             '.course-timesheet .x-grid-row' : {
37576                                 height: '80px'
37577                             },
37578                             '.x-grid-row td' : {
37579                                 'vertical-align' : 0
37580                             },
37581                             '.course-edit-link' : {
37582                                 'color' : 'blue',
37583                                 'text-overflow' : 'ellipsis',
37584                                 'overflow' : 'hidden',
37585                                 'white-space' : 'nowrap',
37586                                 'cursor' : 'pointer'
37587                             },
37588                             '.sub-link' : {
37589                                 'color' : 'green'
37590                             },
37591                             '.de-act-sup-link' : {
37592                                 'color' : 'purple',
37593                                 'text-decoration' : 'line-through'
37594                             },
37595                             '.de-act-link' : {
37596                                 'color' : 'red',
37597                                 'text-decoration' : 'line-through'
37598                             },
37599                             '.course-timesheet .course-highlight' : {
37600                                 'border-top-style': 'dashed !important',
37601                                 'border-bottom-bottom': 'dashed !important'
37602                             },
37603                             '.course-timesheet .course-item' : {
37604                                 'font-family'   : 'tahoma, arial, helvetica',
37605                                 'font-size'     : '11px',
37606                                 'overflow'      : 'hidden',
37607                                 'padding-left'  : '10px',
37608                                 'padding-right' : '10px',
37609                                 'padding-top' : '10px' 
37610                             }
37611                             
37612                         }, Roo.id());
37613                                 this.ds.load({});
37614                     }
37615                 },
37616                 autoWidth : true,
37617                 monitorWindowResize : false,
37618                 cellrenderer : function(v,x,r)
37619                 {
37620                     return v;
37621                 },
37622                 sm : {
37623                     xtype: 'CellSelectionModel',
37624                     xns: Roo.grid
37625                 },
37626                 dataSource : {
37627                     xtype: 'Store',
37628                     xns: Roo.data,
37629                     listeners : {
37630                         beforeload : function (_self, options)
37631                         {
37632                             options.params = options.params || {};
37633                             options.params._month = _this.monthField.getValue();
37634                             options.params.limit = 9999;
37635                             options.params['sort'] = 'when_dt';    
37636                             options.params['dir'] = 'ASC';    
37637                             this.proxy.loadResponse = this.loadResponse;
37638                             Roo.log("load?");
37639                             //this.addColumns();
37640                         },
37641                         load : function (_self, records, options)
37642                         {
37643                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37644                                 // if you click on the translation.. you can edit it...
37645                                 var el = Roo.get(this);
37646                                 var id = el.dom.getAttribute('data-id');
37647                                 var d = el.dom.getAttribute('data-date');
37648                                 var t = el.dom.getAttribute('data-time');
37649                                 //var id = this.child('span').dom.textContent;
37650                                 
37651                                 //Roo.log(this);
37652                                 Pman.Dialog.CourseCalendar.show({
37653                                     id : id,
37654                                     when_d : d,
37655                                     when_t : t,
37656                                     productitem_active : id ? 1 : 0
37657                                 }, function() {
37658                                     _this.grid.ds.load({});
37659                                 });
37660                            
37661                            });
37662                            
37663                            _this.panel.fireEvent('resize', [ '', '' ]);
37664                         }
37665                     },
37666                     loadResponse : function(o, success, response){
37667                             // this is overridden on before load..
37668                             
37669                             Roo.log("our code?");       
37670                             //Roo.log(success);
37671                             //Roo.log(response)
37672                             delete this.activeRequest;
37673                             if(!success){
37674                                 this.fireEvent("loadexception", this, o, response);
37675                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37676                                 return;
37677                             }
37678                             var result;
37679                             try {
37680                                 result = o.reader.read(response);
37681                             }catch(e){
37682                                 Roo.log("load exception?");
37683                                 this.fireEvent("loadexception", this, o, response, e);
37684                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37685                                 return;
37686                             }
37687                             Roo.log("ready...");        
37688                             // loop through result.records;
37689                             // and set this.tdate[date] = [] << array of records..
37690                             _this.tdata  = {};
37691                             Roo.each(result.records, function(r){
37692                                 //Roo.log(r.data);
37693                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37694                                     _this.tdata[r.data.when_dt.format('j')] = [];
37695                                 }
37696                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37697                             });
37698                             
37699                             //Roo.log(_this.tdata);
37700                             
37701                             result.records = [];
37702                             result.totalRecords = 6;
37703                     
37704                             // let's generate some duumy records for the rows.
37705                             //var st = _this.dateField.getValue();
37706                             
37707                             // work out monday..
37708                             //st = st.add(Date.DAY, -1 * st.format('w'));
37709                             
37710                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37711                             
37712                             var firstOfMonth = date.getFirstDayOfMonth();
37713                             var days = date.getDaysInMonth();
37714                             var d = 1;
37715                             var firstAdded = false;
37716                             for (var i = 0; i < result.totalRecords ; i++) {
37717                                 //var d= st.add(Date.DAY, i);
37718                                 var row = {};
37719                                 var added = 0;
37720                                 for(var w = 0 ; w < 7 ; w++){
37721                                     if(!firstAdded && firstOfMonth != w){
37722                                         continue;
37723                                     }
37724                                     if(d > days){
37725                                         continue;
37726                                     }
37727                                     firstAdded = true;
37728                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37729                                     row['weekday'+w] = String.format(
37730                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37731                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37732                                                     d,
37733                                                     date.format('Y-m-')+dd
37734                                                 );
37735                                     added++;
37736                                     if(typeof(_this.tdata[d]) != 'undefined'){
37737                                         Roo.each(_this.tdata[d], function(r){
37738                                             var is_sub = '';
37739                                             var deactive = '';
37740                                             var id = r.id;
37741                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37742                                             if(r.parent_id*1>0){
37743                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37744                                                 id = r.parent_id;
37745                                             }
37746                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37747                                                 deactive = 'de-act-link';
37748                                             }
37749                                             
37750                                             row['weekday'+w] += String.format(
37751                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37752                                                     id, //0
37753                                                     r.product_id_name, //1
37754                                                     r.when_dt.format('h:ia'), //2
37755                                                     is_sub, //3
37756                                                     deactive, //4
37757                                                     desc // 5
37758                                             );
37759                                         });
37760                                     }
37761                                     d++;
37762                                 }
37763                                 
37764                                 // only do this if something added..
37765                                 if(added > 0){ 
37766                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37767                                 }
37768                                 
37769                                 
37770                                 // push it twice. (second one with an hour..
37771                                 
37772                             }
37773                             //Roo.log(result);
37774                             this.fireEvent("load", this, o, o.request.arg);
37775                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37776                         },
37777                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37778                     proxy : {
37779                         xtype: 'HttpProxy',
37780                         xns: Roo.data,
37781                         method : 'GET',
37782                         url : baseURL + '/Roo/Shop_course.php'
37783                     },
37784                     reader : {
37785                         xtype: 'JsonReader',
37786                         xns: Roo.data,
37787                         id : 'id',
37788                         fields : [
37789                             {
37790                                 'name': 'id',
37791                                 'type': 'int'
37792                             },
37793                             {
37794                                 'name': 'when_dt',
37795                                 'type': 'string'
37796                             },
37797                             {
37798                                 'name': 'end_dt',
37799                                 'type': 'string'
37800                             },
37801                             {
37802                                 'name': 'parent_id',
37803                                 'type': 'int'
37804                             },
37805                             {
37806                                 'name': 'product_id',
37807                                 'type': 'int'
37808                             },
37809                             {
37810                                 'name': 'productitem_id',
37811                                 'type': 'int'
37812                             },
37813                             {
37814                                 'name': 'guid',
37815                                 'type': 'int'
37816                             }
37817                         ]
37818                     }
37819                 },
37820                 toolbar : {
37821                     xtype: 'Toolbar',
37822                     xns: Roo,
37823                     items : [
37824                         {
37825                             xtype: 'Button',
37826                             xns: Roo.Toolbar,
37827                             listeners : {
37828                                 click : function (_self, e)
37829                                 {
37830                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37831                                     sd.setMonth(sd.getMonth()-1);
37832                                     _this.monthField.setValue(sd.format('Y-m-d'));
37833                                     _this.grid.ds.load({});
37834                                 }
37835                             },
37836                             text : "Back"
37837                         },
37838                         {
37839                             xtype: 'Separator',
37840                             xns: Roo.Toolbar
37841                         },
37842                         {
37843                             xtype: 'MonthField',
37844                             xns: Roo.form,
37845                             listeners : {
37846                                 render : function (_self)
37847                                 {
37848                                     _this.monthField = _self;
37849                                    // _this.monthField.set  today
37850                                 },
37851                                 select : function (combo, date)
37852                                 {
37853                                     _this.grid.ds.load({});
37854                                 }
37855                             },
37856                             value : (function() { return new Date(); })()
37857                         },
37858                         {
37859                             xtype: 'Separator',
37860                             xns: Roo.Toolbar
37861                         },
37862                         {
37863                             xtype: 'TextItem',
37864                             xns: Roo.Toolbar,
37865                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37866                         },
37867                         {
37868                             xtype: 'Fill',
37869                             xns: Roo.Toolbar
37870                         },
37871                         {
37872                             xtype: 'Button',
37873                             xns: Roo.Toolbar,
37874                             listeners : {
37875                                 click : function (_self, e)
37876                                 {
37877                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37878                                     sd.setMonth(sd.getMonth()+1);
37879                                     _this.monthField.setValue(sd.format('Y-m-d'));
37880                                     _this.grid.ds.load({});
37881                                 }
37882                             },
37883                             text : "Next"
37884                         }
37885                     ]
37886                 },
37887                  
37888             }
37889         };
37890         
37891         *//*
37892  * Based on:
37893  * Ext JS Library 1.1.1
37894  * Copyright(c) 2006-2007, Ext JS, LLC.
37895  *
37896  * Originally Released Under LGPL - original licence link has changed is not relivant.
37897  *
37898  * Fork - LGPL
37899  * <script type="text/javascript">
37900  */
37901  
37902 /**
37903  * @class Roo.LoadMask
37904  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37905  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37906  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37907  * element's UpdateManager load indicator and will be destroyed after the initial load.
37908  * @constructor
37909  * Create a new LoadMask
37910  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37911  * @param {Object} config The config object
37912  */
37913 Roo.LoadMask = function(el, config){
37914     this.el = Roo.get(el);
37915     Roo.apply(this, config);
37916     if(this.store){
37917         this.store.on('beforeload', this.onBeforeLoad, this);
37918         this.store.on('load', this.onLoad, this);
37919         this.store.on('loadexception', this.onLoadException, this);
37920         this.removeMask = false;
37921     }else{
37922         var um = this.el.getUpdateManager();
37923         um.showLoadIndicator = false; // disable the default indicator
37924         um.on('beforeupdate', this.onBeforeLoad, this);
37925         um.on('update', this.onLoad, this);
37926         um.on('failure', this.onLoad, this);
37927         this.removeMask = true;
37928     }
37929 };
37930
37931 Roo.LoadMask.prototype = {
37932     /**
37933      * @cfg {Boolean} removeMask
37934      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37935      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37936      */
37937     /**
37938      * @cfg {String} msg
37939      * The text to display in a centered loading message box (defaults to 'Loading...')
37940      */
37941     msg : 'Loading...',
37942     /**
37943      * @cfg {String} msgCls
37944      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37945      */
37946     msgCls : 'x-mask-loading',
37947
37948     /**
37949      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37950      * @type Boolean
37951      */
37952     disabled: false,
37953
37954     /**
37955      * Disables the mask to prevent it from being displayed
37956      */
37957     disable : function(){
37958        this.disabled = true;
37959     },
37960
37961     /**
37962      * Enables the mask so that it can be displayed
37963      */
37964     enable : function(){
37965         this.disabled = false;
37966     },
37967     
37968     onLoadException : function()
37969     {
37970         Roo.log(arguments);
37971         
37972         if (typeof(arguments[3]) != 'undefined') {
37973             Roo.MessageBox.alert("Error loading",arguments[3]);
37974         } 
37975         /*
37976         try {
37977             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37978                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37979             }   
37980         } catch(e) {
37981             
37982         }
37983         */
37984     
37985         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37986     },
37987     // private
37988     onLoad : function()
37989     {
37990         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37991     },
37992
37993     // private
37994     onBeforeLoad : function(){
37995         if(!this.disabled){
37996             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37997         }
37998     },
37999
38000     // private
38001     destroy : function(){
38002         if(this.store){
38003             this.store.un('beforeload', this.onBeforeLoad, this);
38004             this.store.un('load', this.onLoad, this);
38005             this.store.un('loadexception', this.onLoadException, this);
38006         }else{
38007             var um = this.el.getUpdateManager();
38008             um.un('beforeupdate', this.onBeforeLoad, this);
38009             um.un('update', this.onLoad, this);
38010             um.un('failure', this.onLoad, this);
38011         }
38012     }
38013 };/*
38014  * Based on:
38015  * Ext JS Library 1.1.1
38016  * Copyright(c) 2006-2007, Ext JS, LLC.
38017  *
38018  * Originally Released Under LGPL - original licence link has changed is not relivant.
38019  *
38020  * Fork - LGPL
38021  * <script type="text/javascript">
38022  */
38023
38024
38025 /**
38026  * @class Roo.XTemplate
38027  * @extends Roo.Template
38028  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38029 <pre><code>
38030 var t = new Roo.XTemplate(
38031         '&lt;select name="{name}"&gt;',
38032                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38033         '&lt;/select&gt;'
38034 );
38035  
38036 // then append, applying the master template values
38037  </code></pre>
38038  *
38039  * Supported features:
38040  *
38041  *  Tags:
38042
38043 <pre><code>
38044       {a_variable} - output encoded.
38045       {a_variable.format:("Y-m-d")} - call a method on the variable
38046       {a_variable:raw} - unencoded output
38047       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38048       {a_variable:this.method_on_template(...)} - call a method on the template object.
38049  
38050 </code></pre>
38051  *  The tpl tag:
38052 <pre><code>
38053         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38054         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38055         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38056         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38057   
38058         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38059         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38060 </code></pre>
38061  *      
38062  */
38063 Roo.XTemplate = function()
38064 {
38065     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38066     if (this.html) {
38067         this.compile();
38068     }
38069 };
38070
38071
38072 Roo.extend(Roo.XTemplate, Roo.Template, {
38073
38074     /**
38075      * The various sub templates
38076      */
38077     tpls : false,
38078     /**
38079      *
38080      * basic tag replacing syntax
38081      * WORD:WORD()
38082      *
38083      * // you can fake an object call by doing this
38084      *  x.t:(test,tesT) 
38085      * 
38086      */
38087     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38088
38089     /**
38090      * compile the template
38091      *
38092      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38093      *
38094      */
38095     compile: function()
38096     {
38097         var s = this.html;
38098      
38099         s = ['<tpl>', s, '</tpl>'].join('');
38100     
38101         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38102             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38103             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38104             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38105             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38106             m,
38107             id     = 0,
38108             tpls   = [];
38109     
38110         while(true == !!(m = s.match(re))){
38111             var forMatch   = m[0].match(nameRe),
38112                 ifMatch   = m[0].match(ifRe),
38113                 execMatch   = m[0].match(execRe),
38114                 namedMatch   = m[0].match(namedRe),
38115                 
38116                 exp  = null, 
38117                 fn   = null,
38118                 exec = null,
38119                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38120                 
38121             if (ifMatch) {
38122                 // if - puts fn into test..
38123                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38124                 if(exp){
38125                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38126                 }
38127             }
38128             
38129             if (execMatch) {
38130                 // exec - calls a function... returns empty if true is  returned.
38131                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38132                 if(exp){
38133                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38134                 }
38135             }
38136             
38137             
38138             if (name) {
38139                 // for = 
38140                 switch(name){
38141                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38142                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38143                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38144                 }
38145             }
38146             var uid = namedMatch ? namedMatch[1] : id;
38147             
38148             
38149             tpls.push({
38150                 id:     namedMatch ? namedMatch[1] : id,
38151                 target: name,
38152                 exec:   exec,
38153                 test:   fn,
38154                 body:   m[1] || ''
38155             });
38156             if (namedMatch) {
38157                 s = s.replace(m[0], '');
38158             } else { 
38159                 s = s.replace(m[0], '{xtpl'+ id + '}');
38160             }
38161             ++id;
38162         }
38163         this.tpls = [];
38164         for(var i = tpls.length-1; i >= 0; --i){
38165             this.compileTpl(tpls[i]);
38166             this.tpls[tpls[i].id] = tpls[i];
38167         }
38168         this.master = tpls[tpls.length-1];
38169         return this;
38170     },
38171     /**
38172      * same as applyTemplate, except it's done to one of the subTemplates
38173      * when using named templates, you can do:
38174      *
38175      * var str = pl.applySubTemplate('your-name', values);
38176      *
38177      * 
38178      * @param {Number} id of the template
38179      * @param {Object} values to apply to template
38180      * @param {Object} parent (normaly the instance of this object)
38181      */
38182     applySubTemplate : function(id, values, parent)
38183     {
38184         
38185         
38186         var t = this.tpls[id];
38187         
38188         
38189         try { 
38190             if(t.test && !t.test.call(this, values, parent)){
38191                 return '';
38192             }
38193         } catch(e) {
38194             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38195             Roo.log(e.toString());
38196             Roo.log(t.test);
38197             return ''
38198         }
38199         try { 
38200             
38201             if(t.exec && t.exec.call(this, values, parent)){
38202                 return '';
38203             }
38204         } catch(e) {
38205             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38206             Roo.log(e.toString());
38207             Roo.log(t.exec);
38208             return ''
38209         }
38210         try {
38211             var vs = t.target ? t.target.call(this, values, parent) : values;
38212             parent = t.target ? values : parent;
38213             if(t.target && vs instanceof Array){
38214                 var buf = [];
38215                 for(var i = 0, len = vs.length; i < len; i++){
38216                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38217                 }
38218                 return buf.join('');
38219             }
38220             return t.compiled.call(this, vs, parent);
38221         } catch (e) {
38222             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38223             Roo.log(e.toString());
38224             Roo.log(t.compiled);
38225             return '';
38226         }
38227     },
38228
38229     compileTpl : function(tpl)
38230     {
38231         var fm = Roo.util.Format;
38232         var useF = this.disableFormats !== true;
38233         var sep = Roo.isGecko ? "+" : ",";
38234         var undef = function(str) {
38235             Roo.log("Property not found :"  + str);
38236             return '';
38237         };
38238         
38239         var fn = function(m, name, format, args)
38240         {
38241             //Roo.log(arguments);
38242             args = args ? args.replace(/\\'/g,"'") : args;
38243             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38244             if (typeof(format) == 'undefined') {
38245                 format= 'htmlEncode';
38246             }
38247             if (format == 'raw' ) {
38248                 format = false;
38249             }
38250             
38251             if(name.substr(0, 4) == 'xtpl'){
38252                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38253             }
38254             
38255             // build an array of options to determine if value is undefined..
38256             
38257             // basically get 'xxxx.yyyy' then do
38258             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38259             //    (function () { Roo.log("Property not found"); return ''; })() :
38260             //    ......
38261             
38262             var udef_ar = [];
38263             var lookfor = '';
38264             Roo.each(name.split('.'), function(st) {
38265                 lookfor += (lookfor.length ? '.': '') + st;
38266                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38267             });
38268             
38269             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38270             
38271             
38272             if(format && useF){
38273                 
38274                 args = args ? ',' + args : "";
38275                  
38276                 if(format.substr(0, 5) != "this."){
38277                     format = "fm." + format + '(';
38278                 }else{
38279                     format = 'this.call("'+ format.substr(5) + '", ';
38280                     args = ", values";
38281                 }
38282                 
38283                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38284             }
38285              
38286             if (args.length) {
38287                 // called with xxyx.yuu:(test,test)
38288                 // change to ()
38289                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38290             }
38291             // raw.. - :raw modifier..
38292             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38293             
38294         };
38295         var body;
38296         // branched to use + in gecko and [].join() in others
38297         if(Roo.isGecko){
38298             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38299                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38300                     "';};};";
38301         }else{
38302             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38303             body.push(tpl.body.replace(/(\r\n|\n)/g,
38304                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38305             body.push("'].join('');};};");
38306             body = body.join('');
38307         }
38308         
38309         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38310        
38311         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38312         eval(body);
38313         
38314         return this;
38315     },
38316
38317     applyTemplate : function(values){
38318         return this.master.compiled.call(this, values, {});
38319         //var s = this.subs;
38320     },
38321
38322     apply : function(){
38323         return this.applyTemplate.apply(this, arguments);
38324     }
38325
38326  });
38327
38328 Roo.XTemplate.from = function(el){
38329     el = Roo.getDom(el);
38330     return new Roo.XTemplate(el.value || el.innerHTML);
38331 };