roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @static
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 [required] 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.DataReader} reader [required]  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>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
670      * <pre>
671                 {
672                     data : data,  // array of key=>value data like JsonReader
673                     total : data.length,
674                     success : true
675                     
676                 }
677         </pre>
678             }.</li>
679      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
680      * passed the following arguments:<ul>
681      * <li>r : Roo.data.Record[]</li>
682      * <li>options: Options object from the load call</li>
683      * <li>success: Boolean success indicator</li></ul></li>
684      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
685      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
686      * </ul>
687      */
688     load : function(options){
689         options = options || {};
690         if(this.fireEvent("beforeload", this, options) !== false){
691             this.storeOptions(options);
692             var p = Roo.apply(options.params || {}, this.baseParams);
693             // if meta was not loaded from remote source.. try requesting it.
694             if (!this.reader.metaFromRemote) {
695                 p._requestMeta = 1;
696             }
697             if(this.sortInfo && this.remoteSort){
698                 var pn = this.paramNames;
699                 p[pn["sort"]] = this.sortInfo.field;
700                 p[pn["dir"]] = this.sortInfo.direction;
701             }
702             if (this.multiSort) {
703                 var pn = this.paramNames;
704                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
705             }
706             
707             this.proxy.load(p, this.reader, this.loadRecords, this, options);
708         }
709     },
710
711     /**
712      * Reloads the Record cache from the configured Proxy using the configured Reader and
713      * the options from the last load operation performed.
714      * @param {Object} options (optional) An object containing properties which may override the options
715      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
716      * the most recently used options are reused).
717      */
718     reload : function(options){
719         this.load(Roo.applyIf(options||{}, this.lastOptions));
720     },
721
722     // private
723     // Called as a callback by the Reader during a load operation.
724     loadRecords : function(o, options, success){
725          
726         if(!o){
727             if(success !== false){
728                 this.fireEvent("load", this, [], options, o);
729             }
730             if(options.callback){
731                 options.callback.call(options.scope || this, [], options, false);
732             }
733             return;
734         }
735         // if data returned failure - throw an exception.
736         if (o.success === false) {
737             // show a message if no listener is registered.
738             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
739                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
740             }
741             // loadmask wil be hooked into this..
742             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
743             return;
744         }
745         var r = o.records, t = o.totalRecords || r.length;
746         
747         this.fireEvent("beforeloadadd", this, r, options, o);
748         
749         if(!options || options.add !== true){
750             if(this.pruneModifiedRecords){
751                 this.modified = [];
752             }
753             for(var i = 0, len = r.length; i < len; i++){
754                 r[i].join(this);
755             }
756             if(this.snapshot){
757                 this.data = this.snapshot;
758                 delete this.snapshot;
759             }
760             this.data.clear();
761             this.data.addAll(r);
762             this.totalLength = t;
763             this.applySort();
764             this.fireEvent("datachanged", this);
765         }else{
766             this.totalLength = Math.max(t, this.data.length+r.length);
767             this.add(r);
768         }
769         
770         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
771                 
772             var e = new Roo.data.Record({});
773
774             e.set(this.parent.displayField, this.parent.emptyTitle);
775             e.set(this.parent.valueField, '');
776
777             this.insert(0, e);
778         }
779             
780         this.fireEvent("load", this, r, options, o);
781         if(options.callback){
782             options.callback.call(options.scope || this, r, options, true);
783         }
784     },
785
786
787     /**
788      * Loads data from a passed data block. A Reader which understands the format of the data
789      * must have been configured in the constructor.
790      * @param {Object} data The data block from which to read the Records.  The format of the data expected
791      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
792      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
793      */
794     loadData : function(o, append){
795         var r = this.reader.readRecords(o);
796         this.loadRecords(r, {add: append}, true);
797     },
798     
799      /**
800      * using 'cn' the nested child reader read the child array into it's child stores.
801      * @param {Object} rec The record with a 'children array
802      */
803     loadDataFromChildren : function(rec)
804     {
805         this.loadData(this.reader.toLoadData(rec));
806     },
807     
808
809     /**
810      * Gets the number of cached records.
811      * <p>
812      * <em>If using paging, this may not be the total size of the dataset. If the data object
813      * used by the Reader contains the dataset size, then the getTotalCount() function returns
814      * the data set size</em>
815      */
816     getCount : function(){
817         return this.data.length || 0;
818     },
819
820     /**
821      * Gets the total number of records in the dataset as returned by the server.
822      * <p>
823      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
824      * the dataset size</em>
825      */
826     getTotalCount : function(){
827         return this.totalLength || 0;
828     },
829
830     /**
831      * Returns the sort state of the Store as an object with two properties:
832      * <pre><code>
833  field {String} The name of the field by which the Records are sorted
834  direction {String} The sort order, "ASC" or "DESC"
835      * </code></pre>
836      */
837     getSortState : function(){
838         return this.sortInfo;
839     },
840
841     // private
842     applySort : function(){
843         if(this.sortInfo && !this.remoteSort){
844             var s = this.sortInfo, f = s.field;
845             var st = this.fields.get(f).sortType;
846             var fn = function(r1, r2){
847                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
848                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
849             };
850             this.data.sort(s.direction, fn);
851             if(this.snapshot && this.snapshot != this.data){
852                 this.snapshot.sort(s.direction, fn);
853             }
854         }
855     },
856
857     /**
858      * Sets the default sort column and order to be used by the next load operation.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     setDefaultSort : function(field, dir){
863         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
864     },
865
866     /**
867      * Sort the Records.
868      * If remote sorting is used, the sort is performed on the server, and the cache is
869      * reloaded. If local sorting is used, the cache is sorted internally.
870      * @param {String} fieldName The name of the field to sort by.
871      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
872      */
873     sort : function(fieldName, dir){
874         var f = this.fields.get(fieldName);
875         if(!dir){
876             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
877             
878             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
879                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
880             }else{
881                 dir = f.sortDir;
882             }
883         }
884         this.sortToggle[f.name] = dir;
885         this.sortInfo = {field: f.name, direction: dir};
886         if(!this.remoteSort){
887             this.applySort();
888             this.fireEvent("datachanged", this);
889         }else{
890             this.load(this.lastOptions);
891         }
892     },
893
894     /**
895      * Calls the specified function for each of the Records in the cache.
896      * @param {Function} fn The function to call. The Record is passed as the first parameter.
897      * Returning <em>false</em> aborts and exits the iteration.
898      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
899      */
900     each : function(fn, scope){
901         this.data.each(fn, scope);
902     },
903
904     /**
905      * Gets all records modified since the last commit.  Modified records are persisted across load operations
906      * (e.g., during paging).
907      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
908      */
909     getModifiedRecords : function(){
910         return this.modified;
911     },
912
913     // private
914     createFilterFn : function(property, value, anyMatch){
915         if(!value.exec){ // not a regex
916             value = String(value);
917             if(value.length == 0){
918                 return false;
919             }
920             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
921         }
922         return function(r){
923             return value.test(r.data[property]);
924         };
925     },
926
927     /**
928      * Sums the value of <i>property</i> for each record between start and end and returns the result.
929      * @param {String} property A field on your records
930      * @param {Number} start The record index to start at (defaults to 0)
931      * @param {Number} end The last record index to include (defaults to length - 1)
932      * @return {Number} The sum
933      */
934     sum : function(property, start, end){
935         var rs = this.data.items, v = 0;
936         start = start || 0;
937         end = (end || end === 0) ? end : rs.length-1;
938
939         for(var i = start; i <= end; i++){
940             v += (rs[i].data[property] || 0);
941         }
942         return v;
943     },
944
945     /**
946      * Filter the records by a specified property.
947      * @param {String} field A field on your records
948      * @param {String/RegExp} value Either a string that the field
949      * should start with or a RegExp to test against the field
950      * @param {Boolean} anyMatch True to match any part not just the beginning
951      */
952     filter : function(property, value, anyMatch){
953         var fn = this.createFilterFn(property, value, anyMatch);
954         return fn ? this.filterBy(fn) : this.clearFilter();
955     },
956
957     /**
958      * Filter by a function. The specified function will be called with each
959      * record in this data source. If the function returns true the record is included,
960      * otherwise it is filtered.
961      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
962      * @param {Object} scope (optional) The scope of the function (defaults to this)
963      */
964     filterBy : function(fn, scope){
965         this.snapshot = this.snapshot || this.data;
966         this.data = this.queryBy(fn, scope||this);
967         this.fireEvent("datachanged", this);
968     },
969
970     /**
971      * Query the records by a specified property.
972      * @param {String} field A field on your records
973      * @param {String/RegExp} value Either a string that the field
974      * should start with or a RegExp to test against the field
975      * @param {Boolean} anyMatch True to match any part not just the beginning
976      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
977      */
978     query : function(property, value, anyMatch){
979         var fn = this.createFilterFn(property, value, anyMatch);
980         return fn ? this.queryBy(fn) : this.data.clone();
981     },
982
983     /**
984      * Query by a function. The specified function will be called with each
985      * record in this data source. If the function returns true the record is included
986      * in the results.
987      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
988      * @param {Object} scope (optional) The scope of the function (defaults to this)
989       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
990      **/
991     queryBy : function(fn, scope){
992         var data = this.snapshot || this.data;
993         return data.filterBy(fn, scope||this);
994     },
995
996     /**
997      * Collects unique values for a particular dataIndex from this store.
998      * @param {String} dataIndex The property to collect
999      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
1000      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
1001      * @return {Array} An array of the unique values
1002      **/
1003     collect : function(dataIndex, allowNull, bypassFilter){
1004         var d = (bypassFilter === true && this.snapshot) ?
1005                 this.snapshot.items : this.data.items;
1006         var v, sv, r = [], l = {};
1007         for(var i = 0, len = d.length; i < len; i++){
1008             v = d[i].data[dataIndex];
1009             sv = String(v);
1010             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1011                 l[sv] = true;
1012                 r[r.length] = v;
1013             }
1014         }
1015         return r;
1016     },
1017
1018     /**
1019      * Revert to a view of the Record cache with no filtering applied.
1020      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1021      */
1022     clearFilter : function(suppressEvent){
1023         if(this.snapshot && this.snapshot != this.data){
1024             this.data = this.snapshot;
1025             delete this.snapshot;
1026             if(suppressEvent !== true){
1027                 this.fireEvent("datachanged", this);
1028             }
1029         }
1030     },
1031
1032     // private
1033     afterEdit : function(record){
1034         if(this.modified.indexOf(record) == -1){
1035             this.modified.push(record);
1036         }
1037         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1038     },
1039     
1040     // private
1041     afterReject : function(record){
1042         this.modified.remove(record);
1043         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1044     },
1045
1046     // private
1047     afterCommit : function(record){
1048         this.modified.remove(record);
1049         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1050     },
1051
1052     /**
1053      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1054      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1055      */
1056     commitChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].commit();
1061         }
1062     },
1063
1064     /**
1065      * Cancel outstanding changes on all changed records.
1066      */
1067     rejectChanges : function(){
1068         var m = this.modified.slice(0);
1069         this.modified = [];
1070         for(var i = 0, len = m.length; i < len; i++){
1071             m[i].reject();
1072         }
1073     },
1074
1075     onMetaChange : function(meta, rtype, o){
1076         this.recordType = rtype;
1077         this.fields = rtype.prototype.fields;
1078         delete this.snapshot;
1079         this.sortInfo = meta.sortInfo || this.sortInfo;
1080         this.modified = [];
1081         this.fireEvent('metachange', this, this.reader.meta);
1082     },
1083     
1084     moveIndex : function(data, type)
1085     {
1086         var index = this.indexOf(data);
1087         
1088         var newIndex = index + type;
1089         
1090         this.remove(data);
1091         
1092         this.insert(newIndex, data);
1093         
1094     }
1095 });/*
1096  * Based on:
1097  * Ext JS Library 1.1.1
1098  * Copyright(c) 2006-2007, Ext JS, LLC.
1099  *
1100  * Originally Released Under LGPL - original licence link has changed is not relivant.
1101  *
1102  * Fork - LGPL
1103  * <script type="text/javascript">
1104  */
1105
1106 /**
1107  * @class Roo.data.SimpleStore
1108  * @extends Roo.data.Store
1109  * Small helper class to make creating Stores from Array data easier.
1110  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1111  * @cfg {Array} fields An array of field definition objects, or field name strings.
1112  * @cfg {Object} an existing reader (eg. copied from another store)
1113  * @cfg {Array} data The multi-dimensional array of data
1114  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1115  * @cfg {Roo.data.Reader} reader  [not-required] 
1116  * @constructor
1117  * @param {Object} config
1118  */
1119 Roo.data.SimpleStore = function(config)
1120 {
1121     Roo.data.SimpleStore.superclass.constructor.call(this, {
1122         isLocal : true,
1123         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1124                 id: config.id
1125             },
1126             Roo.data.Record.create(config.fields)
1127         ),
1128         proxy : new Roo.data.MemoryProxy(config.data)
1129     });
1130     this.load();
1131 };
1132 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1133  * Based on:
1134  * Ext JS Library 1.1.1
1135  * Copyright(c) 2006-2007, Ext JS, LLC.
1136  *
1137  * Originally Released Under LGPL - original licence link has changed is not relivant.
1138  *
1139  * Fork - LGPL
1140  * <script type="text/javascript">
1141  */
1142
1143 /**
1144 /**
1145  * @extends Roo.data.Store
1146  * @class Roo.data.JsonStore
1147  * Small helper class to make creating Stores for JSON data easier. <br/>
1148 <pre><code>
1149 var store = new Roo.data.JsonStore({
1150     url: 'get-images.php',
1151     root: 'images',
1152     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1153 });
1154 </code></pre>
1155  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1156  * JsonReader and HttpProxy (unless inline data is provided).</b>
1157  * @cfg {Array} fields An array of field definition objects, or field name strings.
1158  * @constructor
1159  * @param {Object} config
1160  */
1161 Roo.data.JsonStore = function(c){
1162     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1163         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1164         reader: new Roo.data.JsonReader(c, c.fields)
1165     }));
1166 };
1167 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1168  * Based on:
1169  * Ext JS Library 1.1.1
1170  * Copyright(c) 2006-2007, Ext JS, LLC.
1171  *
1172  * Originally Released Under LGPL - original licence link has changed is not relivant.
1173  *
1174  * Fork - LGPL
1175  * <script type="text/javascript">
1176  */
1177
1178  
1179 Roo.data.Field = function(config){
1180     if(typeof config == "string"){
1181         config = {name: config};
1182     }
1183     Roo.apply(this, config);
1184     
1185     if(!this.type){
1186         this.type = "auto";
1187     }
1188     
1189     var st = Roo.data.SortTypes;
1190     // named sortTypes are supported, here we look them up
1191     if(typeof this.sortType == "string"){
1192         this.sortType = st[this.sortType];
1193     }
1194     
1195     // set default sortType for strings and dates
1196     if(!this.sortType){
1197         switch(this.type){
1198             case "string":
1199                 this.sortType = st.asUCString;
1200                 break;
1201             case "date":
1202                 this.sortType = st.asDate;
1203                 break;
1204             default:
1205                 this.sortType = st.none;
1206         }
1207     }
1208
1209     // define once
1210     var stripRe = /[\$,%]/g;
1211
1212     // prebuilt conversion function for this field, instead of
1213     // switching every time we're reading a value
1214     if(!this.convert){
1215         var cv, dateFormat = this.dateFormat;
1216         switch(this.type){
1217             case "":
1218             case "auto":
1219             case undefined:
1220                 cv = function(v){ return v; };
1221                 break;
1222             case "string":
1223                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1224                 break;
1225             case "int":
1226                 cv = function(v){
1227                     return v !== undefined && v !== null && v !== '' ?
1228                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1229                     };
1230                 break;
1231             case "float":
1232                 cv = function(v){
1233                     return v !== undefined && v !== null && v !== '' ?
1234                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1235                     };
1236                 break;
1237             case "bool":
1238             case "boolean":
1239                 cv = function(v){ return v === true || v === "true" || v == 1; };
1240                 break;
1241             case "date":
1242                 cv = function(v){
1243                     if(!v){
1244                         return '';
1245                     }
1246                     if(v instanceof Date){
1247                         return v;
1248                     }
1249                     if(dateFormat){
1250                         if(dateFormat == "timestamp"){
1251                             return new Date(v*1000);
1252                         }
1253                         return Date.parseDate(v, dateFormat);
1254                     }
1255                     var parsed = Date.parse(v);
1256                     return parsed ? new Date(parsed) : null;
1257                 };
1258              break;
1259             
1260         }
1261         this.convert = cv;
1262     }
1263 };
1264
1265 Roo.data.Field.prototype = {
1266     dateFormat: null,
1267     defaultValue: "",
1268     mapping: null,
1269     sortType : null,
1270     sortDir : "ASC"
1271 };/*
1272  * Based on:
1273  * Ext JS Library 1.1.1
1274  * Copyright(c) 2006-2007, Ext JS, LLC.
1275  *
1276  * Originally Released Under LGPL - original licence link has changed is not relivant.
1277  *
1278  * Fork - LGPL
1279  * <script type="text/javascript">
1280  */
1281  
1282 // Base class for reading structured data from a data source.  This class is intended to be
1283 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1284
1285 /**
1286  * @class Roo.data.DataReader
1287  * @abstract
1288  * Base class for reading structured data from a data source.  This class is intended to be
1289  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1290  */
1291
1292 Roo.data.DataReader = function(meta, recordType){
1293     
1294     this.meta = meta;
1295     
1296     this.recordType = recordType instanceof Array ? 
1297         Roo.data.Record.create(recordType) : recordType;
1298 };
1299
1300 Roo.data.DataReader.prototype = {
1301     
1302     
1303     readerType : 'Data',
1304      /**
1305      * Create an empty record
1306      * @param {Object} data (optional) - overlay some values
1307      * @return {Roo.data.Record} record created.
1308      */
1309     newRow :  function(d) {
1310         var da =  {};
1311         this.recordType.prototype.fields.each(function(c) {
1312             switch( c.type) {
1313                 case 'int' : da[c.name] = 0; break;
1314                 case 'date' : da[c.name] = new Date(); break;
1315                 case 'float' : da[c.name] = 0.0; break;
1316                 case 'boolean' : da[c.name] = false; break;
1317                 default : da[c.name] = ""; break;
1318             }
1319             
1320         });
1321         return new this.recordType(Roo.apply(da, d));
1322     }
1323     
1324     
1325 };/*
1326  * Based on:
1327  * Ext JS Library 1.1.1
1328  * Copyright(c) 2006-2007, Ext JS, LLC.
1329  *
1330  * Originally Released Under LGPL - original licence link has changed is not relivant.
1331  *
1332  * Fork - LGPL
1333  * <script type="text/javascript">
1334  */
1335
1336 /**
1337  * @class Roo.data.DataProxy
1338  * @extends Roo.util.Observable
1339  * @abstract
1340  * This class is an abstract base class for implementations which provide retrieval of
1341  * unformatted data objects.<br>
1342  * <p>
1343  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1344  * (of the appropriate type which knows how to parse the data object) to provide a block of
1345  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1346  * <p>
1347  * Custom implementations must implement the load method as described in
1348  * {@link Roo.data.HttpProxy#load}.
1349  */
1350 Roo.data.DataProxy = function(){
1351     this.addEvents({
1352         /**
1353          * @event beforeload
1354          * Fires before a network request is made to retrieve a data object.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} params The params parameter to the load function.
1357          */
1358         beforeload : true,
1359         /**
1360          * @event load
1361          * Fires before the load method's callback is called.
1362          * @param {Object} This DataProxy object.
1363          * @param {Object} o The data object.
1364          * @param {Object} arg The callback argument object passed to the load function.
1365          */
1366         load : true,
1367         /**
1368          * @event loadexception
1369          * Fires if an Exception occurs during data retrieval.
1370          * @param {Object} This DataProxy object.
1371          * @param {Object} o The data object.
1372          * @param {Object} arg The callback argument object passed to the load function.
1373          * @param {Object} e The Exception.
1374          */
1375         loadexception : true
1376     });
1377     Roo.data.DataProxy.superclass.constructor.call(this);
1378 };
1379
1380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1381
1382     /**
1383      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1384      */
1385 /*
1386  * Based on:
1387  * Ext JS Library 1.1.1
1388  * Copyright(c) 2006-2007, Ext JS, LLC.
1389  *
1390  * Originally Released Under LGPL - original licence link has changed is not relivant.
1391  *
1392  * Fork - LGPL
1393  * <script type="text/javascript">
1394  */
1395 /**
1396  * @class Roo.data.MemoryProxy
1397  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1398  * to the Reader when its load method is called.
1399  * @constructor
1400  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1401  */
1402 Roo.data.MemoryProxy = function(data){
1403     if (data.data) {
1404         data = data.data;
1405     }
1406     Roo.data.MemoryProxy.superclass.constructor.call(this);
1407     this.data = data;
1408 };
1409
1410 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1411     
1412     /**
1413      * Load data from the requested source (in this case an in-memory
1414      * data object passed to the constructor), read the data object into
1415      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1416      * process that block using the passed callback.
1417      * @param {Object} params This parameter is not used by the MemoryProxy class.
1418      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1419      * object into a block of Roo.data.Records.
1420      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1421      * The function must be passed <ul>
1422      * <li>The Record block object</li>
1423      * <li>The "arg" argument from the load function</li>
1424      * <li>A boolean success indicator</li>
1425      * </ul>
1426      * @param {Object} scope The scope in which to call the callback
1427      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1428      */
1429     load : function(params, reader, callback, scope, arg){
1430         params = params || {};
1431         var result;
1432         try {
1433             result = reader.readRecords(params.data ? params.data :this.data);
1434         }catch(e){
1435             this.fireEvent("loadexception", this, arg, null, e);
1436             callback.call(scope, null, arg, false);
1437             return;
1438         }
1439         callback.call(scope, result, arg, true);
1440     },
1441     
1442     // private
1443     update : function(params, records){
1444         
1445     }
1446 });/*
1447  * Based on:
1448  * Ext JS Library 1.1.1
1449  * Copyright(c) 2006-2007, Ext JS, LLC.
1450  *
1451  * Originally Released Under LGPL - original licence link has changed is not relivant.
1452  *
1453  * Fork - LGPL
1454  * <script type="text/javascript">
1455  */
1456 /**
1457  * @class Roo.data.HttpProxy
1458  * @extends Roo.data.DataProxy
1459  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1460  * configured to reference a certain URL.<br><br>
1461  * <p>
1462  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1463  * from which the running page was served.<br><br>
1464  * <p>
1465  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1466  * <p>
1467  * Be aware that to enable the browser to parse an XML document, the server must set
1468  * the Content-Type header in the HTTP response to "text/xml".
1469  * @constructor
1470  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1471  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1472  * will be used to make the request.
1473  */
1474 Roo.data.HttpProxy = function(conn){
1475     Roo.data.HttpProxy.superclass.constructor.call(this);
1476     // is conn a conn config or a real conn?
1477     this.conn = conn;
1478     this.useAjax = !conn || !conn.events;
1479   
1480 };
1481
1482 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1483     // thse are take from connection...
1484     
1485     /**
1486      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1487      */
1488     /**
1489      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1490      * extra parameters to each request made by this object. (defaults to undefined)
1491      */
1492     /**
1493      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1494      *  to each request made by this object. (defaults to undefined)
1495      */
1496     /**
1497      * @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)
1498      */
1499     /**
1500      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1501      */
1502      /**
1503      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1504      * @type Boolean
1505      */
1506   
1507
1508     /**
1509      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1510      * @type Boolean
1511      */
1512     /**
1513      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1514      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1515      * a finer-grained basis than the DataProxy events.
1516      */
1517     getConnection : function(){
1518         return this.useAjax ? Roo.Ajax : this.conn;
1519     },
1520
1521     /**
1522      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1523      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1524      * process that block using the passed callback.
1525      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1526      * for the request to the remote server.
1527      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1528      * object into a block of Roo.data.Records.
1529      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1530      * The function must be passed <ul>
1531      * <li>The Record block object</li>
1532      * <li>The "arg" argument from the load function</li>
1533      * <li>A boolean success indicator</li>
1534      * </ul>
1535      * @param {Object} scope The scope in which to call the callback
1536      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1537      */
1538     load : function(params, reader, callback, scope, arg){
1539         if(this.fireEvent("beforeload", this, params) !== false){
1540             var  o = {
1541                 params : params || {},
1542                 request: {
1543                     callback : callback,
1544                     scope : scope,
1545                     arg : arg
1546                 },
1547                 reader: reader,
1548                 callback : this.loadResponse,
1549                 scope: this
1550             };
1551             if(this.useAjax){
1552                 Roo.applyIf(o, this.conn);
1553                 if(this.activeRequest){
1554                     Roo.Ajax.abort(this.activeRequest);
1555                 }
1556                 this.activeRequest = Roo.Ajax.request(o);
1557             }else{
1558                 this.conn.request(o);
1559             }
1560         }else{
1561             callback.call(scope||this, null, arg, false);
1562         }
1563     },
1564
1565     // private
1566     loadResponse : function(o, success, response){
1567         delete this.activeRequest;
1568         if(!success){
1569             this.fireEvent("loadexception", this, o, response);
1570             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1571             return;
1572         }
1573         var result;
1574         try {
1575             result = o.reader.read(response);
1576         }catch(e){
1577             o.success = false;
1578             o.raw = { errorMsg : response.responseText };
1579             this.fireEvent("loadexception", this, o, response, e);
1580             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1581             return;
1582         }
1583         
1584         this.fireEvent("load", this, o, o.request.arg);
1585         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1586     },
1587
1588     // private
1589     update : function(dataSet){
1590
1591     },
1592
1593     // private
1594     updateResponse : function(dataSet){
1595
1596     }
1597 });/*
1598  * Based on:
1599  * Ext JS Library 1.1.1
1600  * Copyright(c) 2006-2007, Ext JS, LLC.
1601  *
1602  * Originally Released Under LGPL - original licence link has changed is not relivant.
1603  *
1604  * Fork - LGPL
1605  * <script type="text/javascript">
1606  */
1607
1608 /**
1609  * @class Roo.data.ScriptTagProxy
1610  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1611  * other than the originating domain of the running page.<br><br>
1612  * <p>
1613  * <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
1614  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1615  * <p>
1616  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1617  * source code that is used as the source inside a &lt;script> tag.<br><br>
1618  * <p>
1619  * In order for the browser to process the returned data, the server must wrap the data object
1620  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1621  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1622  * depending on whether the callback name was passed:
1623  * <p>
1624  * <pre><code>
1625 boolean scriptTag = false;
1626 String cb = request.getParameter("callback");
1627 if (cb != null) {
1628     scriptTag = true;
1629     response.setContentType("text/javascript");
1630 } else {
1631     response.setContentType("application/x-json");
1632 }
1633 Writer out = response.getWriter();
1634 if (scriptTag) {
1635     out.write(cb + "(");
1636 }
1637 out.print(dataBlock.toJsonString());
1638 if (scriptTag) {
1639     out.write(");");
1640 }
1641 </pre></code>
1642  *
1643  * @constructor
1644  * @param {Object} config A configuration object.
1645  */
1646 Roo.data.ScriptTagProxy = function(config){
1647     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1648     Roo.apply(this, config);
1649     this.head = document.getElementsByTagName("head")[0];
1650 };
1651
1652 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1653
1654 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1655     /**
1656      * @cfg {String} url The URL from which to request the data object.
1657      */
1658     /**
1659      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1660      */
1661     timeout : 30000,
1662     /**
1663      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1664      * the server the name of the callback function set up by the load call to process the returned data object.
1665      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1666      * javascript output which calls this named function passing the data object as its only parameter.
1667      */
1668     callbackParam : "callback",
1669     /**
1670      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1671      * name to the request.
1672      */
1673     nocache : true,
1674
1675     /**
1676      * Load data from the configured URL, read the data object into
1677      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1678      * process that block using the passed callback.
1679      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1680      * for the request to the remote server.
1681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1682      * object into a block of Roo.data.Records.
1683      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1684      * The function must be passed <ul>
1685      * <li>The Record block object</li>
1686      * <li>The "arg" argument from the load function</li>
1687      * <li>A boolean success indicator</li>
1688      * </ul>
1689      * @param {Object} scope The scope in which to call the callback
1690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1691      */
1692     load : function(params, reader, callback, scope, arg){
1693         if(this.fireEvent("beforeload", this, params) !== false){
1694
1695             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1696
1697             var url = this.url;
1698             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1699             if(this.nocache){
1700                 url += "&_dc=" + (new Date().getTime());
1701             }
1702             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1703             var trans = {
1704                 id : transId,
1705                 cb : "stcCallback"+transId,
1706                 scriptId : "stcScript"+transId,
1707                 params : params,
1708                 arg : arg,
1709                 url : url,
1710                 callback : callback,
1711                 scope : scope,
1712                 reader : reader
1713             };
1714             var conn = this;
1715
1716             window[trans.cb] = function(o){
1717                 conn.handleResponse(o, trans);
1718             };
1719
1720             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1721
1722             if(this.autoAbort !== false){
1723                 this.abort();
1724             }
1725
1726             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1727
1728             var script = document.createElement("script");
1729             script.setAttribute("src", url);
1730             script.setAttribute("type", "text/javascript");
1731             script.setAttribute("id", trans.scriptId);
1732             this.head.appendChild(script);
1733
1734             this.trans = trans;
1735         }else{
1736             callback.call(scope||this, null, arg, false);
1737         }
1738     },
1739
1740     // private
1741     isLoading : function(){
1742         return this.trans ? true : false;
1743     },
1744
1745     /**
1746      * Abort the current server request.
1747      */
1748     abort : function(){
1749         if(this.isLoading()){
1750             this.destroyTrans(this.trans);
1751         }
1752     },
1753
1754     // private
1755     destroyTrans : function(trans, isLoaded){
1756         this.head.removeChild(document.getElementById(trans.scriptId));
1757         clearTimeout(trans.timeoutId);
1758         if(isLoaded){
1759             window[trans.cb] = undefined;
1760             try{
1761                 delete window[trans.cb];
1762             }catch(e){}
1763         }else{
1764             // if hasn't been loaded, wait for load to remove it to prevent script error
1765             window[trans.cb] = function(){
1766                 window[trans.cb] = undefined;
1767                 try{
1768                     delete window[trans.cb];
1769                 }catch(e){}
1770             };
1771         }
1772     },
1773
1774     // private
1775     handleResponse : function(o, trans){
1776         this.trans = false;
1777         this.destroyTrans(trans, true);
1778         var result;
1779         try {
1780             result = trans.reader.readRecords(o);
1781         }catch(e){
1782             this.fireEvent("loadexception", this, o, trans.arg, e);
1783             trans.callback.call(trans.scope||window, null, trans.arg, false);
1784             return;
1785         }
1786         this.fireEvent("load", this, o, trans.arg);
1787         trans.callback.call(trans.scope||window, result, trans.arg, true);
1788     },
1789
1790     // private
1791     handleFailure : function(trans){
1792         this.trans = false;
1793         this.destroyTrans(trans, false);
1794         this.fireEvent("loadexception", this, null, trans.arg);
1795         trans.callback.call(trans.scope||window, null, trans.arg, false);
1796     }
1797 });/*
1798  * Based on:
1799  * Ext JS Library 1.1.1
1800  * Copyright(c) 2006-2007, Ext JS, LLC.
1801  *
1802  * Originally Released Under LGPL - original licence link has changed is not relivant.
1803  *
1804  * Fork - LGPL
1805  * <script type="text/javascript">
1806  */
1807
1808 /**
1809  * @class Roo.data.JsonReader
1810  * @extends Roo.data.DataReader
1811  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1812  * based on mappings in a provided Roo.data.Record constructor.
1813  * 
1814  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1815  * in the reply previously. 
1816  * 
1817  * <p>
1818  * Example code:
1819  * <pre><code>
1820 var RecordDef = Roo.data.Record.create([
1821     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1822     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1823 ]);
1824 var myReader = new Roo.data.JsonReader({
1825     totalProperty: "results",    // The property which contains the total dataset size (optional)
1826     root: "rows",                // The property which contains an Array of row objects
1827     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1828 }, RecordDef);
1829 </code></pre>
1830  * <p>
1831  * This would consume a JSON file like this:
1832  * <pre><code>
1833 { 'results': 2, 'rows': [
1834     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1835     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1836 }
1837 </code></pre>
1838  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1839  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1840  * paged from the remote server.
1841  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1842  * @cfg {String} root name of the property which contains the Array of row objects.
1843  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1844  * @cfg {Array} fields Array of field definition objects
1845  * @constructor
1846  * Create a new JsonReader
1847  * @param {Object} meta Metadata configuration options
1848  * @param {Object} recordType Either an Array of field definition objects,
1849  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1850  */
1851 Roo.data.JsonReader = function(meta, recordType){
1852     
1853     meta = meta || {};
1854     // set some defaults:
1855     Roo.applyIf(meta, {
1856         totalProperty: 'total',
1857         successProperty : 'success',
1858         root : 'data',
1859         id : 'id'
1860     });
1861     
1862     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1863 };
1864 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1865     
1866     readerType : 'Json',
1867     
1868     /**
1869      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1870      * Used by Store query builder to append _requestMeta to params.
1871      * 
1872      */
1873     metaFromRemote : false,
1874     /**
1875      * This method is only used by a DataProxy which has retrieved data from a remote server.
1876      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1877      * @return {Object} data A data block which is used by an Roo.data.Store object as
1878      * a cache of Roo.data.Records.
1879      */
1880     read : function(response){
1881         var json = response.responseText;
1882        
1883         var o = /* eval:var:o */ eval("("+json+")");
1884         if(!o) {
1885             throw {message: "JsonReader.read: Json object not found"};
1886         }
1887         
1888         if(o.metaData){
1889             
1890             delete this.ef;
1891             this.metaFromRemote = true;
1892             this.meta = o.metaData;
1893             this.recordType = Roo.data.Record.create(o.metaData.fields);
1894             this.onMetaChange(this.meta, this.recordType, o);
1895         }
1896         return this.readRecords(o);
1897     },
1898
1899     // private function a store will implement
1900     onMetaChange : function(meta, recordType, o){
1901
1902     },
1903
1904     /**
1905          * @ignore
1906          */
1907     simpleAccess: function(obj, subsc) {
1908         return obj[subsc];
1909     },
1910
1911         /**
1912          * @ignore
1913          */
1914     getJsonAccessor: function(){
1915         var re = /[\[\.]/;
1916         return function(expr) {
1917             try {
1918                 return(re.test(expr))
1919                     ? new Function("obj", "return obj." + expr)
1920                     : function(obj){
1921                         return obj[expr];
1922                     };
1923             } catch(e){}
1924             return Roo.emptyFn;
1925         };
1926     }(),
1927
1928     /**
1929      * Create a data block containing Roo.data.Records from an XML document.
1930      * @param {Object} o An object which contains an Array of row objects in the property specified
1931      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1932      * which contains the total size of the dataset.
1933      * @return {Object} data A data block which is used by an Roo.data.Store object as
1934      * a cache of Roo.data.Records.
1935      */
1936     readRecords : function(o){
1937         /**
1938          * After any data loads, the raw JSON data is available for further custom processing.
1939          * @type Object
1940          */
1941         this.o = o;
1942         var s = this.meta, Record = this.recordType,
1943             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1944
1945 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1946         if (!this.ef) {
1947             if(s.totalProperty) {
1948                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1949                 }
1950                 if(s.successProperty) {
1951                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1952                 }
1953                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1954                 if (s.id) {
1955                         var g = this.getJsonAccessor(s.id);
1956                         this.getId = function(rec) {
1957                                 var r = g(rec);  
1958                                 return (r === undefined || r === "") ? null : r;
1959                         };
1960                 } else {
1961                         this.getId = function(){return null;};
1962                 }
1963             this.ef = [];
1964             for(var jj = 0; jj < fl; jj++){
1965                 f = fi[jj];
1966                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1967                 this.ef[jj] = this.getJsonAccessor(map);
1968             }
1969         }
1970
1971         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1972         if(s.totalProperty){
1973             var vt = parseInt(this.getTotal(o), 10);
1974             if(!isNaN(vt)){
1975                 totalRecords = vt;
1976             }
1977         }
1978         if(s.successProperty){
1979             var vs = this.getSuccess(o);
1980             if(vs === false || vs === 'false'){
1981                 success = false;
1982             }
1983         }
1984         var records = [];
1985         for(var i = 0; i < c; i++){
1986             var n = root[i];
1987             var values = {};
1988             var id = this.getId(n);
1989             for(var j = 0; j < fl; j++){
1990                 f = fi[j];
1991                                 var v = this.ef[j](n);
1992                                 if (!f.convert) {
1993                                         Roo.log('missing convert for ' + f.name);
1994                                         Roo.log(f);
1995                                         continue;
1996                                 }
1997                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1998             }
1999                         if (!Record) {
2000                                 return {
2001                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2002                                         success : false,
2003                                         records : [],
2004                                         totalRecords : 0
2005                                 };
2006                         }
2007             var record = new Record(values, id);
2008             record.json = n;
2009             records[i] = record;
2010         }
2011         return {
2012             raw : o,
2013             success : success,
2014             records : records,
2015             totalRecords : totalRecords
2016         };
2017     },
2018     // used when loading children.. @see loadDataFromChildren
2019     toLoadData: function(rec)
2020     {
2021         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2022         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2023         return { data : data, total : data.length };
2024         
2025     }
2026 });/*
2027  * Based on:
2028  * Ext JS Library 1.1.1
2029  * Copyright(c) 2006-2007, Ext JS, LLC.
2030  *
2031  * Originally Released Under LGPL - original licence link has changed is not relivant.
2032  *
2033  * Fork - LGPL
2034  * <script type="text/javascript">
2035  */
2036
2037 /**
2038  * @class Roo.data.XmlReader
2039  * @extends Roo.data.DataReader
2040  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2041  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2042  * <p>
2043  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2044  * header in the HTTP response must be set to "text/xml".</em>
2045  * <p>
2046  * Example code:
2047  * <pre><code>
2048 var RecordDef = Roo.data.Record.create([
2049    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2050    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2051 ]);
2052 var myReader = new Roo.data.XmlReader({
2053    totalRecords: "results", // The element which contains the total dataset size (optional)
2054    record: "row",           // The repeated element which contains row information
2055    id: "id"                 // The element within the row that provides an ID for the record (optional)
2056 }, RecordDef);
2057 </code></pre>
2058  * <p>
2059  * This would consume an XML file like this:
2060  * <pre><code>
2061 &lt;?xml?>
2062 &lt;dataset>
2063  &lt;results>2&lt;/results>
2064  &lt;row>
2065    &lt;id>1&lt;/id>
2066    &lt;name>Bill&lt;/name>
2067    &lt;occupation>Gardener&lt;/occupation>
2068  &lt;/row>
2069  &lt;row>
2070    &lt;id>2&lt;/id>
2071    &lt;name>Ben&lt;/name>
2072    &lt;occupation>Horticulturalist&lt;/occupation>
2073  &lt;/row>
2074 &lt;/dataset>
2075 </code></pre>
2076  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2077  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2078  * paged from the remote server.
2079  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2080  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2081  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2082  * a record identifier value.
2083  * @constructor
2084  * Create a new XmlReader
2085  * @param {Object} meta Metadata configuration options
2086  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2087  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2088  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2089  */
2090 Roo.data.XmlReader = function(meta, recordType){
2091     meta = meta || {};
2092     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2093 };
2094 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2095     
2096     readerType : 'Xml',
2097     
2098     /**
2099      * This method is only used by a DataProxy which has retrieved data from a remote server.
2100          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2101          * to contain a method called 'responseXML' that returns an XML document object.
2102      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2103      * a cache of Roo.data.Records.
2104      */
2105     read : function(response){
2106         var doc = response.responseXML;
2107         if(!doc) {
2108             throw {message: "XmlReader.read: XML Document not available"};
2109         }
2110         return this.readRecords(doc);
2111     },
2112
2113     /**
2114      * Create a data block containing Roo.data.Records from an XML document.
2115          * @param {Object} doc A parsed XML document.
2116      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2117      * a cache of Roo.data.Records.
2118      */
2119     readRecords : function(doc){
2120         /**
2121          * After any data loads/reads, the raw XML Document is available for further custom processing.
2122          * @type XMLDocument
2123          */
2124         this.xmlData = doc;
2125         var root = doc.documentElement || doc;
2126         var q = Roo.DomQuery;
2127         var recordType = this.recordType, fields = recordType.prototype.fields;
2128         var sid = this.meta.id;
2129         var totalRecords = 0, success = true;
2130         if(this.meta.totalRecords){
2131             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2132         }
2133         
2134         if(this.meta.success){
2135             var sv = q.selectValue(this.meta.success, root, true);
2136             success = sv !== false && sv !== 'false';
2137         }
2138         var records = [];
2139         var ns = q.select(this.meta.record, root);
2140         for(var i = 0, len = ns.length; i < len; i++) {
2141                 var n = ns[i];
2142                 var values = {};
2143                 var id = sid ? q.selectValue(sid, n) : undefined;
2144                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2145                     var f = fields.items[j];
2146                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2147                     v = f.convert(v);
2148                     values[f.name] = v;
2149                 }
2150                 var record = new recordType(values, id);
2151                 record.node = n;
2152                 records[records.length] = record;
2153             }
2154
2155             return {
2156                 success : success,
2157                 records : records,
2158                 totalRecords : totalRecords || records.length
2159             };
2160     }
2161 });/*
2162  * Based on:
2163  * Ext JS Library 1.1.1
2164  * Copyright(c) 2006-2007, Ext JS, LLC.
2165  *
2166  * Originally Released Under LGPL - original licence link has changed is not relivant.
2167  *
2168  * Fork - LGPL
2169  * <script type="text/javascript">
2170  */
2171
2172 /**
2173  * @class Roo.data.ArrayReader
2174  * @extends Roo.data.DataReader
2175  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2176  * Each element of that Array represents a row of data fields. The
2177  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2178  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2179  * <p>
2180  * Example code:.
2181  * <pre><code>
2182 var RecordDef = Roo.data.Record.create([
2183     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2184     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2185 ]);
2186 var myReader = new Roo.data.ArrayReader({
2187     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2188 }, RecordDef);
2189 </code></pre>
2190  * <p>
2191  * This would consume an Array like this:
2192  * <pre><code>
2193 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2194   </code></pre>
2195  
2196  * @constructor
2197  * Create a new JsonReader
2198  * @param {Object} meta Metadata configuration options.
2199  * @param {Object|Array} recordType Either an Array of field definition objects
2200  * 
2201  * @cfg {Array} fields Array of field definition objects
2202  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2203  * as specified to {@link Roo.data.Record#create},
2204  * or an {@link Roo.data.Record} object
2205  *
2206  * 
2207  * created using {@link Roo.data.Record#create}.
2208  */
2209 Roo.data.ArrayReader = function(meta, recordType)
2210 {    
2211     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2212 };
2213
2214 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2215     
2216       /**
2217      * Create a data block containing Roo.data.Records from an XML document.
2218      * @param {Object} o An Array of row objects which represents the dataset.
2219      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2220      * a cache of Roo.data.Records.
2221      */
2222     readRecords : function(o)
2223     {
2224         var sid = this.meta ? this.meta.id : null;
2225         var recordType = this.recordType, fields = recordType.prototype.fields;
2226         var records = [];
2227         var root = o;
2228         for(var i = 0; i < root.length; i++){
2229             var n = root[i];
2230             var values = {};
2231             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2232             for(var j = 0, jlen = fields.length; j < jlen; j++){
2233                 var f = fields.items[j];
2234                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2235                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2236                 v = f.convert(v);
2237                 values[f.name] = v;
2238             }
2239             var record = new recordType(values, id);
2240             record.json = n;
2241             records[records.length] = record;
2242         }
2243         return {
2244             records : records,
2245             totalRecords : records.length
2246         };
2247     },
2248     // used when loading children.. @see loadDataFromChildren
2249     toLoadData: function(rec)
2250     {
2251         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2252         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2253         
2254     }
2255     
2256     
2257 });/*
2258  * Based on:
2259  * Ext JS Library 1.1.1
2260  * Copyright(c) 2006-2007, Ext JS, LLC.
2261  *
2262  * Originally Released Under LGPL - original licence link has changed is not relivant.
2263  *
2264  * Fork - LGPL
2265  * <script type="text/javascript">
2266  */
2267
2268
2269 /**
2270  * @class Roo.data.Tree
2271  * @extends Roo.util.Observable
2272  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2273  * in the tree have most standard DOM functionality.
2274  * @constructor
2275  * @param {Node} root (optional) The root node
2276  */
2277 Roo.data.Tree = function(root){
2278    this.nodeHash = {};
2279    /**
2280     * The root node for this tree
2281     * @type Node
2282     */
2283    this.root = null;
2284    if(root){
2285        this.setRootNode(root);
2286    }
2287    this.addEvents({
2288        /**
2289         * @event append
2290         * Fires when a new child node is appended to a node in this tree.
2291         * @param {Tree} tree The owner tree
2292         * @param {Node} parent The parent node
2293         * @param {Node} node The newly appended node
2294         * @param {Number} index The index of the newly appended node
2295         */
2296        "append" : true,
2297        /**
2298         * @event remove
2299         * Fires when a child node is removed from a node in this tree.
2300         * @param {Tree} tree The owner tree
2301         * @param {Node} parent The parent node
2302         * @param {Node} node The child node removed
2303         */
2304        "remove" : true,
2305        /**
2306         * @event move
2307         * Fires when a node is moved to a new location in the tree
2308         * @param {Tree} tree The owner tree
2309         * @param {Node} node The node moved
2310         * @param {Node} oldParent The old parent of this node
2311         * @param {Node} newParent The new parent of this node
2312         * @param {Number} index The index it was moved to
2313         */
2314        "move" : true,
2315        /**
2316         * @event insert
2317         * Fires when a new child node is inserted in a node in this tree.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} parent The parent node
2320         * @param {Node} node The child node inserted
2321         * @param {Node} refNode The child node the node was inserted before
2322         */
2323        "insert" : true,
2324        /**
2325         * @event beforeappend
2326         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2327         * @param {Tree} tree The owner tree
2328         * @param {Node} parent The parent node
2329         * @param {Node} node The child node to be appended
2330         */
2331        "beforeappend" : true,
2332        /**
2333         * @event beforeremove
2334         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2335         * @param {Tree} tree The owner tree
2336         * @param {Node} parent The parent node
2337         * @param {Node} node The child node to be removed
2338         */
2339        "beforeremove" : true,
2340        /**
2341         * @event beforemove
2342         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2343         * @param {Tree} tree The owner tree
2344         * @param {Node} node The node being moved
2345         * @param {Node} oldParent The parent of the node
2346         * @param {Node} newParent The new parent the node is moving to
2347         * @param {Number} index The index it is being moved to
2348         */
2349        "beforemove" : true,
2350        /**
2351         * @event beforeinsert
2352         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2353         * @param {Tree} tree The owner tree
2354         * @param {Node} parent The parent node
2355         * @param {Node} node The child node to be inserted
2356         * @param {Node} refNode The child node the node is being inserted before
2357         */
2358        "beforeinsert" : true
2359    });
2360
2361     Roo.data.Tree.superclass.constructor.call(this);
2362 };
2363
2364 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2365     pathSeparator: "/",
2366
2367     proxyNodeEvent : function(){
2368         return this.fireEvent.apply(this, arguments);
2369     },
2370
2371     /**
2372      * Returns the root node for this tree.
2373      * @return {Node}
2374      */
2375     getRootNode : function(){
2376         return this.root;
2377     },
2378
2379     /**
2380      * Sets the root node for this tree.
2381      * @param {Node} node
2382      * @return {Node}
2383      */
2384     setRootNode : function(node){
2385         this.root = node;
2386         node.ownerTree = this;
2387         node.isRoot = true;
2388         this.registerNode(node);
2389         return node;
2390     },
2391
2392     /**
2393      * Gets a node in this tree by its id.
2394      * @param {String} id
2395      * @return {Node}
2396      */
2397     getNodeById : function(id){
2398         return this.nodeHash[id];
2399     },
2400
2401     registerNode : function(node){
2402         this.nodeHash[node.id] = node;
2403     },
2404
2405     unregisterNode : function(node){
2406         delete this.nodeHash[node.id];
2407     },
2408
2409     toString : function(){
2410         return "[Tree"+(this.id?" "+this.id:"")+"]";
2411     }
2412 });
2413
2414 /**
2415  * @class Roo.data.Node
2416  * @extends Roo.util.Observable
2417  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2418  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2419  * @constructor
2420  * @param {Object} attributes The attributes/config for the node
2421  */
2422 Roo.data.Node = function(attributes){
2423     /**
2424      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2425      * @type {Object}
2426      */
2427     this.attributes = attributes || {};
2428     this.leaf = this.attributes.leaf;
2429     /**
2430      * The node id. @type String
2431      */
2432     this.id = this.attributes.id;
2433     if(!this.id){
2434         this.id = Roo.id(null, "ynode-");
2435         this.attributes.id = this.id;
2436     }
2437      
2438     
2439     /**
2440      * All child nodes of this node. @type Array
2441      */
2442     this.childNodes = [];
2443     if(!this.childNodes.indexOf){ // indexOf is a must
2444         this.childNodes.indexOf = function(o){
2445             for(var i = 0, len = this.length; i < len; i++){
2446                 if(this[i] == o) {
2447                     return i;
2448                 }
2449             }
2450             return -1;
2451         };
2452     }
2453     /**
2454      * The parent node for this node. @type Node
2455      */
2456     this.parentNode = null;
2457     /**
2458      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2459      */
2460     this.firstChild = null;
2461     /**
2462      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2463      */
2464     this.lastChild = null;
2465     /**
2466      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2467      */
2468     this.previousSibling = null;
2469     /**
2470      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2471      */
2472     this.nextSibling = null;
2473
2474     this.addEvents({
2475        /**
2476         * @event append
2477         * Fires when a new child node is appended
2478         * @param {Tree} tree The owner tree
2479         * @param {Node} this This node
2480         * @param {Node} node The newly appended node
2481         * @param {Number} index The index of the newly appended node
2482         */
2483        "append" : true,
2484        /**
2485         * @event remove
2486         * Fires when a child node is removed
2487         * @param {Tree} tree The owner tree
2488         * @param {Node} this This node
2489         * @param {Node} node The removed node
2490         */
2491        "remove" : true,
2492        /**
2493         * @event move
2494         * Fires when this node is moved to a new location in the tree
2495         * @param {Tree} tree The owner tree
2496         * @param {Node} this This node
2497         * @param {Node} oldParent The old parent of this node
2498         * @param {Node} newParent The new parent of this node
2499         * @param {Number} index The index it was moved to
2500         */
2501        "move" : true,
2502        /**
2503         * @event insert
2504         * Fires when a new child node is inserted.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} node The child node inserted
2508         * @param {Node} refNode The child node the node was inserted before
2509         */
2510        "insert" : true,
2511        /**
2512         * @event beforeappend
2513         * Fires before a new child is appended, return false to cancel the append.
2514         * @param {Tree} tree The owner tree
2515         * @param {Node} this This node
2516         * @param {Node} node The child node to be appended
2517         */
2518        "beforeappend" : true,
2519        /**
2520         * @event beforeremove
2521         * Fires before a child is removed, return false to cancel the remove.
2522         * @param {Tree} tree The owner tree
2523         * @param {Node} this This node
2524         * @param {Node} node The child node to be removed
2525         */
2526        "beforeremove" : true,
2527        /**
2528         * @event beforemove
2529         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2530         * @param {Tree} tree The owner tree
2531         * @param {Node} this This node
2532         * @param {Node} oldParent The parent of this node
2533         * @param {Node} newParent The new parent this node is moving to
2534         * @param {Number} index The index it is being moved to
2535         */
2536        "beforemove" : true,
2537        /**
2538         * @event beforeinsert
2539         * Fires before a new child is inserted, return false to cancel the insert.
2540         * @param {Tree} tree The owner tree
2541         * @param {Node} this This node
2542         * @param {Node} node The child node to be inserted
2543         * @param {Node} refNode The child node the node is being inserted before
2544         */
2545        "beforeinsert" : true
2546    });
2547     this.listeners = this.attributes.listeners;
2548     Roo.data.Node.superclass.constructor.call(this);
2549 };
2550
2551 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2552     fireEvent : function(evtName){
2553         // first do standard event for this node
2554         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2555             return false;
2556         }
2557         // then bubble it up to the tree if the event wasn't cancelled
2558         var ot = this.getOwnerTree();
2559         if(ot){
2560             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2561                 return false;
2562             }
2563         }
2564         return true;
2565     },
2566
2567     /**
2568      * Returns true if this node is a leaf
2569      * @return {Boolean}
2570      */
2571     isLeaf : function(){
2572         return this.leaf === true;
2573     },
2574
2575     // private
2576     setFirstChild : function(node){
2577         this.firstChild = node;
2578     },
2579
2580     //private
2581     setLastChild : function(node){
2582         this.lastChild = node;
2583     },
2584
2585
2586     /**
2587      * Returns true if this node is the last child of its parent
2588      * @return {Boolean}
2589      */
2590     isLast : function(){
2591        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2592     },
2593
2594     /**
2595      * Returns true if this node is the first child of its parent
2596      * @return {Boolean}
2597      */
2598     isFirst : function(){
2599        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2600     },
2601
2602     hasChildNodes : function(){
2603         return !this.isLeaf() && this.childNodes.length > 0;
2604     },
2605
2606     /**
2607      * Insert node(s) as the last child node of this node.
2608      * @param {Node/Array} node The node or Array of nodes to append
2609      * @return {Node} The appended node if single append, or null if an array was passed
2610      */
2611     appendChild : function(node){
2612         var multi = false;
2613         if(node instanceof Array){
2614             multi = node;
2615         }else if(arguments.length > 1){
2616             multi = arguments;
2617         }
2618         
2619         // if passed an array or multiple args do them one by one
2620         if(multi){
2621             for(var i = 0, len = multi.length; i < len; i++) {
2622                 this.appendChild(multi[i]);
2623             }
2624         }else{
2625             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2626                 return false;
2627             }
2628             var index = this.childNodes.length;
2629             var oldParent = node.parentNode;
2630             // it's a move, make sure we move it cleanly
2631             if(oldParent){
2632                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2633                     return false;
2634                 }
2635                 oldParent.removeChild(node);
2636             }
2637             
2638             index = this.childNodes.length;
2639             if(index == 0){
2640                 this.setFirstChild(node);
2641             }
2642             this.childNodes.push(node);
2643             node.parentNode = this;
2644             var ps = this.childNodes[index-1];
2645             if(ps){
2646                 node.previousSibling = ps;
2647                 ps.nextSibling = node;
2648             }else{
2649                 node.previousSibling = null;
2650             }
2651             node.nextSibling = null;
2652             this.setLastChild(node);
2653             node.setOwnerTree(this.getOwnerTree());
2654             this.fireEvent("append", this.ownerTree, this, node, index);
2655             if(this.ownerTree) {
2656                 this.ownerTree.fireEvent("appendnode", this, node, index);
2657             }
2658             if(oldParent){
2659                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2660             }
2661             return node;
2662         }
2663     },
2664
2665     /**
2666      * Removes a child node from this node.
2667      * @param {Node} node The node to remove
2668      * @return {Node} The removed node
2669      */
2670     removeChild : function(node){
2671         var index = this.childNodes.indexOf(node);
2672         if(index == -1){
2673             return false;
2674         }
2675         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2676             return false;
2677         }
2678
2679         // remove it from childNodes collection
2680         this.childNodes.splice(index, 1);
2681
2682         // update siblings
2683         if(node.previousSibling){
2684             node.previousSibling.nextSibling = node.nextSibling;
2685         }
2686         if(node.nextSibling){
2687             node.nextSibling.previousSibling = node.previousSibling;
2688         }
2689
2690         // update child refs
2691         if(this.firstChild == node){
2692             this.setFirstChild(node.nextSibling);
2693         }
2694         if(this.lastChild == node){
2695             this.setLastChild(node.previousSibling);
2696         }
2697
2698         node.setOwnerTree(null);
2699         // clear any references from the node
2700         node.parentNode = null;
2701         node.previousSibling = null;
2702         node.nextSibling = null;
2703         this.fireEvent("remove", this.ownerTree, this, node);
2704         return node;
2705     },
2706
2707     /**
2708      * Inserts the first node before the second node in this nodes childNodes collection.
2709      * @param {Node} node The node to insert
2710      * @param {Node} refNode The node to insert before (if null the node is appended)
2711      * @return {Node} The inserted node
2712      */
2713     insertBefore : function(node, refNode){
2714         if(!refNode){ // like standard Dom, refNode can be null for append
2715             return this.appendChild(node);
2716         }
2717         // nothing to do
2718         if(node == refNode){
2719             return false;
2720         }
2721
2722         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2723             return false;
2724         }
2725         var index = this.childNodes.indexOf(refNode);
2726         var oldParent = node.parentNode;
2727         var refIndex = index;
2728
2729         // when moving internally, indexes will change after remove
2730         if(oldParent == this && this.childNodes.indexOf(node) < index){
2731             refIndex--;
2732         }
2733
2734         // it's a move, make sure we move it cleanly
2735         if(oldParent){
2736             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2737                 return false;
2738             }
2739             oldParent.removeChild(node);
2740         }
2741         if(refIndex == 0){
2742             this.setFirstChild(node);
2743         }
2744         this.childNodes.splice(refIndex, 0, node);
2745         node.parentNode = this;
2746         var ps = this.childNodes[refIndex-1];
2747         if(ps){
2748             node.previousSibling = ps;
2749             ps.nextSibling = node;
2750         }else{
2751             node.previousSibling = null;
2752         }
2753         node.nextSibling = refNode;
2754         refNode.previousSibling = node;
2755         node.setOwnerTree(this.getOwnerTree());
2756         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2757         if(oldParent){
2758             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2759         }
2760         return node;
2761     },
2762
2763     /**
2764      * Returns the child node at the specified index.
2765      * @param {Number} index
2766      * @return {Node}
2767      */
2768     item : function(index){
2769         return this.childNodes[index];
2770     },
2771
2772     /**
2773      * Replaces one child node in this node with another.
2774      * @param {Node} newChild The replacement node
2775      * @param {Node} oldChild The node to replace
2776      * @return {Node} The replaced node
2777      */
2778     replaceChild : function(newChild, oldChild){
2779         this.insertBefore(newChild, oldChild);
2780         this.removeChild(oldChild);
2781         return oldChild;
2782     },
2783
2784     /**
2785      * Returns the index of a child node
2786      * @param {Node} node
2787      * @return {Number} The index of the node or -1 if it was not found
2788      */
2789     indexOf : function(child){
2790         return this.childNodes.indexOf(child);
2791     },
2792
2793     /**
2794      * Returns the tree this node is in.
2795      * @return {Tree}
2796      */
2797     getOwnerTree : function(){
2798         // if it doesn't have one, look for one
2799         if(!this.ownerTree){
2800             var p = this;
2801             while(p){
2802                 if(p.ownerTree){
2803                     this.ownerTree = p.ownerTree;
2804                     break;
2805                 }
2806                 p = p.parentNode;
2807             }
2808         }
2809         return this.ownerTree;
2810     },
2811
2812     /**
2813      * Returns depth of this node (the root node has a depth of 0)
2814      * @return {Number}
2815      */
2816     getDepth : function(){
2817         var depth = 0;
2818         var p = this;
2819         while(p.parentNode){
2820             ++depth;
2821             p = p.parentNode;
2822         }
2823         return depth;
2824     },
2825
2826     // private
2827     setOwnerTree : function(tree){
2828         // if it's move, we need to update everyone
2829         if(tree != this.ownerTree){
2830             if(this.ownerTree){
2831                 this.ownerTree.unregisterNode(this);
2832             }
2833             this.ownerTree = tree;
2834             var cs = this.childNodes;
2835             for(var i = 0, len = cs.length; i < len; i++) {
2836                 cs[i].setOwnerTree(tree);
2837             }
2838             if(tree){
2839                 tree.registerNode(this);
2840             }
2841         }
2842     },
2843
2844     /**
2845      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2846      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2847      * @return {String} The path
2848      */
2849     getPath : function(attr){
2850         attr = attr || "id";
2851         var p = this.parentNode;
2852         var b = [this.attributes[attr]];
2853         while(p){
2854             b.unshift(p.attributes[attr]);
2855             p = p.parentNode;
2856         }
2857         var sep = this.getOwnerTree().pathSeparator;
2858         return sep + b.join(sep);
2859     },
2860
2861     /**
2862      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2863      * function call will be the scope provided or the current node. The arguments to the function
2864      * will be the args provided or the current node. If the function returns false at any point,
2865      * the bubble is stopped.
2866      * @param {Function} fn The function to call
2867      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2868      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2869      */
2870     bubble : function(fn, scope, args){
2871         var p = this;
2872         while(p){
2873             if(fn.call(scope || p, args || p) === false){
2874                 break;
2875             }
2876             p = p.parentNode;
2877         }
2878     },
2879
2880     /**
2881      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2882      * function call will be the scope provided or the current node. The arguments to the function
2883      * will be the args provided or the current node. If the function returns false at any point,
2884      * the cascade is stopped on that branch.
2885      * @param {Function} fn The function to call
2886      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2887      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2888      */
2889     cascade : function(fn, scope, args){
2890         if(fn.call(scope || this, args || this) !== false){
2891             var cs = this.childNodes;
2892             for(var i = 0, len = cs.length; i < len; i++) {
2893                 cs[i].cascade(fn, scope, args);
2894             }
2895         }
2896     },
2897
2898     /**
2899      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2900      * function call will be the scope provided or the current node. The arguments to the function
2901      * will be the args provided or the current node. If the function returns false at any point,
2902      * the iteration stops.
2903      * @param {Function} fn The function to call
2904      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2905      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2906      */
2907     eachChild : function(fn, scope, args){
2908         var cs = this.childNodes;
2909         for(var i = 0, len = cs.length; i < len; i++) {
2910                 if(fn.call(scope || this, args || cs[i]) === false){
2911                     break;
2912                 }
2913         }
2914     },
2915
2916     /**
2917      * Finds the first child that has the attribute with the specified value.
2918      * @param {String} attribute The attribute name
2919      * @param {Mixed} value The value to search for
2920      * @return {Node} The found child or null if none was found
2921      */
2922     findChild : function(attribute, value){
2923         var cs = this.childNodes;
2924         for(var i = 0, len = cs.length; i < len; i++) {
2925                 if(cs[i].attributes[attribute] == value){
2926                     return cs[i];
2927                 }
2928         }
2929         return null;
2930     },
2931
2932     /**
2933      * Finds the first child by a custom function. The child matches if the function passed
2934      * returns true.
2935      * @param {Function} fn
2936      * @param {Object} scope (optional)
2937      * @return {Node} The found child or null if none was found
2938      */
2939     findChildBy : function(fn, scope){
2940         var cs = this.childNodes;
2941         for(var i = 0, len = cs.length; i < len; i++) {
2942                 if(fn.call(scope||cs[i], cs[i]) === true){
2943                     return cs[i];
2944                 }
2945         }
2946         return null;
2947     },
2948
2949     /**
2950      * Sorts this nodes children using the supplied sort function
2951      * @param {Function} fn
2952      * @param {Object} scope (optional)
2953      */
2954     sort : function(fn, scope){
2955         var cs = this.childNodes;
2956         var len = cs.length;
2957         if(len > 0){
2958             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2959             cs.sort(sortFn);
2960             for(var i = 0; i < len; i++){
2961                 var n = cs[i];
2962                 n.previousSibling = cs[i-1];
2963                 n.nextSibling = cs[i+1];
2964                 if(i == 0){
2965                     this.setFirstChild(n);
2966                 }
2967                 if(i == len-1){
2968                     this.setLastChild(n);
2969                 }
2970             }
2971         }
2972     },
2973
2974     /**
2975      * Returns true if this node is an ancestor (at any point) of the passed node.
2976      * @param {Node} node
2977      * @return {Boolean}
2978      */
2979     contains : function(node){
2980         return node.isAncestor(this);
2981     },
2982
2983     /**
2984      * Returns true if the passed node is an ancestor (at any point) of this node.
2985      * @param {Node} node
2986      * @return {Boolean}
2987      */
2988     isAncestor : function(node){
2989         var p = this.parentNode;
2990         while(p){
2991             if(p == node){
2992                 return true;
2993             }
2994             p = p.parentNode;
2995         }
2996         return false;
2997     },
2998
2999     toString : function(){
3000         return "[Node"+(this.id?" "+this.id:"")+"]";
3001     }
3002 });/*
3003  * Based on:
3004  * Ext JS Library 1.1.1
3005  * Copyright(c) 2006-2007, Ext JS, LLC.
3006  *
3007  * Originally Released Under LGPL - original licence link has changed is not relivant.
3008  *
3009  * Fork - LGPL
3010  * <script type="text/javascript">
3011  */
3012
3013
3014 /**
3015  * @class Roo.Shadow
3016  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3017  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3018  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3019  * @constructor
3020  * Create a new Shadow
3021  * @param {Object} config The config object
3022  */
3023 Roo.Shadow = function(config){
3024     Roo.apply(this, config);
3025     if(typeof this.mode != "string"){
3026         this.mode = this.defaultMode;
3027     }
3028     var o = this.offset, a = {h: 0};
3029     var rad = Math.floor(this.offset/2);
3030     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3031         case "drop":
3032             a.w = 0;
3033             a.l = a.t = o;
3034             a.t -= 1;
3035             if(Roo.isIE){
3036                 a.l -= this.offset + rad;
3037                 a.t -= this.offset + rad;
3038                 a.w -= rad;
3039                 a.h -= rad;
3040                 a.t += 1;
3041             }
3042         break;
3043         case "sides":
3044             a.w = (o*2);
3045             a.l = -o;
3046             a.t = o-1;
3047             if(Roo.isIE){
3048                 a.l -= (this.offset - rad);
3049                 a.t -= this.offset + rad;
3050                 a.l += 1;
3051                 a.w -= (this.offset - rad)*2;
3052                 a.w -= rad + 1;
3053                 a.h -= 1;
3054             }
3055         break;
3056         case "frame":
3057             a.w = a.h = (o*2);
3058             a.l = a.t = -o;
3059             a.t += 1;
3060             a.h -= 2;
3061             if(Roo.isIE){
3062                 a.l -= (this.offset - rad);
3063                 a.t -= (this.offset - rad);
3064                 a.l += 1;
3065                 a.w -= (this.offset + rad + 1);
3066                 a.h -= (this.offset + rad);
3067                 a.h += 1;
3068             }
3069         break;
3070     };
3071
3072     this.adjusts = a;
3073 };
3074
3075 Roo.Shadow.prototype = {
3076     /**
3077      * @cfg {String} mode
3078      * The shadow display mode.  Supports the following options:<br />
3079      * sides: Shadow displays on both sides and bottom only<br />
3080      * frame: Shadow displays equally on all four sides<br />
3081      * drop: Traditional bottom-right drop shadow (default)
3082      */
3083     mode: false,
3084     /**
3085      * @cfg {String} offset
3086      * The number of pixels to offset the shadow from the element (defaults to 4)
3087      */
3088     offset: 4,
3089
3090     // private
3091     defaultMode: "drop",
3092
3093     /**
3094      * Displays the shadow under the target element
3095      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3096      */
3097     show : function(target){
3098         target = Roo.get(target);
3099         if(!this.el){
3100             this.el = Roo.Shadow.Pool.pull();
3101             if(this.el.dom.nextSibling != target.dom){
3102                 this.el.insertBefore(target);
3103             }
3104         }
3105         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3106         if(Roo.isIE){
3107             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3108         }
3109         this.realign(
3110             target.getLeft(true),
3111             target.getTop(true),
3112             target.getWidth(),
3113             target.getHeight()
3114         );
3115         this.el.dom.style.display = "block";
3116     },
3117
3118     /**
3119      * Returns true if the shadow is visible, else false
3120      */
3121     isVisible : function(){
3122         return this.el ? true : false;  
3123     },
3124
3125     /**
3126      * Direct alignment when values are already available. Show must be called at least once before
3127      * calling this method to ensure it is initialized.
3128      * @param {Number} left The target element left position
3129      * @param {Number} top The target element top position
3130      * @param {Number} width The target element width
3131      * @param {Number} height The target element height
3132      */
3133     realign : function(l, t, w, h){
3134         if(!this.el){
3135             return;
3136         }
3137         var a = this.adjusts, d = this.el.dom, s = d.style;
3138         var iea = 0;
3139         s.left = (l+a.l)+"px";
3140         s.top = (t+a.t)+"px";
3141         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3142  
3143         if(s.width != sws || s.height != shs){
3144             s.width = sws;
3145             s.height = shs;
3146             if(!Roo.isIE){
3147                 var cn = d.childNodes;
3148                 var sww = Math.max(0, (sw-12))+"px";
3149                 cn[0].childNodes[1].style.width = sww;
3150                 cn[1].childNodes[1].style.width = sww;
3151                 cn[2].childNodes[1].style.width = sww;
3152                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3153             }
3154         }
3155     },
3156
3157     /**
3158      * Hides this shadow
3159      */
3160     hide : function(){
3161         if(this.el){
3162             this.el.dom.style.display = "none";
3163             Roo.Shadow.Pool.push(this.el);
3164             delete this.el;
3165         }
3166     },
3167
3168     /**
3169      * Adjust the z-index of this shadow
3170      * @param {Number} zindex The new z-index
3171      */
3172     setZIndex : function(z){
3173         this.zIndex = z;
3174         if(this.el){
3175             this.el.setStyle("z-index", z);
3176         }
3177     }
3178 };
3179
3180 // Private utility class that manages the internal Shadow cache
3181 Roo.Shadow.Pool = function(){
3182     var p = [];
3183     var markup = Roo.isIE ?
3184                  '<div class="x-ie-shadow"></div>' :
3185                  '<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>';
3186     return {
3187         pull : function(){
3188             var sh = p.shift();
3189             if(!sh){
3190                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3191                 sh.autoBoxAdjust = false;
3192             }
3193             return sh;
3194         },
3195
3196         push : function(sh){
3197             p.push(sh);
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210
3211
3212 /**
3213  * @class Roo.SplitBar
3214  * @extends Roo.util.Observable
3215  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3216  * <br><br>
3217  * Usage:
3218  * <pre><code>
3219 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3220                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3221 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3222 split.minSize = 100;
3223 split.maxSize = 600;
3224 split.animate = true;
3225 split.on('moved', splitterMoved);
3226 </code></pre>
3227  * @constructor
3228  * Create a new SplitBar
3229  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3230  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3231  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3232  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3233                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3234                         position of the SplitBar).
3235  */
3236 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3237     
3238     /** @private */
3239     this.el = Roo.get(dragElement, true);
3240     this.el.dom.unselectable = "on";
3241     /** @private */
3242     this.resizingEl = Roo.get(resizingElement, true);
3243
3244     /**
3245      * @private
3246      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3247      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3248      * @type Number
3249      */
3250     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3251     
3252     /**
3253      * The minimum size of the resizing element. (Defaults to 0)
3254      * @type Number
3255      */
3256     this.minSize = 0;
3257     
3258     /**
3259      * The maximum size of the resizing element. (Defaults to 2000)
3260      * @type Number
3261      */
3262     this.maxSize = 2000;
3263     
3264     /**
3265      * Whether to animate the transition to the new size
3266      * @type Boolean
3267      */
3268     this.animate = false;
3269     
3270     /**
3271      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3272      * @type Boolean
3273      */
3274     this.useShim = false;
3275     
3276     /** @private */
3277     this.shim = null;
3278     
3279     if(!existingProxy){
3280         /** @private */
3281         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3282     }else{
3283         this.proxy = Roo.get(existingProxy).dom;
3284     }
3285     /** @private */
3286     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3287     
3288     /** @private */
3289     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3290     
3291     /** @private */
3292     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3293     
3294     /** @private */
3295     this.dragSpecs = {};
3296     
3297     /**
3298      * @private The adapter to use to positon and resize elements
3299      */
3300     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3301     this.adapter.init(this);
3302     
3303     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3304         /** @private */
3305         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3306         this.el.addClass("x-splitbar-h");
3307     }else{
3308         /** @private */
3309         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3310         this.el.addClass("x-splitbar-v");
3311     }
3312     
3313     this.addEvents({
3314         /**
3315          * @event resize
3316          * Fires when the splitter is moved (alias for {@link #event-moved})
3317          * @param {Roo.SplitBar} this
3318          * @param {Number} newSize the new width or height
3319          */
3320         "resize" : true,
3321         /**
3322          * @event moved
3323          * Fires when the splitter is moved
3324          * @param {Roo.SplitBar} this
3325          * @param {Number} newSize the new width or height
3326          */
3327         "moved" : true,
3328         /**
3329          * @event beforeresize
3330          * Fires before the splitter is dragged
3331          * @param {Roo.SplitBar} this
3332          */
3333         "beforeresize" : true,
3334
3335         "beforeapply" : true
3336     });
3337
3338     Roo.util.Observable.call(this);
3339 };
3340
3341 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3342     onStartProxyDrag : function(x, y){
3343         this.fireEvent("beforeresize", this);
3344         if(!this.overlay){
3345             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3346             o.unselectable();
3347             o.enableDisplayMode("block");
3348             // all splitbars share the same overlay
3349             Roo.SplitBar.prototype.overlay = o;
3350         }
3351         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3352         this.overlay.show();
3353         Roo.get(this.proxy).setDisplayed("block");
3354         var size = this.adapter.getElementSize(this);
3355         this.activeMinSize = this.getMinimumSize();;
3356         this.activeMaxSize = this.getMaximumSize();;
3357         var c1 = size - this.activeMinSize;
3358         var c2 = Math.max(this.activeMaxSize - size, 0);
3359         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3360             this.dd.resetConstraints();
3361             this.dd.setXConstraint(
3362                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3363                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3364             );
3365             this.dd.setYConstraint(0, 0);
3366         }else{
3367             this.dd.resetConstraints();
3368             this.dd.setXConstraint(0, 0);
3369             this.dd.setYConstraint(
3370                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3371                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3372             );
3373          }
3374         this.dragSpecs.startSize = size;
3375         this.dragSpecs.startPoint = [x, y];
3376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3377     },
3378     
3379     /** 
3380      * @private Called after the drag operation by the DDProxy
3381      */
3382     onEndProxyDrag : function(e){
3383         Roo.get(this.proxy).setDisplayed(false);
3384         var endPoint = Roo.lib.Event.getXY(e);
3385         if(this.overlay){
3386             this.overlay.hide();
3387         }
3388         var newSize;
3389         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3390             newSize = this.dragSpecs.startSize + 
3391                 (this.placement == Roo.SplitBar.LEFT ?
3392                     endPoint[0] - this.dragSpecs.startPoint[0] :
3393                     this.dragSpecs.startPoint[0] - endPoint[0]
3394                 );
3395         }else{
3396             newSize = this.dragSpecs.startSize + 
3397                 (this.placement == Roo.SplitBar.TOP ?
3398                     endPoint[1] - this.dragSpecs.startPoint[1] :
3399                     this.dragSpecs.startPoint[1] - endPoint[1]
3400                 );
3401         }
3402         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3403         if(newSize != this.dragSpecs.startSize){
3404             if(this.fireEvent('beforeapply', this, newSize) !== false){
3405                 this.adapter.setElementSize(this, newSize);
3406                 this.fireEvent("moved", this, newSize);
3407                 this.fireEvent("resize", this, newSize);
3408             }
3409         }
3410     },
3411     
3412     /**
3413      * Get the adapter this SplitBar uses
3414      * @return The adapter object
3415      */
3416     getAdapter : function(){
3417         return this.adapter;
3418     },
3419     
3420     /**
3421      * Set the adapter this SplitBar uses
3422      * @param {Object} adapter A SplitBar adapter object
3423      */
3424     setAdapter : function(adapter){
3425         this.adapter = adapter;
3426         this.adapter.init(this);
3427     },
3428     
3429     /**
3430      * Gets the minimum size for the resizing element
3431      * @return {Number} The minimum size
3432      */
3433     getMinimumSize : function(){
3434         return this.minSize;
3435     },
3436     
3437     /**
3438      * Sets the minimum size for the resizing element
3439      * @param {Number} minSize The minimum size
3440      */
3441     setMinimumSize : function(minSize){
3442         this.minSize = minSize;
3443     },
3444     
3445     /**
3446      * Gets the maximum size for the resizing element
3447      * @return {Number} The maximum size
3448      */
3449     getMaximumSize : function(){
3450         return this.maxSize;
3451     },
3452     
3453     /**
3454      * Sets the maximum size for the resizing element
3455      * @param {Number} maxSize The maximum size
3456      */
3457     setMaximumSize : function(maxSize){
3458         this.maxSize = maxSize;
3459     },
3460     
3461     /**
3462      * Sets the initialize size for the resizing element
3463      * @param {Number} size The initial size
3464      */
3465     setCurrentSize : function(size){
3466         var oldAnimate = this.animate;
3467         this.animate = false;
3468         this.adapter.setElementSize(this, size);
3469         this.animate = oldAnimate;
3470     },
3471     
3472     /**
3473      * Destroy this splitbar. 
3474      * @param {Boolean} removeEl True to remove the element
3475      */
3476     destroy : function(removeEl){
3477         if(this.shim){
3478             this.shim.remove();
3479         }
3480         this.dd.unreg();
3481         this.proxy.parentNode.removeChild(this.proxy);
3482         if(removeEl){
3483             this.el.remove();
3484         }
3485     }
3486 });
3487
3488 /**
3489  * @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.
3490  */
3491 Roo.SplitBar.createProxy = function(dir){
3492     var proxy = new Roo.Element(document.createElement("div"));
3493     proxy.unselectable();
3494     var cls = 'x-splitbar-proxy';
3495     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3496     document.body.appendChild(proxy.dom);
3497     return proxy.dom;
3498 };
3499
3500 /** 
3501  * @class Roo.SplitBar.BasicLayoutAdapter
3502  * Default Adapter. It assumes the splitter and resizing element are not positioned
3503  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3504  */
3505 Roo.SplitBar.BasicLayoutAdapter = function(){
3506 };
3507
3508 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3509     // do nothing for now
3510     init : function(s){
3511     
3512     },
3513     /**
3514      * Called before drag operations to get the current size of the resizing element. 
3515      * @param {Roo.SplitBar} s The SplitBar using this adapter
3516      */
3517      getElementSize : function(s){
3518         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3519             return s.resizingEl.getWidth();
3520         }else{
3521             return s.resizingEl.getHeight();
3522         }
3523     },
3524     
3525     /**
3526      * Called after drag operations to set the size of the resizing element.
3527      * @param {Roo.SplitBar} s The SplitBar using this adapter
3528      * @param {Number} newSize The new size to set
3529      * @param {Function} onComplete A function to be invoked when resizing is complete
3530      */
3531     setElementSize : function(s, newSize, onComplete){
3532         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3533             if(!s.animate){
3534                 s.resizingEl.setWidth(newSize);
3535                 if(onComplete){
3536                     onComplete(s, newSize);
3537                 }
3538             }else{
3539                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3540             }
3541         }else{
3542             
3543             if(!s.animate){
3544                 s.resizingEl.setHeight(newSize);
3545                 if(onComplete){
3546                     onComplete(s, newSize);
3547                 }
3548             }else{
3549                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3550             }
3551         }
3552     }
3553 };
3554
3555 /** 
3556  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3557  * @extends Roo.SplitBar.BasicLayoutAdapter
3558  * Adapter that  moves the splitter element to align with the resized sizing element. 
3559  * Used with an absolute positioned SplitBar.
3560  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3561  * document.body, make sure you assign an id to the body element.
3562  */
3563 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3564     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3565     this.container = Roo.get(container);
3566 };
3567
3568 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3569     init : function(s){
3570         this.basic.init(s);
3571     },
3572     
3573     getElementSize : function(s){
3574         return this.basic.getElementSize(s);
3575     },
3576     
3577     setElementSize : function(s, newSize, onComplete){
3578         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3579     },
3580     
3581     moveSplitter : function(s){
3582         var yes = Roo.SplitBar;
3583         switch(s.placement){
3584             case yes.LEFT:
3585                 s.el.setX(s.resizingEl.getRight());
3586                 break;
3587             case yes.RIGHT:
3588                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3589                 break;
3590             case yes.TOP:
3591                 s.el.setY(s.resizingEl.getBottom());
3592                 break;
3593             case yes.BOTTOM:
3594                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3595                 break;
3596         }
3597     }
3598 };
3599
3600 /**
3601  * Orientation constant - Create a vertical SplitBar
3602  * @static
3603  * @type Number
3604  */
3605 Roo.SplitBar.VERTICAL = 1;
3606
3607 /**
3608  * Orientation constant - Create a horizontal SplitBar
3609  * @static
3610  * @type Number
3611  */
3612 Roo.SplitBar.HORIZONTAL = 2;
3613
3614 /**
3615  * Placement constant - The resizing element is to the left of the splitter element
3616  * @static
3617  * @type Number
3618  */
3619 Roo.SplitBar.LEFT = 1;
3620
3621 /**
3622  * Placement constant - The resizing element is to the right of the splitter element
3623  * @static
3624  * @type Number
3625  */
3626 Roo.SplitBar.RIGHT = 2;
3627
3628 /**
3629  * Placement constant - The resizing element is positioned above the splitter element
3630  * @static
3631  * @type Number
3632  */
3633 Roo.SplitBar.TOP = 3;
3634
3635 /**
3636  * Placement constant - The resizing element is positioned under splitter element
3637  * @static
3638  * @type Number
3639  */
3640 Roo.SplitBar.BOTTOM = 4;
3641 /*
3642  * Based on:
3643  * Ext JS Library 1.1.1
3644  * Copyright(c) 2006-2007, Ext JS, LLC.
3645  *
3646  * Originally Released Under LGPL - original licence link has changed is not relivant.
3647  *
3648  * Fork - LGPL
3649  * <script type="text/javascript">
3650  */
3651
3652 /**
3653  * @class Roo.View
3654  * @extends Roo.util.Observable
3655  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3656  * This class also supports single and multi selection modes. <br>
3657  * Create a data model bound view:
3658  <pre><code>
3659  var store = new Roo.data.Store(...);
3660
3661  var view = new Roo.View({
3662     el : "my-element",
3663     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3664  
3665     singleSelect: true,
3666     selectedClass: "ydataview-selected",
3667     store: store
3668  });
3669
3670  // listen for node click?
3671  view.on("click", function(vw, index, node, e){
3672  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3673  });
3674
3675  // load XML data
3676  dataModel.load("foobar.xml");
3677  </code></pre>
3678  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3679  * <br><br>
3680  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3681  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3682  * 
3683  * Note: old style constructor is still suported (container, template, config)
3684  * 
3685  * @constructor
3686  * Create a new View
3687  * @param {Object} config The config object
3688  * 
3689  */
3690 Roo.View = function(config, depreciated_tpl, depreciated_config){
3691     
3692     this.parent = false;
3693     
3694     if (typeof(depreciated_tpl) == 'undefined') {
3695         // new way.. - universal constructor.
3696         Roo.apply(this, config);
3697         this.el  = Roo.get(this.el);
3698     } else {
3699         // old format..
3700         this.el  = Roo.get(config);
3701         this.tpl = depreciated_tpl;
3702         Roo.apply(this, depreciated_config);
3703     }
3704     this.wrapEl  = this.el.wrap().wrap();
3705     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3706     
3707     
3708     if(typeof(this.tpl) == "string"){
3709         this.tpl = new Roo.Template(this.tpl);
3710     } else {
3711         // support xtype ctors..
3712         this.tpl = new Roo.factory(this.tpl, Roo);
3713     }
3714     
3715     
3716     this.tpl.compile();
3717     
3718     /** @private */
3719     this.addEvents({
3720         /**
3721          * @event beforeclick
3722          * Fires before a click is processed. Returns false to cancel the default action.
3723          * @param {Roo.View} this
3724          * @param {Number} index The index of the target node
3725          * @param {HTMLElement} node The target node
3726          * @param {Roo.EventObject} e The raw event object
3727          */
3728             "beforeclick" : true,
3729         /**
3730          * @event click
3731          * Fires when a template node is clicked.
3732          * @param {Roo.View} this
3733          * @param {Number} index The index of the target node
3734          * @param {HTMLElement} node The target node
3735          * @param {Roo.EventObject} e The raw event object
3736          */
3737             "click" : true,
3738         /**
3739          * @event dblclick
3740          * Fires when a template node is double clicked.
3741          * @param {Roo.View} this
3742          * @param {Number} index The index of the target node
3743          * @param {HTMLElement} node The target node
3744          * @param {Roo.EventObject} e The raw event object
3745          */
3746             "dblclick" : true,
3747         /**
3748          * @event contextmenu
3749          * Fires when a template node is right clicked.
3750          * @param {Roo.View} this
3751          * @param {Number} index The index of the target node
3752          * @param {HTMLElement} node The target node
3753          * @param {Roo.EventObject} e The raw event object
3754          */
3755             "contextmenu" : true,
3756         /**
3757          * @event selectionchange
3758          * Fires when the selected nodes change.
3759          * @param {Roo.View} this
3760          * @param {Array} selections Array of the selected nodes
3761          */
3762             "selectionchange" : true,
3763     
3764         /**
3765          * @event beforeselect
3766          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3767          * @param {Roo.View} this
3768          * @param {HTMLElement} node The node to be selected
3769          * @param {Array} selections Array of currently selected nodes
3770          */
3771             "beforeselect" : true,
3772         /**
3773          * @event preparedata
3774          * Fires on every row to render, to allow you to change the data.
3775          * @param {Roo.View} this
3776          * @param {Object} data to be rendered (change this)
3777          */
3778           "preparedata" : true
3779           
3780           
3781         });
3782
3783
3784
3785     this.el.on({
3786         "click": this.onClick,
3787         "dblclick": this.onDblClick,
3788         "contextmenu": this.onContextMenu,
3789         scope:this
3790     });
3791
3792     this.selections = [];
3793     this.nodes = [];
3794     this.cmp = new Roo.CompositeElementLite([]);
3795     if(this.store){
3796         this.store = Roo.factory(this.store, Roo.data);
3797         this.setStore(this.store, true);
3798     }
3799     
3800     if ( this.footer && this.footer.xtype) {
3801            
3802          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3803         
3804         this.footer.dataSource = this.store;
3805         this.footer.container = fctr;
3806         this.footer = Roo.factory(this.footer, Roo);
3807         fctr.insertFirst(this.el);
3808         
3809         // this is a bit insane - as the paging toolbar seems to detach the el..
3810 //        dom.parentNode.parentNode.parentNode
3811          // they get detached?
3812     }
3813     
3814     
3815     Roo.View.superclass.constructor.call(this);
3816     
3817     
3818 };
3819
3820 Roo.extend(Roo.View, Roo.util.Observable, {
3821     
3822      /**
3823      * @cfg {Roo.data.Store} store Data store to load data from.
3824      */
3825     store : false,
3826     
3827     /**
3828      * @cfg {String|Roo.Element} el The container element.
3829      */
3830     el : '',
3831     
3832     /**
3833      * @cfg {String|Roo.Template} tpl The template used by this View 
3834      */
3835     tpl : false,
3836     /**
3837      * @cfg {String} dataName the named area of the template to use as the data area
3838      *                          Works with domtemplates roo-name="name"
3839      */
3840     dataName: false,
3841     /**
3842      * @cfg {String} selectedClass The css class to add to selected nodes
3843      */
3844     selectedClass : "x-view-selected",
3845      /**
3846      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3847      */
3848     emptyText : "",
3849     
3850     /**
3851      * @cfg {String} text to display on mask (default Loading)
3852      */
3853     mask : false,
3854     /**
3855      * @cfg {Boolean} multiSelect Allow multiple selection
3856      */
3857     multiSelect : false,
3858     /**
3859      * @cfg {Boolean} singleSelect Allow single selection
3860      */
3861     singleSelect:  false,
3862     
3863     /**
3864      * @cfg {Boolean} toggleSelect - selecting 
3865      */
3866     toggleSelect : false,
3867     
3868     /**
3869      * @cfg {Boolean} tickable - selecting 
3870      */
3871     tickable : false,
3872     
3873     /**
3874      * Returns the element this view is bound to.
3875      * @return {Roo.Element}
3876      */
3877     getEl : function(){
3878         return this.wrapEl;
3879     },
3880     
3881     
3882
3883     /**
3884      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3885      */
3886     refresh : function(){
3887         //Roo.log('refresh');
3888         var t = this.tpl;
3889         
3890         // if we are using something like 'domtemplate', then
3891         // the what gets used is:
3892         // t.applySubtemplate(NAME, data, wrapping data..)
3893         // the outer template then get' applied with
3894         //     the store 'extra data'
3895         // and the body get's added to the
3896         //      roo-name="data" node?
3897         //      <span class='roo-tpl-{name}'></span> ?????
3898         
3899         
3900         
3901         this.clearSelections();
3902         this.el.update("");
3903         var html = [];
3904         var records = this.store.getRange();
3905         if(records.length < 1) {
3906             
3907             // is this valid??  = should it render a template??
3908             
3909             this.el.update(this.emptyText);
3910             return;
3911         }
3912         var el = this.el;
3913         if (this.dataName) {
3914             this.el.update(t.apply(this.store.meta)); //????
3915             el = this.el.child('.roo-tpl-' + this.dataName);
3916         }
3917         
3918         for(var i = 0, len = records.length; i < len; i++){
3919             var data = this.prepareData(records[i].data, i, records[i]);
3920             this.fireEvent("preparedata", this, data, i, records[i]);
3921             
3922             var d = Roo.apply({}, data);
3923             
3924             if(this.tickable){
3925                 Roo.apply(d, {'roo-id' : Roo.id()});
3926                 
3927                 var _this = this;
3928             
3929                 Roo.each(this.parent.item, function(item){
3930                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3931                         return;
3932                     }
3933                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3934                 });
3935             }
3936             
3937             html[html.length] = Roo.util.Format.trim(
3938                 this.dataName ?
3939                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3940                     t.apply(d)
3941             );
3942         }
3943         
3944         
3945         
3946         el.update(html.join(""));
3947         this.nodes = el.dom.childNodes;
3948         this.updateIndexes(0);
3949     },
3950     
3951
3952     /**
3953      * Function to override to reformat the data that is sent to
3954      * the template for each node.
3955      * DEPRICATED - use the preparedata event handler.
3956      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3957      * a JSON object for an UpdateManager bound view).
3958      */
3959     prepareData : function(data, index, record)
3960     {
3961         this.fireEvent("preparedata", this, data, index, record);
3962         return data;
3963     },
3964
3965     onUpdate : function(ds, record){
3966         // Roo.log('on update');   
3967         this.clearSelections();
3968         var index = this.store.indexOf(record);
3969         var n = this.nodes[index];
3970         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3971         n.parentNode.removeChild(n);
3972         this.updateIndexes(index, index);
3973     },
3974
3975     
3976     
3977 // --------- FIXME     
3978     onAdd : function(ds, records, index)
3979     {
3980         //Roo.log(['on Add', ds, records, index] );        
3981         this.clearSelections();
3982         if(this.nodes.length == 0){
3983             this.refresh();
3984             return;
3985         }
3986         var n = this.nodes[index];
3987         for(var i = 0, len = records.length; i < len; i++){
3988             var d = this.prepareData(records[i].data, i, records[i]);
3989             if(n){
3990                 this.tpl.insertBefore(n, d);
3991             }else{
3992                 
3993                 this.tpl.append(this.el, d);
3994             }
3995         }
3996         this.updateIndexes(index);
3997     },
3998
3999     onRemove : function(ds, record, index){
4000        // Roo.log('onRemove');
4001         this.clearSelections();
4002         var el = this.dataName  ?
4003             this.el.child('.roo-tpl-' + this.dataName) :
4004             this.el; 
4005         
4006         el.dom.removeChild(this.nodes[index]);
4007         this.updateIndexes(index);
4008     },
4009
4010     /**
4011      * Refresh an individual node.
4012      * @param {Number} index
4013      */
4014     refreshNode : function(index){
4015         this.onUpdate(this.store, this.store.getAt(index));
4016     },
4017
4018     updateIndexes : function(startIndex, endIndex){
4019         var ns = this.nodes;
4020         startIndex = startIndex || 0;
4021         endIndex = endIndex || ns.length - 1;
4022         for(var i = startIndex; i <= endIndex; i++){
4023             ns[i].nodeIndex = i;
4024         }
4025     },
4026
4027     /**
4028      * Changes the data store this view uses and refresh the view.
4029      * @param {Store} store
4030      */
4031     setStore : function(store, initial){
4032         if(!initial && this.store){
4033             this.store.un("datachanged", this.refresh);
4034             this.store.un("add", this.onAdd);
4035             this.store.un("remove", this.onRemove);
4036             this.store.un("update", this.onUpdate);
4037             this.store.un("clear", this.refresh);
4038             this.store.un("beforeload", this.onBeforeLoad);
4039             this.store.un("load", this.onLoad);
4040             this.store.un("loadexception", this.onLoad);
4041         }
4042         if(store){
4043           
4044             store.on("datachanged", this.refresh, this);
4045             store.on("add", this.onAdd, this);
4046             store.on("remove", this.onRemove, this);
4047             store.on("update", this.onUpdate, this);
4048             store.on("clear", this.refresh, this);
4049             store.on("beforeload", this.onBeforeLoad, this);
4050             store.on("load", this.onLoad, this);
4051             store.on("loadexception", this.onLoad, this);
4052         }
4053         
4054         if(store){
4055             this.refresh();
4056         }
4057     },
4058     /**
4059      * onbeforeLoad - masks the loading area.
4060      *
4061      */
4062     onBeforeLoad : function(store,opts)
4063     {
4064          //Roo.log('onBeforeLoad');   
4065         if (!opts.add) {
4066             this.el.update("");
4067         }
4068         this.el.mask(this.mask ? this.mask : "Loading" ); 
4069     },
4070     onLoad : function ()
4071     {
4072         this.el.unmask();
4073     },
4074     
4075
4076     /**
4077      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4078      * @param {HTMLElement} node
4079      * @return {HTMLElement} The template node
4080      */
4081     findItemFromChild : function(node){
4082         var el = this.dataName  ?
4083             this.el.child('.roo-tpl-' + this.dataName,true) :
4084             this.el.dom; 
4085         
4086         if(!node || node.parentNode == el){
4087                     return node;
4088             }
4089             var p = node.parentNode;
4090             while(p && p != el){
4091             if(p.parentNode == el){
4092                 return p;
4093             }
4094             p = p.parentNode;
4095         }
4096             return null;
4097     },
4098
4099     /** @ignore */
4100     onClick : function(e){
4101         var item = this.findItemFromChild(e.getTarget());
4102         if(item){
4103             var index = this.indexOf(item);
4104             if(this.onItemClick(item, index, e) !== false){
4105                 this.fireEvent("click", this, index, item, e);
4106             }
4107         }else{
4108             this.clearSelections();
4109         }
4110     },
4111
4112     /** @ignore */
4113     onContextMenu : function(e){
4114         var item = this.findItemFromChild(e.getTarget());
4115         if(item){
4116             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4117         }
4118     },
4119
4120     /** @ignore */
4121     onDblClick : function(e){
4122         var item = this.findItemFromChild(e.getTarget());
4123         if(item){
4124             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4125         }
4126     },
4127
4128     onItemClick : function(item, index, e)
4129     {
4130         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4131             return false;
4132         }
4133         if (this.toggleSelect) {
4134             var m = this.isSelected(item) ? 'unselect' : 'select';
4135             //Roo.log(m);
4136             var _t = this;
4137             _t[m](item, true, false);
4138             return true;
4139         }
4140         if(this.multiSelect || this.singleSelect){
4141             if(this.multiSelect && e.shiftKey && this.lastSelection){
4142                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4143             }else{
4144                 this.select(item, this.multiSelect && e.ctrlKey);
4145                 this.lastSelection = item;
4146             }
4147             
4148             if(!this.tickable){
4149                 e.preventDefault();
4150             }
4151             
4152         }
4153         return true;
4154     },
4155
4156     /**
4157      * Get the number of selected nodes.
4158      * @return {Number}
4159      */
4160     getSelectionCount : function(){
4161         return this.selections.length;
4162     },
4163
4164     /**
4165      * Get the currently selected nodes.
4166      * @return {Array} An array of HTMLElements
4167      */
4168     getSelectedNodes : function(){
4169         return this.selections;
4170     },
4171
4172     /**
4173      * Get the indexes of the selected nodes.
4174      * @return {Array}
4175      */
4176     getSelectedIndexes : function(){
4177         var indexes = [], s = this.selections;
4178         for(var i = 0, len = s.length; i < len; i++){
4179             indexes.push(s[i].nodeIndex);
4180         }
4181         return indexes;
4182     },
4183
4184     /**
4185      * Clear all selections
4186      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4187      */
4188     clearSelections : function(suppressEvent){
4189         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4190             this.cmp.elements = this.selections;
4191             this.cmp.removeClass(this.selectedClass);
4192             this.selections = [];
4193             if(!suppressEvent){
4194                 this.fireEvent("selectionchange", this, this.selections);
4195             }
4196         }
4197     },
4198
4199     /**
4200      * Returns true if the passed node is selected
4201      * @param {HTMLElement/Number} node The node or node index
4202      * @return {Boolean}
4203      */
4204     isSelected : function(node){
4205         var s = this.selections;
4206         if(s.length < 1){
4207             return false;
4208         }
4209         node = this.getNode(node);
4210         return s.indexOf(node) !== -1;
4211     },
4212
4213     /**
4214      * Selects nodes.
4215      * @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
4216      * @param {Boolean} keepExisting (optional) true to keep existing selections
4217      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4218      */
4219     select : function(nodeInfo, keepExisting, suppressEvent){
4220         if(nodeInfo instanceof Array){
4221             if(!keepExisting){
4222                 this.clearSelections(true);
4223             }
4224             for(var i = 0, len = nodeInfo.length; i < len; i++){
4225                 this.select(nodeInfo[i], true, true);
4226             }
4227             return;
4228         } 
4229         var node = this.getNode(nodeInfo);
4230         if(!node || this.isSelected(node)){
4231             return; // already selected.
4232         }
4233         if(!keepExisting){
4234             this.clearSelections(true);
4235         }
4236         
4237         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4238             Roo.fly(node).addClass(this.selectedClass);
4239             this.selections.push(node);
4240             if(!suppressEvent){
4241                 this.fireEvent("selectionchange", this, this.selections);
4242             }
4243         }
4244         
4245         
4246     },
4247       /**
4248      * Unselects nodes.
4249      * @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
4250      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4251      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4252      */
4253     unselect : function(nodeInfo, keepExisting, suppressEvent)
4254     {
4255         if(nodeInfo instanceof Array){
4256             Roo.each(this.selections, function(s) {
4257                 this.unselect(s, nodeInfo);
4258             }, this);
4259             return;
4260         }
4261         var node = this.getNode(nodeInfo);
4262         if(!node || !this.isSelected(node)){
4263             //Roo.log("not selected");
4264             return; // not selected.
4265         }
4266         // fireevent???
4267         var ns = [];
4268         Roo.each(this.selections, function(s) {
4269             if (s == node ) {
4270                 Roo.fly(node).removeClass(this.selectedClass);
4271
4272                 return;
4273             }
4274             ns.push(s);
4275         },this);
4276         
4277         this.selections= ns;
4278         this.fireEvent("selectionchange", this, this.selections);
4279     },
4280
4281     /**
4282      * Gets a template node.
4283      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4284      * @return {HTMLElement} The node or null if it wasn't found
4285      */
4286     getNode : function(nodeInfo){
4287         if(typeof nodeInfo == "string"){
4288             return document.getElementById(nodeInfo);
4289         }else if(typeof nodeInfo == "number"){
4290             return this.nodes[nodeInfo];
4291         }
4292         return nodeInfo;
4293     },
4294
4295     /**
4296      * Gets a range template nodes.
4297      * @param {Number} startIndex
4298      * @param {Number} endIndex
4299      * @return {Array} An array of nodes
4300      */
4301     getNodes : function(start, end){
4302         var ns = this.nodes;
4303         start = start || 0;
4304         end = typeof end == "undefined" ? ns.length - 1 : end;
4305         var nodes = [];
4306         if(start <= end){
4307             for(var i = start; i <= end; i++){
4308                 nodes.push(ns[i]);
4309             }
4310         } else{
4311             for(var i = start; i >= end; i--){
4312                 nodes.push(ns[i]);
4313             }
4314         }
4315         return nodes;
4316     },
4317
4318     /**
4319      * Finds the index of the passed node
4320      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4321      * @return {Number} The index of the node or -1
4322      */
4323     indexOf : function(node){
4324         node = this.getNode(node);
4325         if(typeof node.nodeIndex == "number"){
4326             return node.nodeIndex;
4327         }
4328         var ns = this.nodes;
4329         for(var i = 0, len = ns.length; i < len; i++){
4330             if(ns[i] == node){
4331                 return i;
4332             }
4333         }
4334         return -1;
4335     }
4336 });
4337 /*
4338  * Based on:
4339  * Ext JS Library 1.1.1
4340  * Copyright(c) 2006-2007, Ext JS, LLC.
4341  *
4342  * Originally Released Under LGPL - original licence link has changed is not relivant.
4343  *
4344  * Fork - LGPL
4345  * <script type="text/javascript">
4346  */
4347
4348 /**
4349  * @class Roo.JsonView
4350  * @extends Roo.View
4351  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4352 <pre><code>
4353 var view = new Roo.JsonView({
4354     container: "my-element",
4355     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4356     multiSelect: true, 
4357     jsonRoot: "data" 
4358 });
4359
4360 // listen for node click?
4361 view.on("click", function(vw, index, node, e){
4362     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4363 });
4364
4365 // direct load of JSON data
4366 view.load("foobar.php");
4367
4368 // Example from my blog list
4369 var tpl = new Roo.Template(
4370     '&lt;div class="entry"&gt;' +
4371     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4372     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4373     "&lt;/div&gt;&lt;hr /&gt;"
4374 );
4375
4376 var moreView = new Roo.JsonView({
4377     container :  "entry-list", 
4378     template : tpl,
4379     jsonRoot: "posts"
4380 });
4381 moreView.on("beforerender", this.sortEntries, this);
4382 moreView.load({
4383     url: "/blog/get-posts.php",
4384     params: "allposts=true",
4385     text: "Loading Blog Entries..."
4386 });
4387 </code></pre>
4388
4389 * Note: old code is supported with arguments : (container, template, config)
4390
4391
4392  * @constructor
4393  * Create a new JsonView
4394  * 
4395  * @param {Object} config The config object
4396  * 
4397  */
4398 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4399     
4400     
4401     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4402
4403     var um = this.el.getUpdateManager();
4404     um.setRenderer(this);
4405     um.on("update", this.onLoad, this);
4406     um.on("failure", this.onLoadException, this);
4407
4408     /**
4409      * @event beforerender
4410      * Fires before rendering of the downloaded JSON data.
4411      * @param {Roo.JsonView} this
4412      * @param {Object} data The JSON data loaded
4413      */
4414     /**
4415      * @event load
4416      * Fires when data is loaded.
4417      * @param {Roo.JsonView} this
4418      * @param {Object} data The JSON data loaded
4419      * @param {Object} response The raw Connect response object
4420      */
4421     /**
4422      * @event loadexception
4423      * Fires when loading fails.
4424      * @param {Roo.JsonView} this
4425      * @param {Object} response The raw Connect response object
4426      */
4427     this.addEvents({
4428         'beforerender' : true,
4429         'load' : true,
4430         'loadexception' : true
4431     });
4432 };
4433 Roo.extend(Roo.JsonView, Roo.View, {
4434     /**
4435      * @type {String} The root property in the loaded JSON object that contains the data
4436      */
4437     jsonRoot : "",
4438
4439     /**
4440      * Refreshes the view.
4441      */
4442     refresh : function(){
4443         this.clearSelections();
4444         this.el.update("");
4445         var html = [];
4446         var o = this.jsonData;
4447         if(o && o.length > 0){
4448             for(var i = 0, len = o.length; i < len; i++){
4449                 var data = this.prepareData(o[i], i, o);
4450                 html[html.length] = this.tpl.apply(data);
4451             }
4452         }else{
4453             html.push(this.emptyText);
4454         }
4455         this.el.update(html.join(""));
4456         this.nodes = this.el.dom.childNodes;
4457         this.updateIndexes(0);
4458     },
4459
4460     /**
4461      * 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.
4462      * @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:
4463      <pre><code>
4464      view.load({
4465          url: "your-url.php",
4466          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4467          callback: yourFunction,
4468          scope: yourObject, //(optional scope)
4469          discardUrl: false,
4470          nocache: false,
4471          text: "Loading...",
4472          timeout: 30,
4473          scripts: false
4474      });
4475      </code></pre>
4476      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4477      * 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.
4478      * @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}
4479      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4480      * @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.
4481      */
4482     load : function(){
4483         var um = this.el.getUpdateManager();
4484         um.update.apply(um, arguments);
4485     },
4486
4487     // note - render is a standard framework call...
4488     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4489     render : function(el, response){
4490         
4491         this.clearSelections();
4492         this.el.update("");
4493         var o;
4494         try{
4495             if (response != '') {
4496                 o = Roo.util.JSON.decode(response.responseText);
4497                 if(this.jsonRoot){
4498                     
4499                     o = o[this.jsonRoot];
4500                 }
4501             }
4502         } catch(e){
4503         }
4504         /**
4505          * The current JSON data or null
4506          */
4507         this.jsonData = o;
4508         this.beforeRender();
4509         this.refresh();
4510     },
4511
4512 /**
4513  * Get the number of records in the current JSON dataset
4514  * @return {Number}
4515  */
4516     getCount : function(){
4517         return this.jsonData ? this.jsonData.length : 0;
4518     },
4519
4520 /**
4521  * Returns the JSON object for the specified node(s)
4522  * @param {HTMLElement/Array} node The node or an array of nodes
4523  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4524  * you get the JSON object for the node
4525  */
4526     getNodeData : function(node){
4527         if(node instanceof Array){
4528             var data = [];
4529             for(var i = 0, len = node.length; i < len; i++){
4530                 data.push(this.getNodeData(node[i]));
4531             }
4532             return data;
4533         }
4534         return this.jsonData[this.indexOf(node)] || null;
4535     },
4536
4537     beforeRender : function(){
4538         this.snapshot = this.jsonData;
4539         if(this.sortInfo){
4540             this.sort.apply(this, this.sortInfo);
4541         }
4542         this.fireEvent("beforerender", this, this.jsonData);
4543     },
4544
4545     onLoad : function(el, o){
4546         this.fireEvent("load", this, this.jsonData, o);
4547     },
4548
4549     onLoadException : function(el, o){
4550         this.fireEvent("loadexception", this, o);
4551     },
4552
4553 /**
4554  * Filter the data by a specific property.
4555  * @param {String} property A property on your JSON objects
4556  * @param {String/RegExp} value Either string that the property values
4557  * should start with, or a RegExp to test against the property
4558  */
4559     filter : function(property, value){
4560         if(this.jsonData){
4561             var data = [];
4562             var ss = this.snapshot;
4563             if(typeof value == "string"){
4564                 var vlen = value.length;
4565                 if(vlen == 0){
4566                     this.clearFilter();
4567                     return;
4568                 }
4569                 value = value.toLowerCase();
4570                 for(var i = 0, len = ss.length; i < len; i++){
4571                     var o = ss[i];
4572                     if(o[property].substr(0, vlen).toLowerCase() == value){
4573                         data.push(o);
4574                     }
4575                 }
4576             } else if(value.exec){ // regex?
4577                 for(var i = 0, len = ss.length; i < len; i++){
4578                     var o = ss[i];
4579                     if(value.test(o[property])){
4580                         data.push(o);
4581                     }
4582                 }
4583             } else{
4584                 return;
4585             }
4586             this.jsonData = data;
4587             this.refresh();
4588         }
4589     },
4590
4591 /**
4592  * Filter by a function. The passed function will be called with each
4593  * object in the current dataset. If the function returns true the value is kept,
4594  * otherwise it is filtered.
4595  * @param {Function} fn
4596  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4597  */
4598     filterBy : function(fn, scope){
4599         if(this.jsonData){
4600             var data = [];
4601             var ss = this.snapshot;
4602             for(var i = 0, len = ss.length; i < len; i++){
4603                 var o = ss[i];
4604                 if(fn.call(scope || this, o)){
4605                     data.push(o);
4606                 }
4607             }
4608             this.jsonData = data;
4609             this.refresh();
4610         }
4611     },
4612
4613 /**
4614  * Clears the current filter.
4615  */
4616     clearFilter : function(){
4617         if(this.snapshot && this.jsonData != this.snapshot){
4618             this.jsonData = this.snapshot;
4619             this.refresh();
4620         }
4621     },
4622
4623
4624 /**
4625  * Sorts the data for this view and refreshes it.
4626  * @param {String} property A property on your JSON objects to sort on
4627  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4628  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4629  */
4630     sort : function(property, dir, sortType){
4631         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4632         if(this.jsonData){
4633             var p = property;
4634             var dsc = dir && dir.toLowerCase() == "desc";
4635             var f = function(o1, o2){
4636                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4637                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4638                 ;
4639                 if(v1 < v2){
4640                     return dsc ? +1 : -1;
4641                 } else if(v1 > v2){
4642                     return dsc ? -1 : +1;
4643                 } else{
4644                     return 0;
4645                 }
4646             };
4647             this.jsonData.sort(f);
4648             this.refresh();
4649             if(this.jsonData != this.snapshot){
4650                 this.snapshot.sort(f);
4651             }
4652         }
4653     }
4654 });/*
4655  * Based on:
4656  * Ext JS Library 1.1.1
4657  * Copyright(c) 2006-2007, Ext JS, LLC.
4658  *
4659  * Originally Released Under LGPL - original licence link has changed is not relivant.
4660  *
4661  * Fork - LGPL
4662  * <script type="text/javascript">
4663  */
4664  
4665
4666 /**
4667  * @class Roo.ColorPalette
4668  * @extends Roo.Component
4669  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4670  * Here's an example of typical usage:
4671  * <pre><code>
4672 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4673 cp.render('my-div');
4674
4675 cp.on('select', function(palette, selColor){
4676     // do something with selColor
4677 });
4678 </code></pre>
4679  * @constructor
4680  * Create a new ColorPalette
4681  * @param {Object} config The config object
4682  */
4683 Roo.ColorPalette = function(config){
4684     Roo.ColorPalette.superclass.constructor.call(this, config);
4685     this.addEvents({
4686         /**
4687              * @event select
4688              * Fires when a color is selected
4689              * @param {ColorPalette} this
4690              * @param {String} color The 6-digit color hex code (without the # symbol)
4691              */
4692         select: true
4693     });
4694
4695     if(this.handler){
4696         this.on("select", this.handler, this.scope, true);
4697     }
4698 };
4699 Roo.extend(Roo.ColorPalette, Roo.Component, {
4700     /**
4701      * @cfg {String} itemCls
4702      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4703      */
4704     itemCls : "x-color-palette",
4705     /**
4706      * @cfg {String} value
4707      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4708      * the hex codes are case-sensitive.
4709      */
4710     value : null,
4711     clickEvent:'click',
4712     // private
4713     ctype: "Roo.ColorPalette",
4714
4715     /**
4716      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4717      */
4718     allowReselect : false,
4719
4720     /**
4721      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4722      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4723      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4724      * of colors with the width setting until the box is symmetrical.</p>
4725      * <p>You can override individual colors if needed:</p>
4726      * <pre><code>
4727 var cp = new Roo.ColorPalette();
4728 cp.colors[0] = "FF0000";  // change the first box to red
4729 </code></pre>
4730
4731 Or you can provide a custom array of your own for complete control:
4732 <pre><code>
4733 var cp = new Roo.ColorPalette();
4734 cp.colors = ["000000", "993300", "333300"];
4735 </code></pre>
4736      * @type Array
4737      */
4738     colors : [
4739         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4740         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4741         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4742         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4743         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4744     ],
4745
4746     // private
4747     onRender : function(container, position){
4748         var t = new Roo.MasterTemplate(
4749             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4750         );
4751         var c = this.colors;
4752         for(var i = 0, len = c.length; i < len; i++){
4753             t.add([c[i]]);
4754         }
4755         var el = document.createElement("div");
4756         el.className = this.itemCls;
4757         t.overwrite(el);
4758         container.dom.insertBefore(el, position);
4759         this.el = Roo.get(el);
4760         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4761         if(this.clickEvent != 'click'){
4762             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4763         }
4764     },
4765
4766     // private
4767     afterRender : function(){
4768         Roo.ColorPalette.superclass.afterRender.call(this);
4769         if(this.value){
4770             var s = this.value;
4771             this.value = null;
4772             this.select(s);
4773         }
4774     },
4775
4776     // private
4777     handleClick : function(e, t){
4778         e.preventDefault();
4779         if(!this.disabled){
4780             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4781             this.select(c.toUpperCase());
4782         }
4783     },
4784
4785     /**
4786      * Selects the specified color in the palette (fires the select event)
4787      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4788      */
4789     select : function(color){
4790         color = color.replace("#", "");
4791         if(color != this.value || this.allowReselect){
4792             var el = this.el;
4793             if(this.value){
4794                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4795             }
4796             el.child("a.color-"+color).addClass("x-color-palette-sel");
4797             this.value = color;
4798             this.fireEvent("select", this, color);
4799         }
4800     }
4801 });/*
4802  * Based on:
4803  * Ext JS Library 1.1.1
4804  * Copyright(c) 2006-2007, Ext JS, LLC.
4805  *
4806  * Originally Released Under LGPL - original licence link has changed is not relivant.
4807  *
4808  * Fork - LGPL
4809  * <script type="text/javascript">
4810  */
4811  
4812 /**
4813  * @class Roo.DatePicker
4814  * @extends Roo.Component
4815  * Simple date picker class.
4816  * @constructor
4817  * Create a new DatePicker
4818  * @param {Object} config The config object
4819  */
4820 Roo.DatePicker = function(config){
4821     Roo.DatePicker.superclass.constructor.call(this, config);
4822
4823     this.value = config && config.value ?
4824                  config.value.clearTime() : new Date().clearTime();
4825
4826     this.addEvents({
4827         /**
4828              * @event select
4829              * Fires when a date is selected
4830              * @param {DatePicker} this
4831              * @param {Date} date The selected date
4832              */
4833         'select': true,
4834         /**
4835              * @event monthchange
4836              * Fires when the displayed month changes 
4837              * @param {DatePicker} this
4838              * @param {Date} date The selected month
4839              */
4840         'monthchange': true
4841     });
4842
4843     if(this.handler){
4844         this.on("select", this.handler,  this.scope || this);
4845     }
4846     // build the disabledDatesRE
4847     if(!this.disabledDatesRE && this.disabledDates){
4848         var dd = this.disabledDates;
4849         var re = "(?:";
4850         for(var i = 0; i < dd.length; i++){
4851             re += dd[i];
4852             if(i != dd.length-1) {
4853                 re += "|";
4854             }
4855         }
4856         this.disabledDatesRE = new RegExp(re + ")");
4857     }
4858 };
4859
4860 Roo.extend(Roo.DatePicker, Roo.Component, {
4861     /**
4862      * @cfg {String} todayText
4863      * The text to display on the button that selects the current date (defaults to "Today")
4864      */
4865     todayText : "Today",
4866     /**
4867      * @cfg {String} okText
4868      * The text to display on the ok button
4869      */
4870     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4871     /**
4872      * @cfg {String} cancelText
4873      * The text to display on the cancel button
4874      */
4875     cancelText : "Cancel",
4876     /**
4877      * @cfg {String} todayTip
4878      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4879      */
4880     todayTip : "{0} (Spacebar)",
4881     /**
4882      * @cfg {Date} minDate
4883      * Minimum allowable date (JavaScript date object, defaults to null)
4884      */
4885     minDate : null,
4886     /**
4887      * @cfg {Date} maxDate
4888      * Maximum allowable date (JavaScript date object, defaults to null)
4889      */
4890     maxDate : null,
4891     /**
4892      * @cfg {String} minText
4893      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4894      */
4895     minText : "This date is before the minimum date",
4896     /**
4897      * @cfg {String} maxText
4898      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4899      */
4900     maxText : "This date is after the maximum date",
4901     /**
4902      * @cfg {String} format
4903      * The default date format string which can be overriden for localization support.  The format must be
4904      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4905      */
4906     format : "m/d/y",
4907     /**
4908      * @cfg {Array} disabledDays
4909      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4910      */
4911     disabledDays : null,
4912     /**
4913      * @cfg {String} disabledDaysText
4914      * The tooltip to display when the date falls on a disabled day (defaults to "")
4915      */
4916     disabledDaysText : "",
4917     /**
4918      * @cfg {RegExp} disabledDatesRE
4919      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4920      */
4921     disabledDatesRE : null,
4922     /**
4923      * @cfg {String} disabledDatesText
4924      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4925      */
4926     disabledDatesText : "",
4927     /**
4928      * @cfg {Boolean} constrainToViewport
4929      * True to constrain the date picker to the viewport (defaults to true)
4930      */
4931     constrainToViewport : true,
4932     /**
4933      * @cfg {Array} monthNames
4934      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4935      */
4936     monthNames : Date.monthNames,
4937     /**
4938      * @cfg {Array} dayNames
4939      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4940      */
4941     dayNames : Date.dayNames,
4942     /**
4943      * @cfg {String} nextText
4944      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4945      */
4946     nextText: 'Next Month (Control+Right)',
4947     /**
4948      * @cfg {String} prevText
4949      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4950      */
4951     prevText: 'Previous Month (Control+Left)',
4952     /**
4953      * @cfg {String} monthYearText
4954      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4955      */
4956     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4957     /**
4958      * @cfg {Number} startDay
4959      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4960      */
4961     startDay : 0,
4962     /**
4963      * @cfg {Bool} showClear
4964      * Show a clear button (usefull for date form elements that can be blank.)
4965      */
4966     
4967     showClear: false,
4968     
4969     /**
4970      * Sets the value of the date field
4971      * @param {Date} value The date to set
4972      */
4973     setValue : function(value){
4974         var old = this.value;
4975         
4976         if (typeof(value) == 'string') {
4977          
4978             value = Date.parseDate(value, this.format);
4979         }
4980         if (!value) {
4981             value = new Date();
4982         }
4983         
4984         this.value = value.clearTime(true);
4985         if(this.el){
4986             this.update(this.value);
4987         }
4988     },
4989
4990     /**
4991      * Gets the current selected value of the date field
4992      * @return {Date} The selected date
4993      */
4994     getValue : function(){
4995         return this.value;
4996     },
4997
4998     // private
4999     focus : function(){
5000         if(this.el){
5001             this.update(this.activeDate);
5002         }
5003     },
5004
5005     // privateval
5006     onRender : function(container, position){
5007         
5008         var m = [
5009              '<table cellspacing="0">',
5010                 '<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>',
5011                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5012         var dn = this.dayNames;
5013         for(var i = 0; i < 7; i++){
5014             var d = this.startDay+i;
5015             if(d > 6){
5016                 d = d-7;
5017             }
5018             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5019         }
5020         m[m.length] = "</tr></thead><tbody><tr>";
5021         for(var i = 0; i < 42; i++) {
5022             if(i % 7 == 0 && i != 0){
5023                 m[m.length] = "</tr><tr>";
5024             }
5025             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5026         }
5027         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5028             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5029
5030         var el = document.createElement("div");
5031         el.className = "x-date-picker";
5032         el.innerHTML = m.join("");
5033
5034         container.dom.insertBefore(el, position);
5035
5036         this.el = Roo.get(el);
5037         this.eventEl = Roo.get(el.firstChild);
5038
5039         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5040             handler: this.showPrevMonth,
5041             scope: this,
5042             preventDefault:true,
5043             stopDefault:true
5044         });
5045
5046         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5047             handler: this.showNextMonth,
5048             scope: this,
5049             preventDefault:true,
5050             stopDefault:true
5051         });
5052
5053         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5054
5055         this.monthPicker = this.el.down('div.x-date-mp');
5056         this.monthPicker.enableDisplayMode('block');
5057         
5058         var kn = new Roo.KeyNav(this.eventEl, {
5059             "left" : function(e){
5060                 e.ctrlKey ?
5061                     this.showPrevMonth() :
5062                     this.update(this.activeDate.add("d", -1));
5063             },
5064
5065             "right" : function(e){
5066                 e.ctrlKey ?
5067                     this.showNextMonth() :
5068                     this.update(this.activeDate.add("d", 1));
5069             },
5070
5071             "up" : function(e){
5072                 e.ctrlKey ?
5073                     this.showNextYear() :
5074                     this.update(this.activeDate.add("d", -7));
5075             },
5076
5077             "down" : function(e){
5078                 e.ctrlKey ?
5079                     this.showPrevYear() :
5080                     this.update(this.activeDate.add("d", 7));
5081             },
5082
5083             "pageUp" : function(e){
5084                 this.showNextMonth();
5085             },
5086
5087             "pageDown" : function(e){
5088                 this.showPrevMonth();
5089             },
5090
5091             "enter" : function(e){
5092                 e.stopPropagation();
5093                 return true;
5094             },
5095
5096             scope : this
5097         });
5098
5099         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5100
5101         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5102
5103         this.el.unselectable();
5104         
5105         this.cells = this.el.select("table.x-date-inner tbody td");
5106         this.textNodes = this.el.query("table.x-date-inner tbody span");
5107
5108         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5109             text: "&#160;",
5110             tooltip: this.monthYearText
5111         });
5112
5113         this.mbtn.on('click', this.showMonthPicker, this);
5114         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5115
5116
5117         var today = (new Date()).dateFormat(this.format);
5118         
5119         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5120         if (this.showClear) {
5121             baseTb.add( new Roo.Toolbar.Fill());
5122         }
5123         baseTb.add({
5124             text: String.format(this.todayText, today),
5125             tooltip: String.format(this.todayTip, today),
5126             handler: this.selectToday,
5127             scope: this
5128         });
5129         
5130         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5131             
5132         //});
5133         if (this.showClear) {
5134             
5135             baseTb.add( new Roo.Toolbar.Fill());
5136             baseTb.add({
5137                 text: '&#160;',
5138                 cls: 'x-btn-icon x-btn-clear',
5139                 handler: function() {
5140                     //this.value = '';
5141                     this.fireEvent("select", this, '');
5142                 },
5143                 scope: this
5144             });
5145         }
5146         
5147         
5148         if(Roo.isIE){
5149             this.el.repaint();
5150         }
5151         this.update(this.value);
5152     },
5153
5154     createMonthPicker : function(){
5155         if(!this.monthPicker.dom.firstChild){
5156             var buf = ['<table border="0" cellspacing="0">'];
5157             for(var i = 0; i < 6; i++){
5158                 buf.push(
5159                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5160                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5161                     i == 0 ?
5162                     '<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>' :
5163                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5164                 );
5165             }
5166             buf.push(
5167                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5168                     this.okText,
5169                     '</button><button type="button" class="x-date-mp-cancel">',
5170                     this.cancelText,
5171                     '</button></td></tr>',
5172                 '</table>'
5173             );
5174             this.monthPicker.update(buf.join(''));
5175             this.monthPicker.on('click', this.onMonthClick, this);
5176             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5177
5178             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5179             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5180
5181             this.mpMonths.each(function(m, a, i){
5182                 i += 1;
5183                 if((i%2) == 0){
5184                     m.dom.xmonth = 5 + Math.round(i * .5);
5185                 }else{
5186                     m.dom.xmonth = Math.round((i-1) * .5);
5187                 }
5188             });
5189         }
5190     },
5191
5192     showMonthPicker : function(){
5193         this.createMonthPicker();
5194         var size = this.el.getSize();
5195         this.monthPicker.setSize(size);
5196         this.monthPicker.child('table').setSize(size);
5197
5198         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5199         this.updateMPMonth(this.mpSelMonth);
5200         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5201         this.updateMPYear(this.mpSelYear);
5202
5203         this.monthPicker.slideIn('t', {duration:.2});
5204     },
5205
5206     updateMPYear : function(y){
5207         this.mpyear = y;
5208         var ys = this.mpYears.elements;
5209         for(var i = 1; i <= 10; i++){
5210             var td = ys[i-1], y2;
5211             if((i%2) == 0){
5212                 y2 = y + Math.round(i * .5);
5213                 td.firstChild.innerHTML = y2;
5214                 td.xyear = y2;
5215             }else{
5216                 y2 = y - (5-Math.round(i * .5));
5217                 td.firstChild.innerHTML = y2;
5218                 td.xyear = y2;
5219             }
5220             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5221         }
5222     },
5223
5224     updateMPMonth : function(sm){
5225         this.mpMonths.each(function(m, a, i){
5226             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5227         });
5228     },
5229
5230     selectMPMonth: function(m){
5231         
5232     },
5233
5234     onMonthClick : function(e, t){
5235         e.stopEvent();
5236         var el = new Roo.Element(t), pn;
5237         if(el.is('button.x-date-mp-cancel')){
5238             this.hideMonthPicker();
5239         }
5240         else if(el.is('button.x-date-mp-ok')){
5241             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-month', 2)){
5245             this.mpMonths.removeClass('x-date-mp-sel');
5246             pn.addClass('x-date-mp-sel');
5247             this.mpSelMonth = pn.dom.xmonth;
5248         }
5249         else if(pn = el.up('td.x-date-mp-year', 2)){
5250             this.mpYears.removeClass('x-date-mp-sel');
5251             pn.addClass('x-date-mp-sel');
5252             this.mpSelYear = pn.dom.xyear;
5253         }
5254         else if(el.is('a.x-date-mp-prev')){
5255             this.updateMPYear(this.mpyear-10);
5256         }
5257         else if(el.is('a.x-date-mp-next')){
5258             this.updateMPYear(this.mpyear+10);
5259         }
5260     },
5261
5262     onMonthDblClick : function(e, t){
5263         e.stopEvent();
5264         var el = new Roo.Element(t), pn;
5265         if(pn = el.up('td.x-date-mp-month', 2)){
5266             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5267             this.hideMonthPicker();
5268         }
5269         else if(pn = el.up('td.x-date-mp-year', 2)){
5270             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5271             this.hideMonthPicker();
5272         }
5273     },
5274
5275     hideMonthPicker : function(disableAnim){
5276         if(this.monthPicker){
5277             if(disableAnim === true){
5278                 this.monthPicker.hide();
5279             }else{
5280                 this.monthPicker.slideOut('t', {duration:.2});
5281             }
5282         }
5283     },
5284
5285     // private
5286     showPrevMonth : function(e){
5287         this.update(this.activeDate.add("mo", -1));
5288     },
5289
5290     // private
5291     showNextMonth : function(e){
5292         this.update(this.activeDate.add("mo", 1));
5293     },
5294
5295     // private
5296     showPrevYear : function(){
5297         this.update(this.activeDate.add("y", -1));
5298     },
5299
5300     // private
5301     showNextYear : function(){
5302         this.update(this.activeDate.add("y", 1));
5303     },
5304
5305     // private
5306     handleMouseWheel : function(e){
5307         var delta = e.getWheelDelta();
5308         if(delta > 0){
5309             this.showPrevMonth();
5310             e.stopEvent();
5311         } else if(delta < 0){
5312             this.showNextMonth();
5313             e.stopEvent();
5314         }
5315     },
5316
5317     // private
5318     handleDateClick : function(e, t){
5319         e.stopEvent();
5320         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5321             this.setValue(new Date(t.dateValue));
5322             this.fireEvent("select", this, this.value);
5323         }
5324     },
5325
5326     // private
5327     selectToday : function(){
5328         this.setValue(new Date().clearTime());
5329         this.fireEvent("select", this, this.value);
5330     },
5331
5332     // private
5333     update : function(date)
5334     {
5335         var vd = this.activeDate;
5336         this.activeDate = date;
5337         if(vd && this.el){
5338             var t = date.getTime();
5339             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5340                 this.cells.removeClass("x-date-selected");
5341                 this.cells.each(function(c){
5342                    if(c.dom.firstChild.dateValue == t){
5343                        c.addClass("x-date-selected");
5344                        setTimeout(function(){
5345                             try{c.dom.firstChild.focus();}catch(e){}
5346                        }, 50);
5347                        return false;
5348                    }
5349                 });
5350                 return;
5351             }
5352         }
5353         
5354         var days = date.getDaysInMonth();
5355         var firstOfMonth = date.getFirstDateOfMonth();
5356         var startingPos = firstOfMonth.getDay()-this.startDay;
5357
5358         if(startingPos <= this.startDay){
5359             startingPos += 7;
5360         }
5361
5362         var pm = date.add("mo", -1);
5363         var prevStart = pm.getDaysInMonth()-startingPos;
5364
5365         var cells = this.cells.elements;
5366         var textEls = this.textNodes;
5367         days += startingPos;
5368
5369         // convert everything to numbers so it's fast
5370         var day = 86400000;
5371         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5372         var today = new Date().clearTime().getTime();
5373         var sel = date.clearTime().getTime();
5374         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5375         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5376         var ddMatch = this.disabledDatesRE;
5377         var ddText = this.disabledDatesText;
5378         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5379         var ddaysText = this.disabledDaysText;
5380         var format = this.format;
5381
5382         var setCellClass = function(cal, cell){
5383             cell.title = "";
5384             var t = d.getTime();
5385             cell.firstChild.dateValue = t;
5386             if(t == today){
5387                 cell.className += " x-date-today";
5388                 cell.title = cal.todayText;
5389             }
5390             if(t == sel){
5391                 cell.className += " x-date-selected";
5392                 setTimeout(function(){
5393                     try{cell.firstChild.focus();}catch(e){}
5394                 }, 50);
5395             }
5396             // disabling
5397             if(t < min) {
5398                 cell.className = " x-date-disabled";
5399                 cell.title = cal.minText;
5400                 return;
5401             }
5402             if(t > max) {
5403                 cell.className = " x-date-disabled";
5404                 cell.title = cal.maxText;
5405                 return;
5406             }
5407             if(ddays){
5408                 if(ddays.indexOf(d.getDay()) != -1){
5409                     cell.title = ddaysText;
5410                     cell.className = " x-date-disabled";
5411                 }
5412             }
5413             if(ddMatch && format){
5414                 var fvalue = d.dateFormat(format);
5415                 if(ddMatch.test(fvalue)){
5416                     cell.title = ddText.replace("%0", fvalue);
5417                     cell.className = " x-date-disabled";
5418                 }
5419             }
5420         };
5421
5422         var i = 0;
5423         for(; i < startingPos; i++) {
5424             textEls[i].innerHTML = (++prevStart);
5425             d.setDate(d.getDate()+1);
5426             cells[i].className = "x-date-prevday";
5427             setCellClass(this, cells[i]);
5428         }
5429         for(; i < days; i++){
5430             intDay = i - startingPos + 1;
5431             textEls[i].innerHTML = (intDay);
5432             d.setDate(d.getDate()+1);
5433             cells[i].className = "x-date-active";
5434             setCellClass(this, cells[i]);
5435         }
5436         var extraDays = 0;
5437         for(; i < 42; i++) {
5438              textEls[i].innerHTML = (++extraDays);
5439              d.setDate(d.getDate()+1);
5440              cells[i].className = "x-date-nextday";
5441              setCellClass(this, cells[i]);
5442         }
5443
5444         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5445         this.fireEvent('monthchange', this, date);
5446         
5447         if(!this.internalRender){
5448             var main = this.el.dom.firstChild;
5449             var w = main.offsetWidth;
5450             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5451             Roo.fly(main).setWidth(w);
5452             this.internalRender = true;
5453             // opera does not respect the auto grow header center column
5454             // then, after it gets a width opera refuses to recalculate
5455             // without a second pass
5456             if(Roo.isOpera && !this.secondPass){
5457                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5458                 this.secondPass = true;
5459                 this.update.defer(10, this, [date]);
5460             }
5461         }
5462         
5463         
5464     }
5465 });        /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475 /**
5476  * @class Roo.TabPanel
5477  * @extends Roo.util.Observable
5478  * A lightweight tab container.
5479  * <br><br>
5480  * Usage:
5481  * <pre><code>
5482 // basic tabs 1, built from existing content
5483 var tabs = new Roo.TabPanel("tabs1");
5484 tabs.addTab("script", "View Script");
5485 tabs.addTab("markup", "View Markup");
5486 tabs.activate("script");
5487
5488 // more advanced tabs, built from javascript
5489 var jtabs = new Roo.TabPanel("jtabs");
5490 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5491
5492 // set up the UpdateManager
5493 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5494 var updater = tab2.getUpdateManager();
5495 updater.setDefaultUrl("ajax1.htm");
5496 tab2.on('activate', updater.refresh, updater, true);
5497
5498 // Use setUrl for Ajax loading
5499 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5500 tab3.setUrl("ajax2.htm", null, true);
5501
5502 // Disabled tab
5503 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5504 tab4.disable();
5505
5506 jtabs.activate("jtabs-1");
5507  * </code></pre>
5508  * @constructor
5509  * Create a new TabPanel.
5510  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5511  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5512  */
5513 Roo.TabPanel = function(container, config){
5514     /**
5515     * The container element for this TabPanel.
5516     * @type Roo.Element
5517     */
5518     this.el = Roo.get(container, true);
5519     if(config){
5520         if(typeof config == "boolean"){
5521             this.tabPosition = config ? "bottom" : "top";
5522         }else{
5523             Roo.apply(this, config);
5524         }
5525     }
5526     if(this.tabPosition == "bottom"){
5527         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5528         this.el.addClass("x-tabs-bottom");
5529     }
5530     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5531     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5532     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5533     if(Roo.isIE){
5534         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5535     }
5536     if(this.tabPosition != "bottom"){
5537         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5538          * @type Roo.Element
5539          */
5540         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5541         this.el.addClass("x-tabs-top");
5542     }
5543     this.items = [];
5544
5545     this.bodyEl.setStyle("position", "relative");
5546
5547     this.active = null;
5548     this.activateDelegate = this.activate.createDelegate(this);
5549
5550     this.addEvents({
5551         /**
5552          * @event tabchange
5553          * Fires when the active tab changes
5554          * @param {Roo.TabPanel} this
5555          * @param {Roo.TabPanelItem} activePanel The new active tab
5556          */
5557         "tabchange": true,
5558         /**
5559          * @event beforetabchange
5560          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5561          * @param {Roo.TabPanel} this
5562          * @param {Object} e Set cancel to true on this object to cancel the tab change
5563          * @param {Roo.TabPanelItem} tab The tab being changed to
5564          */
5565         "beforetabchange" : true
5566     });
5567
5568     Roo.EventManager.onWindowResize(this.onResize, this);
5569     this.cpad = this.el.getPadding("lr");
5570     this.hiddenCount = 0;
5571
5572
5573     // toolbar on the tabbar support...
5574     if (this.toolbar) {
5575         var tcfg = this.toolbar;
5576         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5577         this.toolbar = new Roo.Toolbar(tcfg);
5578         if (Roo.isSafari) {
5579             var tbl = tcfg.container.child('table', true);
5580             tbl.setAttribute('width', '100%');
5581         }
5582         
5583     }
5584    
5585
5586
5587     Roo.TabPanel.superclass.constructor.call(this);
5588 };
5589
5590 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5591     /*
5592      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5593      */
5594     tabPosition : "top",
5595     /*
5596      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5597      */
5598     currentTabWidth : 0,
5599     /*
5600      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5601      */
5602     minTabWidth : 40,
5603     /*
5604      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5605      */
5606     maxTabWidth : 250,
5607     /*
5608      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5609      */
5610     preferredTabWidth : 175,
5611     /*
5612      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5613      */
5614     resizeTabs : false,
5615     /*
5616      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5617      */
5618     monitorResize : true,
5619     /*
5620      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5621      */
5622     toolbar : false,
5623
5624     /**
5625      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5626      * @param {String} id The id of the div to use <b>or create</b>
5627      * @param {String} text The text for the tab
5628      * @param {String} content (optional) Content to put in the TabPanelItem body
5629      * @param {Boolean} closable (optional) True to create a close icon on the tab
5630      * @return {Roo.TabPanelItem} The created TabPanelItem
5631      */
5632     addTab : function(id, text, content, closable){
5633         var item = new Roo.TabPanelItem(this, id, text, closable);
5634         this.addTabItem(item);
5635         if(content){
5636             item.setContent(content);
5637         }
5638         return item;
5639     },
5640
5641     /**
5642      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5643      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5644      * @return {Roo.TabPanelItem}
5645      */
5646     getTab : function(id){
5647         return this.items[id];
5648     },
5649
5650     /**
5651      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5652      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5653      */
5654     hideTab : function(id){
5655         var t = this.items[id];
5656         if(!t.isHidden()){
5657            t.setHidden(true);
5658            this.hiddenCount++;
5659            this.autoSizeTabs();
5660         }
5661     },
5662
5663     /**
5664      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5665      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5666      */
5667     unhideTab : function(id){
5668         var t = this.items[id];
5669         if(t.isHidden()){
5670            t.setHidden(false);
5671            this.hiddenCount--;
5672            this.autoSizeTabs();
5673         }
5674     },
5675
5676     /**
5677      * Adds an existing {@link Roo.TabPanelItem}.
5678      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5679      */
5680     addTabItem : function(item){
5681         this.items[item.id] = item;
5682         this.items.push(item);
5683         if(this.resizeTabs){
5684            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5685            this.autoSizeTabs();
5686         }else{
5687             item.autoSize();
5688         }
5689     },
5690
5691     /**
5692      * Removes a {@link Roo.TabPanelItem}.
5693      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5694      */
5695     removeTab : function(id){
5696         var items = this.items;
5697         var tab = items[id];
5698         if(!tab) { return; }
5699         var index = items.indexOf(tab);
5700         if(this.active == tab && items.length > 1){
5701             var newTab = this.getNextAvailable(index);
5702             if(newTab) {
5703                 newTab.activate();
5704             }
5705         }
5706         this.stripEl.dom.removeChild(tab.pnode.dom);
5707         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5708             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5709         }
5710         items.splice(index, 1);
5711         delete this.items[tab.id];
5712         tab.fireEvent("close", tab);
5713         tab.purgeListeners();
5714         this.autoSizeTabs();
5715     },
5716
5717     getNextAvailable : function(start){
5718         var items = this.items;
5719         var index = start;
5720         // look for a next tab that will slide over to
5721         // replace the one being removed
5722         while(index < items.length){
5723             var item = items[++index];
5724             if(item && !item.isHidden()){
5725                 return item;
5726             }
5727         }
5728         // if one isn't found select the previous tab (on the left)
5729         index = start;
5730         while(index >= 0){
5731             var item = items[--index];
5732             if(item && !item.isHidden()){
5733                 return item;
5734             }
5735         }
5736         return null;
5737     },
5738
5739     /**
5740      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5741      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5742      */
5743     disableTab : function(id){
5744         var tab = this.items[id];
5745         if(tab && this.active != tab){
5746             tab.disable();
5747         }
5748     },
5749
5750     /**
5751      * Enables a {@link Roo.TabPanelItem} that is disabled.
5752      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5753      */
5754     enableTab : function(id){
5755         var tab = this.items[id];
5756         tab.enable();
5757     },
5758
5759     /**
5760      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5761      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5762      * @return {Roo.TabPanelItem} The TabPanelItem.
5763      */
5764     activate : function(id){
5765         var tab = this.items[id];
5766         if(!tab){
5767             return null;
5768         }
5769         if(tab == this.active || tab.disabled){
5770             return tab;
5771         }
5772         var e = {};
5773         this.fireEvent("beforetabchange", this, e, tab);
5774         if(e.cancel !== true && !tab.disabled){
5775             if(this.active){
5776                 this.active.hide();
5777             }
5778             this.active = this.items[id];
5779             this.active.show();
5780             this.fireEvent("tabchange", this, this.active);
5781         }
5782         return tab;
5783     },
5784
5785     /**
5786      * Gets the active {@link Roo.TabPanelItem}.
5787      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5788      */
5789     getActiveTab : function(){
5790         return this.active;
5791     },
5792
5793     /**
5794      * Updates the tab body element to fit the height of the container element
5795      * for overflow scrolling
5796      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5797      */
5798     syncHeight : function(targetHeight){
5799         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5800         var bm = this.bodyEl.getMargins();
5801         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5802         this.bodyEl.setHeight(newHeight);
5803         return newHeight;
5804     },
5805
5806     onResize : function(){
5807         if(this.monitorResize){
5808             this.autoSizeTabs();
5809         }
5810     },
5811
5812     /**
5813      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5814      */
5815     beginUpdate : function(){
5816         this.updating = true;
5817     },
5818
5819     /**
5820      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5821      */
5822     endUpdate : function(){
5823         this.updating = false;
5824         this.autoSizeTabs();
5825     },
5826
5827     /**
5828      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5829      */
5830     autoSizeTabs : function(){
5831         var count = this.items.length;
5832         var vcount = count - this.hiddenCount;
5833         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5834             return;
5835         }
5836         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5837         var availWidth = Math.floor(w / vcount);
5838         var b = this.stripBody;
5839         if(b.getWidth() > w){
5840             var tabs = this.items;
5841             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5842             if(availWidth < this.minTabWidth){
5843                 /*if(!this.sleft){    // incomplete scrolling code
5844                     this.createScrollButtons();
5845                 }
5846                 this.showScroll();
5847                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5848             }
5849         }else{
5850             if(this.currentTabWidth < this.preferredTabWidth){
5851                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5852             }
5853         }
5854     },
5855
5856     /**
5857      * Returns the number of tabs in this TabPanel.
5858      * @return {Number}
5859      */
5860      getCount : function(){
5861          return this.items.length;
5862      },
5863
5864     /**
5865      * Resizes all the tabs to the passed width
5866      * @param {Number} The new width
5867      */
5868     setTabWidth : function(width){
5869         this.currentTabWidth = width;
5870         for(var i = 0, len = this.items.length; i < len; i++) {
5871                 if(!this.items[i].isHidden()) {
5872                 this.items[i].setWidth(width);
5873             }
5874         }
5875     },
5876
5877     /**
5878      * Destroys this TabPanel
5879      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5880      */
5881     destroy : function(removeEl){
5882         Roo.EventManager.removeResizeListener(this.onResize, this);
5883         for(var i = 0, len = this.items.length; i < len; i++){
5884             this.items[i].purgeListeners();
5885         }
5886         if(removeEl === true){
5887             this.el.update("");
5888             this.el.remove();
5889         }
5890     }
5891 });
5892
5893 /**
5894  * @class Roo.TabPanelItem
5895  * @extends Roo.util.Observable
5896  * Represents an individual item (tab plus body) in a TabPanel.
5897  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5898  * @param {String} id The id of this TabPanelItem
5899  * @param {String} text The text for the tab of this TabPanelItem
5900  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5901  */
5902 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5903     /**
5904      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5905      * @type Roo.TabPanel
5906      */
5907     this.tabPanel = tabPanel;
5908     /**
5909      * The id for this TabPanelItem
5910      * @type String
5911      */
5912     this.id = id;
5913     /** @private */
5914     this.disabled = false;
5915     /** @private */
5916     this.text = text;
5917     /** @private */
5918     this.loaded = false;
5919     this.closable = closable;
5920
5921     /**
5922      * The body element for this TabPanelItem.
5923      * @type Roo.Element
5924      */
5925     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5926     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5927     this.bodyEl.setStyle("display", "block");
5928     this.bodyEl.setStyle("zoom", "1");
5929     this.hideAction();
5930
5931     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5932     /** @private */
5933     this.el = Roo.get(els.el, true);
5934     this.inner = Roo.get(els.inner, true);
5935     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5936     this.pnode = Roo.get(els.el.parentNode, true);
5937     this.el.on("mousedown", this.onTabMouseDown, this);
5938     this.el.on("click", this.onTabClick, this);
5939     /** @private */
5940     if(closable){
5941         var c = Roo.get(els.close, true);
5942         c.dom.title = this.closeText;
5943         c.addClassOnOver("close-over");
5944         c.on("click", this.closeClick, this);
5945      }
5946
5947     this.addEvents({
5948          /**
5949          * @event activate
5950          * Fires when this tab becomes the active tab.
5951          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5952          * @param {Roo.TabPanelItem} this
5953          */
5954         "activate": true,
5955         /**
5956          * @event beforeclose
5957          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5958          * @param {Roo.TabPanelItem} this
5959          * @param {Object} e Set cancel to true on this object to cancel the close.
5960          */
5961         "beforeclose": true,
5962         /**
5963          * @event close
5964          * Fires when this tab is closed.
5965          * @param {Roo.TabPanelItem} this
5966          */
5967          "close": true,
5968         /**
5969          * @event deactivate
5970          * Fires when this tab is no longer the active tab.
5971          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5972          * @param {Roo.TabPanelItem} this
5973          */
5974          "deactivate" : true
5975     });
5976     this.hidden = false;
5977
5978     Roo.TabPanelItem.superclass.constructor.call(this);
5979 };
5980
5981 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5982     purgeListeners : function(){
5983        Roo.util.Observable.prototype.purgeListeners.call(this);
5984        this.el.removeAllListeners();
5985     },
5986     /**
5987      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5988      */
5989     show : function(){
5990         this.pnode.addClass("on");
5991         this.showAction();
5992         if(Roo.isOpera){
5993             this.tabPanel.stripWrap.repaint();
5994         }
5995         this.fireEvent("activate", this.tabPanel, this);
5996     },
5997
5998     /**
5999      * Returns true if this tab is the active tab.
6000      * @return {Boolean}
6001      */
6002     isActive : function(){
6003         return this.tabPanel.getActiveTab() == this;
6004     },
6005
6006     /**
6007      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6008      */
6009     hide : function(){
6010         this.pnode.removeClass("on");
6011         this.hideAction();
6012         this.fireEvent("deactivate", this.tabPanel, this);
6013     },
6014
6015     hideAction : function(){
6016         this.bodyEl.hide();
6017         this.bodyEl.setStyle("position", "absolute");
6018         this.bodyEl.setLeft("-20000px");
6019         this.bodyEl.setTop("-20000px");
6020     },
6021
6022     showAction : function(){
6023         this.bodyEl.setStyle("position", "relative");
6024         this.bodyEl.setTop("");
6025         this.bodyEl.setLeft("");
6026         this.bodyEl.show();
6027     },
6028
6029     /**
6030      * Set the tooltip for the tab.
6031      * @param {String} tooltip The tab's tooltip
6032      */
6033     setTooltip : function(text){
6034         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6035             this.textEl.dom.qtip = text;
6036             this.textEl.dom.removeAttribute('title');
6037         }else{
6038             this.textEl.dom.title = text;
6039         }
6040     },
6041
6042     onTabClick : function(e){
6043         e.preventDefault();
6044         this.tabPanel.activate(this.id);
6045     },
6046
6047     onTabMouseDown : function(e){
6048         e.preventDefault();
6049         this.tabPanel.activate(this.id);
6050     },
6051
6052     getWidth : function(){
6053         return this.inner.getWidth();
6054     },
6055
6056     setWidth : function(width){
6057         var iwidth = width - this.pnode.getPadding("lr");
6058         this.inner.setWidth(iwidth);
6059         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6060         this.pnode.setWidth(width);
6061     },
6062
6063     /**
6064      * Show or hide the tab
6065      * @param {Boolean} hidden True to hide or false to show.
6066      */
6067     setHidden : function(hidden){
6068         this.hidden = hidden;
6069         this.pnode.setStyle("display", hidden ? "none" : "");
6070     },
6071
6072     /**
6073      * Returns true if this tab is "hidden"
6074      * @return {Boolean}
6075      */
6076     isHidden : function(){
6077         return this.hidden;
6078     },
6079
6080     /**
6081      * Returns the text for this tab
6082      * @return {String}
6083      */
6084     getText : function(){
6085         return this.text;
6086     },
6087
6088     autoSize : function(){
6089         //this.el.beginMeasure();
6090         this.textEl.setWidth(1);
6091         /*
6092          *  #2804 [new] Tabs in Roojs
6093          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6094          */
6095         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6096         //this.el.endMeasure();
6097     },
6098
6099     /**
6100      * Sets the text for the tab (Note: this also sets the tooltip text)
6101      * @param {String} text The tab's text and tooltip
6102      */
6103     setText : function(text){
6104         this.text = text;
6105         this.textEl.update(text);
6106         this.setTooltip(text);
6107         if(!this.tabPanel.resizeTabs){
6108             this.autoSize();
6109         }
6110     },
6111     /**
6112      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6113      */
6114     activate : function(){
6115         this.tabPanel.activate(this.id);
6116     },
6117
6118     /**
6119      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6120      */
6121     disable : function(){
6122         if(this.tabPanel.active != this){
6123             this.disabled = true;
6124             this.pnode.addClass("disabled");
6125         }
6126     },
6127
6128     /**
6129      * Enables this TabPanelItem if it was previously disabled.
6130      */
6131     enable : function(){
6132         this.disabled = false;
6133         this.pnode.removeClass("disabled");
6134     },
6135
6136     /**
6137      * Sets the content for this TabPanelItem.
6138      * @param {String} content The content
6139      * @param {Boolean} loadScripts true to look for and load scripts
6140      */
6141     setContent : function(content, loadScripts){
6142         this.bodyEl.update(content, loadScripts);
6143     },
6144
6145     /**
6146      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6147      * @return {Roo.UpdateManager} The UpdateManager
6148      */
6149     getUpdateManager : function(){
6150         return this.bodyEl.getUpdateManager();
6151     },
6152
6153     /**
6154      * Set a URL to be used to load the content for this TabPanelItem.
6155      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6156      * @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)
6157      * @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)
6158      * @return {Roo.UpdateManager} The UpdateManager
6159      */
6160     setUrl : function(url, params, loadOnce){
6161         if(this.refreshDelegate){
6162             this.un('activate', this.refreshDelegate);
6163         }
6164         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6165         this.on("activate", this.refreshDelegate);
6166         return this.bodyEl.getUpdateManager();
6167     },
6168
6169     /** @private */
6170     _handleRefresh : function(url, params, loadOnce){
6171         if(!loadOnce || !this.loaded){
6172             var updater = this.bodyEl.getUpdateManager();
6173             updater.update(url, params, this._setLoaded.createDelegate(this));
6174         }
6175     },
6176
6177     /**
6178      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6179      *   Will fail silently if the setUrl method has not been called.
6180      *   This does not activate the panel, just updates its content.
6181      */
6182     refresh : function(){
6183         if(this.refreshDelegate){
6184            this.loaded = false;
6185            this.refreshDelegate();
6186         }
6187     },
6188
6189     /** @private */
6190     _setLoaded : function(){
6191         this.loaded = true;
6192     },
6193
6194     /** @private */
6195     closeClick : function(e){
6196         var o = {};
6197         e.stopEvent();
6198         this.fireEvent("beforeclose", this, o);
6199         if(o.cancel !== true){
6200             this.tabPanel.removeTab(this.id);
6201         }
6202     },
6203     /**
6204      * The text displayed in the tooltip for the close icon.
6205      * @type String
6206      */
6207     closeText : "Close this tab"
6208 });
6209
6210 /** @private */
6211 Roo.TabPanel.prototype.createStrip = function(container){
6212     var strip = document.createElement("div");
6213     strip.className = "x-tabs-wrap";
6214     container.appendChild(strip);
6215     return strip;
6216 };
6217 /** @private */
6218 Roo.TabPanel.prototype.createStripList = function(strip){
6219     // div wrapper for retard IE
6220     // returns the "tr" element.
6221     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6222         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6223         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6224     return strip.firstChild.firstChild.firstChild.firstChild;
6225 };
6226 /** @private */
6227 Roo.TabPanel.prototype.createBody = function(container){
6228     var body = document.createElement("div");
6229     Roo.id(body, "tab-body");
6230     Roo.fly(body).addClass("x-tabs-body");
6231     container.appendChild(body);
6232     return body;
6233 };
6234 /** @private */
6235 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6236     var body = Roo.getDom(id);
6237     if(!body){
6238         body = document.createElement("div");
6239         body.id = id;
6240     }
6241     Roo.fly(body).addClass("x-tabs-item-body");
6242     bodyEl.insertBefore(body, bodyEl.firstChild);
6243     return body;
6244 };
6245 /** @private */
6246 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6247     var td = document.createElement("td");
6248     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6249     //stripEl.appendChild(td);
6250     if(closable){
6251         td.className = "x-tabs-closable";
6252         if(!this.closeTpl){
6253             this.closeTpl = new Roo.Template(
6254                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6255                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6256                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6257             );
6258         }
6259         var el = this.closeTpl.overwrite(td, {"text": text});
6260         var close = el.getElementsByTagName("div")[0];
6261         var inner = el.getElementsByTagName("em")[0];
6262         return {"el": el, "close": close, "inner": inner};
6263     } else {
6264         if(!this.tabTpl){
6265             this.tabTpl = new Roo.Template(
6266                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6267                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6268             );
6269         }
6270         var el = this.tabTpl.overwrite(td, {"text": text});
6271         var inner = el.getElementsByTagName("em")[0];
6272         return {"el": el, "inner": inner};
6273     }
6274 };/*
6275  * Based on:
6276  * Ext JS Library 1.1.1
6277  * Copyright(c) 2006-2007, Ext JS, LLC.
6278  *
6279  * Originally Released Under LGPL - original licence link has changed is not relivant.
6280  *
6281  * Fork - LGPL
6282  * <script type="text/javascript">
6283  */
6284
6285 /**
6286  * @class Roo.Button
6287  * @extends Roo.util.Observable
6288  * Simple Button class
6289  * @cfg {String} text The button text
6290  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6291  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6292  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6293  * @cfg {Object} scope The scope of the handler
6294  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6295  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6296  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6297  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6298  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6299  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6300    applies if enableToggle = true)
6301  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6302  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6303   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6304  * @constructor
6305  * Create a new button
6306  * @param {Object} config The config object
6307  */
6308 Roo.Button = function(renderTo, config)
6309 {
6310     if (!config) {
6311         config = renderTo;
6312         renderTo = config.renderTo || false;
6313     }
6314     
6315     Roo.apply(this, config);
6316     this.addEvents({
6317         /**
6318              * @event click
6319              * Fires when this button is clicked
6320              * @param {Button} this
6321              * @param {EventObject} e The click event
6322              */
6323             "click" : true,
6324         /**
6325              * @event toggle
6326              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6327              * @param {Button} this
6328              * @param {Boolean} pressed
6329              */
6330             "toggle" : true,
6331         /**
6332              * @event mouseover
6333              * Fires when the mouse hovers over the button
6334              * @param {Button} this
6335              * @param {Event} e The event object
6336              */
6337         'mouseover' : true,
6338         /**
6339              * @event mouseout
6340              * Fires when the mouse exits the button
6341              * @param {Button} this
6342              * @param {Event} e The event object
6343              */
6344         'mouseout': true,
6345          /**
6346              * @event render
6347              * Fires when the button is rendered
6348              * @param {Button} this
6349              */
6350         'render': true
6351     });
6352     if(this.menu){
6353         this.menu = Roo.menu.MenuMgr.get(this.menu);
6354     }
6355     // register listeners first!!  - so render can be captured..
6356     Roo.util.Observable.call(this);
6357     if(renderTo){
6358         this.render(renderTo);
6359     }
6360     
6361   
6362 };
6363
6364 Roo.extend(Roo.Button, Roo.util.Observable, {
6365     /**
6366      * 
6367      */
6368     
6369     /**
6370      * Read-only. True if this button is hidden
6371      * @type Boolean
6372      */
6373     hidden : false,
6374     /**
6375      * Read-only. True if this button is disabled
6376      * @type Boolean
6377      */
6378     disabled : false,
6379     /**
6380      * Read-only. True if this button is pressed (only if enableToggle = true)
6381      * @type Boolean
6382      */
6383     pressed : false,
6384
6385     /**
6386      * @cfg {Number} tabIndex 
6387      * The DOM tabIndex for this button (defaults to undefined)
6388      */
6389     tabIndex : undefined,
6390
6391     /**
6392      * @cfg {Boolean} enableToggle
6393      * True to enable pressed/not pressed toggling (defaults to false)
6394      */
6395     enableToggle: false,
6396     /**
6397      * @cfg {Roo.menu.Menu} menu
6398      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6399      */
6400     menu : undefined,
6401     /**
6402      * @cfg {String} menuAlign
6403      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6404      */
6405     menuAlign : "tl-bl?",
6406
6407     /**
6408      * @cfg {String} iconCls
6409      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6410      */
6411     iconCls : undefined,
6412     /**
6413      * @cfg {String} type
6414      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6415      */
6416     type : 'button',
6417
6418     // private
6419     menuClassTarget: 'tr',
6420
6421     /**
6422      * @cfg {String} clickEvent
6423      * The type of event to map to the button's event handler (defaults to 'click')
6424      */
6425     clickEvent : 'click',
6426
6427     /**
6428      * @cfg {Boolean} handleMouseEvents
6429      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6430      */
6431     handleMouseEvents : true,
6432
6433     /**
6434      * @cfg {String} tooltipType
6435      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6436      */
6437     tooltipType : 'qtip',
6438
6439     /**
6440      * @cfg {String} cls
6441      * A CSS class to apply to the button's main element.
6442      */
6443     
6444     /**
6445      * @cfg {Roo.Template} template (Optional)
6446      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6447      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6448      * require code modifications if required elements (e.g. a button) aren't present.
6449      */
6450
6451     // private
6452     render : function(renderTo){
6453         var btn;
6454         if(this.hideParent){
6455             this.parentEl = Roo.get(renderTo);
6456         }
6457         if(!this.dhconfig){
6458             if(!this.template){
6459                 if(!Roo.Button.buttonTemplate){
6460                     // hideous table template
6461                     Roo.Button.buttonTemplate = new Roo.Template(
6462                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6463                         '<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>',
6464                         "</tr></tbody></table>");
6465                 }
6466                 this.template = Roo.Button.buttonTemplate;
6467             }
6468             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6469             var btnEl = btn.child("button:first");
6470             btnEl.on('focus', this.onFocus, this);
6471             btnEl.on('blur', this.onBlur, this);
6472             if(this.cls){
6473                 btn.addClass(this.cls);
6474             }
6475             if(this.icon){
6476                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6477             }
6478             if(this.iconCls){
6479                 btnEl.addClass(this.iconCls);
6480                 if(!this.cls){
6481                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6482                 }
6483             }
6484             if(this.tabIndex !== undefined){
6485                 btnEl.dom.tabIndex = this.tabIndex;
6486             }
6487             if(this.tooltip){
6488                 if(typeof this.tooltip == 'object'){
6489                     Roo.QuickTips.tips(Roo.apply({
6490                           target: btnEl.id
6491                     }, this.tooltip));
6492                 } else {
6493                     btnEl.dom[this.tooltipType] = this.tooltip;
6494                 }
6495             }
6496         }else{
6497             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6498         }
6499         this.el = btn;
6500         if(this.id){
6501             this.el.dom.id = this.el.id = this.id;
6502         }
6503         if(this.menu){
6504             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6505             this.menu.on("show", this.onMenuShow, this);
6506             this.menu.on("hide", this.onMenuHide, this);
6507         }
6508         btn.addClass("x-btn");
6509         if(Roo.isIE && !Roo.isIE7){
6510             this.autoWidth.defer(1, this);
6511         }else{
6512             this.autoWidth();
6513         }
6514         if(this.handleMouseEvents){
6515             btn.on("mouseover", this.onMouseOver, this);
6516             btn.on("mouseout", this.onMouseOut, this);
6517             btn.on("mousedown", this.onMouseDown, this);
6518         }
6519         btn.on(this.clickEvent, this.onClick, this);
6520         //btn.on("mouseup", this.onMouseUp, this);
6521         if(this.hidden){
6522             this.hide();
6523         }
6524         if(this.disabled){
6525             this.disable();
6526         }
6527         Roo.ButtonToggleMgr.register(this);
6528         if(this.pressed){
6529             this.el.addClass("x-btn-pressed");
6530         }
6531         if(this.repeat){
6532             var repeater = new Roo.util.ClickRepeater(btn,
6533                 typeof this.repeat == "object" ? this.repeat : {}
6534             );
6535             repeater.on("click", this.onClick,  this);
6536         }
6537         
6538         this.fireEvent('render', this);
6539         
6540     },
6541     /**
6542      * Returns the button's underlying element
6543      * @return {Roo.Element} The element
6544      */
6545     getEl : function(){
6546         return this.el;  
6547     },
6548     
6549     /**
6550      * Destroys this Button and removes any listeners.
6551      */
6552     destroy : function(){
6553         Roo.ButtonToggleMgr.unregister(this);
6554         this.el.removeAllListeners();
6555         this.purgeListeners();
6556         this.el.remove();
6557     },
6558
6559     // private
6560     autoWidth : function(){
6561         if(this.el){
6562             this.el.setWidth("auto");
6563             if(Roo.isIE7 && Roo.isStrict){
6564                 var ib = this.el.child('button');
6565                 if(ib && ib.getWidth() > 20){
6566                     ib.clip();
6567                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6568                 }
6569             }
6570             if(this.minWidth){
6571                 if(this.hidden){
6572                     this.el.beginMeasure();
6573                 }
6574                 if(this.el.getWidth() < this.minWidth){
6575                     this.el.setWidth(this.minWidth);
6576                 }
6577                 if(this.hidden){
6578                     this.el.endMeasure();
6579                 }
6580             }
6581         }
6582     },
6583
6584     /**
6585      * Assigns this button's click handler
6586      * @param {Function} handler The function to call when the button is clicked
6587      * @param {Object} scope (optional) Scope for the function passed in
6588      */
6589     setHandler : function(handler, scope){
6590         this.handler = handler;
6591         this.scope = scope;  
6592     },
6593     
6594     /**
6595      * Sets this button's text
6596      * @param {String} text The button text
6597      */
6598     setText : function(text){
6599         this.text = text;
6600         if(this.el){
6601             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6602         }
6603         this.autoWidth();
6604     },
6605     
6606     /**
6607      * Gets the text for this button
6608      * @return {String} The button text
6609      */
6610     getText : function(){
6611         return this.text;  
6612     },
6613     
6614     /**
6615      * Show this button
6616      */
6617     show: function(){
6618         this.hidden = false;
6619         if(this.el){
6620             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6621         }
6622     },
6623     
6624     /**
6625      * Hide this button
6626      */
6627     hide: function(){
6628         this.hidden = true;
6629         if(this.el){
6630             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6631         }
6632     },
6633     
6634     /**
6635      * Convenience function for boolean show/hide
6636      * @param {Boolean} visible True to show, false to hide
6637      */
6638     setVisible: function(visible){
6639         if(visible) {
6640             this.show();
6641         }else{
6642             this.hide();
6643         }
6644     },
6645     
6646     /**
6647      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6648      * @param {Boolean} state (optional) Force a particular state
6649      */
6650     toggle : function(state){
6651         state = state === undefined ? !this.pressed : state;
6652         if(state != this.pressed){
6653             if(state){
6654                 this.el.addClass("x-btn-pressed");
6655                 this.pressed = true;
6656                 this.fireEvent("toggle", this, true);
6657             }else{
6658                 this.el.removeClass("x-btn-pressed");
6659                 this.pressed = false;
6660                 this.fireEvent("toggle", this, false);
6661             }
6662             if(this.toggleHandler){
6663                 this.toggleHandler.call(this.scope || this, this, state);
6664             }
6665         }
6666     },
6667     
6668     /**
6669      * Focus the button
6670      */
6671     focus : function(){
6672         this.el.child('button:first').focus();
6673     },
6674     
6675     /**
6676      * Disable this button
6677      */
6678     disable : function(){
6679         if(this.el){
6680             this.el.addClass("x-btn-disabled");
6681         }
6682         this.disabled = true;
6683     },
6684     
6685     /**
6686      * Enable this button
6687      */
6688     enable : function(){
6689         if(this.el){
6690             this.el.removeClass("x-btn-disabled");
6691         }
6692         this.disabled = false;
6693     },
6694
6695     /**
6696      * Convenience function for boolean enable/disable
6697      * @param {Boolean} enabled True to enable, false to disable
6698      */
6699     setDisabled : function(v){
6700         this[v !== true ? "enable" : "disable"]();
6701     },
6702
6703     // private
6704     onClick : function(e)
6705     {
6706         if(e){
6707             e.preventDefault();
6708         }
6709         if(e.button != 0){
6710             return;
6711         }
6712         if(!this.disabled){
6713             if(this.enableToggle){
6714                 this.toggle();
6715             }
6716             if(this.menu && !this.menu.isVisible()){
6717                 this.menu.show(this.el, this.menuAlign);
6718             }
6719             this.fireEvent("click", this, e);
6720             if(this.handler){
6721                 this.el.removeClass("x-btn-over");
6722                 this.handler.call(this.scope || this, this, e);
6723             }
6724         }
6725     },
6726     // private
6727     onMouseOver : function(e){
6728         if(!this.disabled){
6729             this.el.addClass("x-btn-over");
6730             this.fireEvent('mouseover', this, e);
6731         }
6732     },
6733     // private
6734     onMouseOut : function(e){
6735         if(!e.within(this.el,  true)){
6736             this.el.removeClass("x-btn-over");
6737             this.fireEvent('mouseout', this, e);
6738         }
6739     },
6740     // private
6741     onFocus : function(e){
6742         if(!this.disabled){
6743             this.el.addClass("x-btn-focus");
6744         }
6745     },
6746     // private
6747     onBlur : function(e){
6748         this.el.removeClass("x-btn-focus");
6749     },
6750     // private
6751     onMouseDown : function(e){
6752         if(!this.disabled && e.button == 0){
6753             this.el.addClass("x-btn-click");
6754             Roo.get(document).on('mouseup', this.onMouseUp, this);
6755         }
6756     },
6757     // private
6758     onMouseUp : function(e){
6759         if(e.button == 0){
6760             this.el.removeClass("x-btn-click");
6761             Roo.get(document).un('mouseup', this.onMouseUp, this);
6762         }
6763     },
6764     // private
6765     onMenuShow : function(e){
6766         this.el.addClass("x-btn-menu-active");
6767     },
6768     // private
6769     onMenuHide : function(e){
6770         this.el.removeClass("x-btn-menu-active");
6771     }   
6772 });
6773
6774 // Private utility class used by Button
6775 Roo.ButtonToggleMgr = function(){
6776    var groups = {};
6777    
6778    function toggleGroup(btn, state){
6779        if(state){
6780            var g = groups[btn.toggleGroup];
6781            for(var i = 0, l = g.length; i < l; i++){
6782                if(g[i] != btn){
6783                    g[i].toggle(false);
6784                }
6785            }
6786        }
6787    }
6788    
6789    return {
6790        register : function(btn){
6791            if(!btn.toggleGroup){
6792                return;
6793            }
6794            var g = groups[btn.toggleGroup];
6795            if(!g){
6796                g = groups[btn.toggleGroup] = [];
6797            }
6798            g.push(btn);
6799            btn.on("toggle", toggleGroup);
6800        },
6801        
6802        unregister : function(btn){
6803            if(!btn.toggleGroup){
6804                return;
6805            }
6806            var g = groups[btn.toggleGroup];
6807            if(g){
6808                g.remove(btn);
6809                btn.un("toggle", toggleGroup);
6810            }
6811        }
6812    };
6813 }();/*
6814  * Based on:
6815  * Ext JS Library 1.1.1
6816  * Copyright(c) 2006-2007, Ext JS, LLC.
6817  *
6818  * Originally Released Under LGPL - original licence link has changed is not relivant.
6819  *
6820  * Fork - LGPL
6821  * <script type="text/javascript">
6822  */
6823  
6824 /**
6825  * @class Roo.SplitButton
6826  * @extends Roo.Button
6827  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6828  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6829  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6830  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6831  * @cfg {String} arrowTooltip The title attribute of the arrow
6832  * @constructor
6833  * Create a new menu button
6834  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6835  * @param {Object} config The config object
6836  */
6837 Roo.SplitButton = function(renderTo, config){
6838     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6839     /**
6840      * @event arrowclick
6841      * Fires when this button's arrow is clicked
6842      * @param {SplitButton} this
6843      * @param {EventObject} e The click event
6844      */
6845     this.addEvents({"arrowclick":true});
6846 };
6847
6848 Roo.extend(Roo.SplitButton, Roo.Button, {
6849     render : function(renderTo){
6850         // this is one sweet looking template!
6851         var tpl = new Roo.Template(
6852             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6853             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6854             '<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>',
6855             "</tbody></table></td><td>",
6856             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6857             '<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>',
6858             "</tbody></table></td></tr></table>"
6859         );
6860         var btn = tpl.append(renderTo, [this.text, this.type], true);
6861         var btnEl = btn.child("button");
6862         if(this.cls){
6863             btn.addClass(this.cls);
6864         }
6865         if(this.icon){
6866             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6867         }
6868         if(this.iconCls){
6869             btnEl.addClass(this.iconCls);
6870             if(!this.cls){
6871                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6872             }
6873         }
6874         this.el = btn;
6875         if(this.handleMouseEvents){
6876             btn.on("mouseover", this.onMouseOver, this);
6877             btn.on("mouseout", this.onMouseOut, this);
6878             btn.on("mousedown", this.onMouseDown, this);
6879             btn.on("mouseup", this.onMouseUp, this);
6880         }
6881         btn.on(this.clickEvent, this.onClick, this);
6882         if(this.tooltip){
6883             if(typeof this.tooltip == 'object'){
6884                 Roo.QuickTips.tips(Roo.apply({
6885                       target: btnEl.id
6886                 }, this.tooltip));
6887             } else {
6888                 btnEl.dom[this.tooltipType] = this.tooltip;
6889             }
6890         }
6891         if(this.arrowTooltip){
6892             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6893         }
6894         if(this.hidden){
6895             this.hide();
6896         }
6897         if(this.disabled){
6898             this.disable();
6899         }
6900         if(this.pressed){
6901             this.el.addClass("x-btn-pressed");
6902         }
6903         if(Roo.isIE && !Roo.isIE7){
6904             this.autoWidth.defer(1, this);
6905         }else{
6906             this.autoWidth();
6907         }
6908         if(this.menu){
6909             this.menu.on("show", this.onMenuShow, this);
6910             this.menu.on("hide", this.onMenuHide, this);
6911         }
6912         this.fireEvent('render', this);
6913     },
6914
6915     // private
6916     autoWidth : function(){
6917         if(this.el){
6918             var tbl = this.el.child("table:first");
6919             var tbl2 = this.el.child("table:last");
6920             this.el.setWidth("auto");
6921             tbl.setWidth("auto");
6922             if(Roo.isIE7 && Roo.isStrict){
6923                 var ib = this.el.child('button:first');
6924                 if(ib && ib.getWidth() > 20){
6925                     ib.clip();
6926                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6927                 }
6928             }
6929             if(this.minWidth){
6930                 if(this.hidden){
6931                     this.el.beginMeasure();
6932                 }
6933                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6934                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6935                 }
6936                 if(this.hidden){
6937                     this.el.endMeasure();
6938                 }
6939             }
6940             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6941         } 
6942     },
6943     /**
6944      * Sets this button's click handler
6945      * @param {Function} handler The function to call when the button is clicked
6946      * @param {Object} scope (optional) Scope for the function passed above
6947      */
6948     setHandler : function(handler, scope){
6949         this.handler = handler;
6950         this.scope = scope;  
6951     },
6952     
6953     /**
6954      * Sets this button's arrow click handler
6955      * @param {Function} handler The function to call when the arrow is clicked
6956      * @param {Object} scope (optional) Scope for the function passed above
6957      */
6958     setArrowHandler : function(handler, scope){
6959         this.arrowHandler = handler;
6960         this.scope = scope;  
6961     },
6962     
6963     /**
6964      * Focus the button
6965      */
6966     focus : function(){
6967         if(this.el){
6968             this.el.child("button:first").focus();
6969         }
6970     },
6971
6972     // private
6973     onClick : function(e){
6974         e.preventDefault();
6975         if(!this.disabled){
6976             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6977                 if(this.menu && !this.menu.isVisible()){
6978                     this.menu.show(this.el, this.menuAlign);
6979                 }
6980                 this.fireEvent("arrowclick", this, e);
6981                 if(this.arrowHandler){
6982                     this.arrowHandler.call(this.scope || this, this, e);
6983                 }
6984             }else{
6985                 this.fireEvent("click", this, e);
6986                 if(this.handler){
6987                     this.handler.call(this.scope || this, this, e);
6988                 }
6989             }
6990         }
6991     },
6992     // private
6993     onMouseDown : function(e){
6994         if(!this.disabled){
6995             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6996         }
6997     },
6998     // private
6999     onMouseUp : function(e){
7000         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7001     }   
7002 });
7003
7004
7005 // backwards compat
7006 Roo.MenuButton = Roo.SplitButton;/*
7007  * Based on:
7008  * Ext JS Library 1.1.1
7009  * Copyright(c) 2006-2007, Ext JS, LLC.
7010  *
7011  * Originally Released Under LGPL - original licence link has changed is not relivant.
7012  *
7013  * Fork - LGPL
7014  * <script type="text/javascript">
7015  */
7016
7017 /**
7018  * @class Roo.Toolbar
7019  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7020  * Basic Toolbar class.
7021  * @constructor
7022  * Creates a new Toolbar
7023  * @param {Object} container The config object
7024  */ 
7025 Roo.Toolbar = function(container, buttons, config)
7026 {
7027     /// old consturctor format still supported..
7028     if(container instanceof Array){ // omit the container for later rendering
7029         buttons = container;
7030         config = buttons;
7031         container = null;
7032     }
7033     if (typeof(container) == 'object' && container.xtype) {
7034         config = container;
7035         container = config.container;
7036         buttons = config.buttons || []; // not really - use items!!
7037     }
7038     var xitems = [];
7039     if (config && config.items) {
7040         xitems = config.items;
7041         delete config.items;
7042     }
7043     Roo.apply(this, config);
7044     this.buttons = buttons;
7045     
7046     if(container){
7047         this.render(container);
7048     }
7049     this.xitems = xitems;
7050     Roo.each(xitems, function(b) {
7051         this.add(b);
7052     }, this);
7053     
7054 };
7055
7056 Roo.Toolbar.prototype = {
7057     /**
7058      * @cfg {Array} items
7059      * array of button configs or elements to add (will be converted to a MixedCollection)
7060      */
7061     items: false,
7062     /**
7063      * @cfg {String/HTMLElement/Element} container
7064      * The id or element that will contain the toolbar
7065      */
7066     // private
7067     render : function(ct){
7068         this.el = Roo.get(ct);
7069         if(this.cls){
7070             this.el.addClass(this.cls);
7071         }
7072         // using a table allows for vertical alignment
7073         // 100% width is needed by Safari...
7074         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7075         this.tr = this.el.child("tr", true);
7076         var autoId = 0;
7077         this.items = new Roo.util.MixedCollection(false, function(o){
7078             return o.id || ("item" + (++autoId));
7079         });
7080         if(this.buttons){
7081             this.add.apply(this, this.buttons);
7082             delete this.buttons;
7083         }
7084     },
7085
7086     /**
7087      * Adds element(s) to the toolbar -- this function takes a variable number of 
7088      * arguments of mixed type and adds them to the toolbar.
7089      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7090      * <ul>
7091      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7092      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7093      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7094      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7095      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7096      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7097      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7098      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7099      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7100      * </ul>
7101      * @param {Mixed} arg2
7102      * @param {Mixed} etc.
7103      */
7104     add : function(){
7105         var a = arguments, l = a.length;
7106         for(var i = 0; i < l; i++){
7107             this._add(a[i]);
7108         }
7109     },
7110     // private..
7111     _add : function(el) {
7112         
7113         if (el.xtype) {
7114             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7115         }
7116         
7117         if (el.applyTo){ // some kind of form field
7118             return this.addField(el);
7119         } 
7120         if (el.render){ // some kind of Toolbar.Item
7121             return this.addItem(el);
7122         }
7123         if (typeof el == "string"){ // string
7124             if(el == "separator" || el == "-"){
7125                 return this.addSeparator();
7126             }
7127             if (el == " "){
7128                 return this.addSpacer();
7129             }
7130             if(el == "->"){
7131                 return this.addFill();
7132             }
7133             return this.addText(el);
7134             
7135         }
7136         if(el.tagName){ // element
7137             return this.addElement(el);
7138         }
7139         if(typeof el == "object"){ // must be button config?
7140             return this.addButton(el);
7141         }
7142         // and now what?!?!
7143         return false;
7144         
7145     },
7146     
7147     /**
7148      * Add an Xtype element
7149      * @param {Object} xtype Xtype Object
7150      * @return {Object} created Object
7151      */
7152     addxtype : function(e){
7153         return this.add(e);  
7154     },
7155     
7156     /**
7157      * Returns the Element for this toolbar.
7158      * @return {Roo.Element}
7159      */
7160     getEl : function(){
7161         return this.el;  
7162     },
7163     
7164     /**
7165      * Adds a separator
7166      * @return {Roo.Toolbar.Item} The separator item
7167      */
7168     addSeparator : function(){
7169         return this.addItem(new Roo.Toolbar.Separator());
7170     },
7171
7172     /**
7173      * Adds a spacer element
7174      * @return {Roo.Toolbar.Spacer} The spacer item
7175      */
7176     addSpacer : function(){
7177         return this.addItem(new Roo.Toolbar.Spacer());
7178     },
7179
7180     /**
7181      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7182      * @return {Roo.Toolbar.Fill} The fill item
7183      */
7184     addFill : function(){
7185         return this.addItem(new Roo.Toolbar.Fill());
7186     },
7187
7188     /**
7189      * Adds any standard HTML element to the toolbar
7190      * @param {String/HTMLElement/Element} el The element or id of the element to add
7191      * @return {Roo.Toolbar.Item} The element's item
7192      */
7193     addElement : function(el){
7194         return this.addItem(new Roo.Toolbar.Item(el));
7195     },
7196     /**
7197      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7198      * @type Roo.util.MixedCollection  
7199      */
7200     items : false,
7201      
7202     /**
7203      * Adds any Toolbar.Item or subclass
7204      * @param {Roo.Toolbar.Item} item
7205      * @return {Roo.Toolbar.Item} The item
7206      */
7207     addItem : function(item){
7208         var td = this.nextBlock();
7209         item.render(td);
7210         this.items.add(item);
7211         return item;
7212     },
7213     
7214     /**
7215      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7216      * @param {Object/Array} config A button config or array of configs
7217      * @return {Roo.Toolbar.Button/Array}
7218      */
7219     addButton : function(config){
7220         if(config instanceof Array){
7221             var buttons = [];
7222             for(var i = 0, len = config.length; i < len; i++) {
7223                 buttons.push(this.addButton(config[i]));
7224             }
7225             return buttons;
7226         }
7227         var b = config;
7228         if(!(config instanceof Roo.Toolbar.Button)){
7229             b = config.split ?
7230                 new Roo.Toolbar.SplitButton(config) :
7231                 new Roo.Toolbar.Button(config);
7232         }
7233         var td = this.nextBlock();
7234         b.render(td);
7235         this.items.add(b);
7236         return b;
7237     },
7238     
7239     /**
7240      * Adds text to the toolbar
7241      * @param {String} text The text to add
7242      * @return {Roo.Toolbar.Item} The element's item
7243      */
7244     addText : function(text){
7245         return this.addItem(new Roo.Toolbar.TextItem(text));
7246     },
7247     
7248     /**
7249      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7250      * @param {Number} index The index where the item is to be inserted
7251      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7252      * @return {Roo.Toolbar.Button/Item}
7253      */
7254     insertButton : function(index, item){
7255         if(item instanceof Array){
7256             var buttons = [];
7257             for(var i = 0, len = item.length; i < len; i++) {
7258                buttons.push(this.insertButton(index + i, item[i]));
7259             }
7260             return buttons;
7261         }
7262         if (!(item instanceof Roo.Toolbar.Button)){
7263            item = new Roo.Toolbar.Button(item);
7264         }
7265         var td = document.createElement("td");
7266         this.tr.insertBefore(td, this.tr.childNodes[index]);
7267         item.render(td);
7268         this.items.insert(index, item);
7269         return item;
7270     },
7271     
7272     /**
7273      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7274      * @param {Object} config
7275      * @return {Roo.Toolbar.Item} The element's item
7276      */
7277     addDom : function(config, returnEl){
7278         var td = this.nextBlock();
7279         Roo.DomHelper.overwrite(td, config);
7280         var ti = new Roo.Toolbar.Item(td.firstChild);
7281         ti.render(td);
7282         this.items.add(ti);
7283         return ti;
7284     },
7285
7286     /**
7287      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7288      * @type Roo.util.MixedCollection  
7289      */
7290     fields : false,
7291     
7292     /**
7293      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7294      * Note: the field should not have been rendered yet. For a field that has already been
7295      * rendered, use {@link #addElement}.
7296      * @param {Roo.form.Field} field
7297      * @return {Roo.ToolbarItem}
7298      */
7299      
7300       
7301     addField : function(field) {
7302         if (!this.fields) {
7303             var autoId = 0;
7304             this.fields = new Roo.util.MixedCollection(false, function(o){
7305                 return o.id || ("item" + (++autoId));
7306             });
7307
7308         }
7309         
7310         var td = this.nextBlock();
7311         field.render(td);
7312         var ti = new Roo.Toolbar.Item(td.firstChild);
7313         ti.render(td);
7314         this.items.add(ti);
7315         this.fields.add(field);
7316         return ti;
7317     },
7318     /**
7319      * Hide the toolbar
7320      * @method hide
7321      */
7322      
7323       
7324     hide : function()
7325     {
7326         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7327         this.el.child('div').hide();
7328     },
7329     /**
7330      * Show the toolbar
7331      * @method show
7332      */
7333     show : function()
7334     {
7335         this.el.child('div').show();
7336     },
7337       
7338     // private
7339     nextBlock : function(){
7340         var td = document.createElement("td");
7341         this.tr.appendChild(td);
7342         return td;
7343     },
7344
7345     // private
7346     destroy : function(){
7347         if(this.items){ // rendered?
7348             Roo.destroy.apply(Roo, this.items.items);
7349         }
7350         if(this.fields){ // rendered?
7351             Roo.destroy.apply(Roo, this.fields.items);
7352         }
7353         Roo.Element.uncache(this.el, this.tr);
7354     }
7355 };
7356
7357 /**
7358  * @class Roo.Toolbar.Item
7359  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7360  * @constructor
7361  * Creates a new Item
7362  * @param {HTMLElement} el 
7363  */
7364 Roo.Toolbar.Item = function(el){
7365     var cfg = {};
7366     if (typeof (el.xtype) != 'undefined') {
7367         cfg = el;
7368         el = cfg.el;
7369     }
7370     
7371     this.el = Roo.getDom(el);
7372     this.id = Roo.id(this.el);
7373     this.hidden = false;
7374     
7375     this.addEvents({
7376          /**
7377              * @event render
7378              * Fires when the button is rendered
7379              * @param {Button} this
7380              */
7381         'render': true
7382     });
7383     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7384 };
7385 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7386 //Roo.Toolbar.Item.prototype = {
7387     
7388     /**
7389      * Get this item's HTML Element
7390      * @return {HTMLElement}
7391      */
7392     getEl : function(){
7393        return this.el;  
7394     },
7395
7396     // private
7397     render : function(td){
7398         
7399          this.td = td;
7400         td.appendChild(this.el);
7401         
7402         this.fireEvent('render', this);
7403     },
7404     
7405     /**
7406      * Removes and destroys this item.
7407      */
7408     destroy : function(){
7409         this.td.parentNode.removeChild(this.td);
7410     },
7411     
7412     /**
7413      * Shows this item.
7414      */
7415     show: function(){
7416         this.hidden = false;
7417         this.td.style.display = "";
7418     },
7419     
7420     /**
7421      * Hides this item.
7422      */
7423     hide: function(){
7424         this.hidden = true;
7425         this.td.style.display = "none";
7426     },
7427     
7428     /**
7429      * Convenience function for boolean show/hide.
7430      * @param {Boolean} visible true to show/false to hide
7431      */
7432     setVisible: function(visible){
7433         if(visible) {
7434             this.show();
7435         }else{
7436             this.hide();
7437         }
7438     },
7439     
7440     /**
7441      * Try to focus this item.
7442      */
7443     focus : function(){
7444         Roo.fly(this.el).focus();
7445     },
7446     
7447     /**
7448      * Disables this item.
7449      */
7450     disable : function(){
7451         Roo.fly(this.td).addClass("x-item-disabled");
7452         this.disabled = true;
7453         this.el.disabled = true;
7454     },
7455     
7456     /**
7457      * Enables this item.
7458      */
7459     enable : function(){
7460         Roo.fly(this.td).removeClass("x-item-disabled");
7461         this.disabled = false;
7462         this.el.disabled = false;
7463     }
7464 });
7465
7466
7467 /**
7468  * @class Roo.Toolbar.Separator
7469  * @extends Roo.Toolbar.Item
7470  * A simple toolbar separator class
7471  * @constructor
7472  * Creates a new Separator
7473  */
7474 Roo.Toolbar.Separator = function(cfg){
7475     
7476     var s = document.createElement("span");
7477     s.className = "ytb-sep";
7478     if (cfg) {
7479         cfg.el = s;
7480     }
7481     
7482     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7483 };
7484 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7485     enable:Roo.emptyFn,
7486     disable:Roo.emptyFn,
7487     focus:Roo.emptyFn
7488 });
7489
7490 /**
7491  * @class Roo.Toolbar.Spacer
7492  * @extends Roo.Toolbar.Item
7493  * A simple element that adds extra horizontal space to a toolbar.
7494  * @constructor
7495  * Creates a new Spacer
7496  */
7497 Roo.Toolbar.Spacer = function(cfg){
7498     var s = document.createElement("div");
7499     s.className = "ytb-spacer";
7500     if (cfg) {
7501         cfg.el = s;
7502     }
7503     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7504 };
7505 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7506     enable:Roo.emptyFn,
7507     disable:Roo.emptyFn,
7508     focus:Roo.emptyFn
7509 });
7510
7511 /**
7512  * @class Roo.Toolbar.Fill
7513  * @extends Roo.Toolbar.Spacer
7514  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7515  * @constructor
7516  * Creates a new Spacer
7517  */
7518 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7519     // private
7520     render : function(td){
7521         td.style.width = '100%';
7522         Roo.Toolbar.Fill.superclass.render.call(this, td);
7523     }
7524 });
7525
7526 /**
7527  * @class Roo.Toolbar.TextItem
7528  * @extends Roo.Toolbar.Item
7529  * A simple class that renders text directly into a toolbar.
7530  * @constructor
7531  * Creates a new TextItem
7532  * @cfg {string} text 
7533  */
7534 Roo.Toolbar.TextItem = function(cfg){
7535     var  text = cfg || "";
7536     if (typeof(cfg) == 'object') {
7537         text = cfg.text || "";
7538     }  else {
7539         cfg = null;
7540     }
7541     var s = document.createElement("span");
7542     s.className = "ytb-text";
7543     s.innerHTML = text;
7544     if (cfg) {
7545         cfg.el  = s;
7546     }
7547     
7548     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7549 };
7550 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7551     
7552      
7553     enable:Roo.emptyFn,
7554     disable:Roo.emptyFn,
7555     focus:Roo.emptyFn,
7556      /**
7557      * Shows this button
7558      */
7559     show: function(){
7560         this.hidden = false;
7561         this.el.style.display = "";
7562     },
7563     
7564     /**
7565      * Hides this button
7566      */
7567     hide: function(){
7568         this.hidden = true;
7569         this.el.style.display = "none";
7570     }
7571     
7572 });
7573
7574 /**
7575  * @class Roo.Toolbar.Button
7576  * @extends Roo.Button
7577  * A button that renders into a toolbar.
7578  * @constructor
7579  * Creates a new Button
7580  * @param {Object} config A standard {@link Roo.Button} config object
7581  */
7582 Roo.Toolbar.Button = function(config){
7583     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7584 };
7585 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7586 {
7587     
7588     
7589     render : function(td){
7590         this.td = td;
7591         Roo.Toolbar.Button.superclass.render.call(this, td);
7592     },
7593     
7594     /**
7595      * Removes and destroys this button
7596      */
7597     destroy : function(){
7598         Roo.Toolbar.Button.superclass.destroy.call(this);
7599         this.td.parentNode.removeChild(this.td);
7600     },
7601     
7602     /**
7603      * Shows this button
7604      */
7605     show: function(){
7606         this.hidden = false;
7607         this.td.style.display = "";
7608     },
7609     
7610     /**
7611      * Hides this button
7612      */
7613     hide: function(){
7614         this.hidden = true;
7615         this.td.style.display = "none";
7616     },
7617
7618     /**
7619      * Disables this item
7620      */
7621     disable : function(){
7622         Roo.fly(this.td).addClass("x-item-disabled");
7623         this.disabled = true;
7624     },
7625
7626     /**
7627      * Enables this item
7628      */
7629     enable : function(){
7630         Roo.fly(this.td).removeClass("x-item-disabled");
7631         this.disabled = false;
7632     }
7633 });
7634 // backwards compat
7635 Roo.ToolbarButton = Roo.Toolbar.Button;
7636
7637 /**
7638  * @class Roo.Toolbar.SplitButton
7639  * @extends Roo.SplitButton
7640  * A menu button that renders into a toolbar.
7641  * @constructor
7642  * Creates a new SplitButton
7643  * @param {Object} config A standard {@link Roo.SplitButton} config object
7644  */
7645 Roo.Toolbar.SplitButton = function(config){
7646     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7647 };
7648 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7649     render : function(td){
7650         this.td = td;
7651         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7652     },
7653     
7654     /**
7655      * Removes and destroys this button
7656      */
7657     destroy : function(){
7658         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7659         this.td.parentNode.removeChild(this.td);
7660     },
7661     
7662     /**
7663      * Shows this button
7664      */
7665     show: function(){
7666         this.hidden = false;
7667         this.td.style.display = "";
7668     },
7669     
7670     /**
7671      * Hides this button
7672      */
7673     hide: function(){
7674         this.hidden = true;
7675         this.td.style.display = "none";
7676     }
7677 });
7678
7679 // backwards compat
7680 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7681  * Based on:
7682  * Ext JS Library 1.1.1
7683  * Copyright(c) 2006-2007, Ext JS, LLC.
7684  *
7685  * Originally Released Under LGPL - original licence link has changed is not relivant.
7686  *
7687  * Fork - LGPL
7688  * <script type="text/javascript">
7689  */
7690  
7691 /**
7692  * @class Roo.PagingToolbar
7693  * @extends Roo.Toolbar
7694  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7695  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7696  * @constructor
7697  * Create a new PagingToolbar
7698  * @param {Object} config The config object
7699  */
7700 Roo.PagingToolbar = function(el, ds, config)
7701 {
7702     // old args format still supported... - xtype is prefered..
7703     if (typeof(el) == 'object' && el.xtype) {
7704         // created from xtype...
7705         config = el;
7706         ds = el.dataSource;
7707         el = config.container;
7708     }
7709     var items = [];
7710     if (config.items) {
7711         items = config.items;
7712         config.items = [];
7713     }
7714     
7715     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7716     this.ds = ds;
7717     this.cursor = 0;
7718     this.renderButtons(this.el);
7719     this.bind(ds);
7720     
7721     // supprot items array.
7722    
7723     Roo.each(items, function(e) {
7724         this.add(Roo.factory(e));
7725     },this);
7726     
7727 };
7728
7729 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7730    
7731     /**
7732      * @cfg {String/HTMLElement/Element} container
7733      * container The id or element that will contain the toolbar
7734      */
7735     /**
7736      * @cfg {Boolean} displayInfo
7737      * True to display the displayMsg (defaults to false)
7738      */
7739     
7740     
7741     /**
7742      * @cfg {Number} pageSize
7743      * The number of records to display per page (defaults to 20)
7744      */
7745     pageSize: 20,
7746     /**
7747      * @cfg {String} displayMsg
7748      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7749      */
7750     displayMsg : 'Displaying {0} - {1} of {2}',
7751     /**
7752      * @cfg {String} emptyMsg
7753      * The message to display when no records are found (defaults to "No data to display")
7754      */
7755     emptyMsg : 'No data to display',
7756     /**
7757      * Customizable piece of the default paging text (defaults to "Page")
7758      * @type String
7759      */
7760     beforePageText : "Page",
7761     /**
7762      * Customizable piece of the default paging text (defaults to "of %0")
7763      * @type String
7764      */
7765     afterPageText : "of {0}",
7766     /**
7767      * Customizable piece of the default paging text (defaults to "First Page")
7768      * @type String
7769      */
7770     firstText : "First Page",
7771     /**
7772      * Customizable piece of the default paging text (defaults to "Previous Page")
7773      * @type String
7774      */
7775     prevText : "Previous Page",
7776     /**
7777      * Customizable piece of the default paging text (defaults to "Next Page")
7778      * @type String
7779      */
7780     nextText : "Next Page",
7781     /**
7782      * Customizable piece of the default paging text (defaults to "Last Page")
7783      * @type String
7784      */
7785     lastText : "Last Page",
7786     /**
7787      * Customizable piece of the default paging text (defaults to "Refresh")
7788      * @type String
7789      */
7790     refreshText : "Refresh",
7791
7792     // private
7793     renderButtons : function(el){
7794         Roo.PagingToolbar.superclass.render.call(this, el);
7795         this.first = this.addButton({
7796             tooltip: this.firstText,
7797             cls: "x-btn-icon x-grid-page-first",
7798             disabled: true,
7799             handler: this.onClick.createDelegate(this, ["first"])
7800         });
7801         this.prev = this.addButton({
7802             tooltip: this.prevText,
7803             cls: "x-btn-icon x-grid-page-prev",
7804             disabled: true,
7805             handler: this.onClick.createDelegate(this, ["prev"])
7806         });
7807         //this.addSeparator();
7808         this.add(this.beforePageText);
7809         this.field = Roo.get(this.addDom({
7810            tag: "input",
7811            type: "text",
7812            size: "3",
7813            value: "1",
7814            cls: "x-grid-page-number"
7815         }).el);
7816         this.field.on("keydown", this.onPagingKeydown, this);
7817         this.field.on("focus", function(){this.dom.select();});
7818         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7819         this.field.setHeight(18);
7820         //this.addSeparator();
7821         this.next = this.addButton({
7822             tooltip: this.nextText,
7823             cls: "x-btn-icon x-grid-page-next",
7824             disabled: true,
7825             handler: this.onClick.createDelegate(this, ["next"])
7826         });
7827         this.last = this.addButton({
7828             tooltip: this.lastText,
7829             cls: "x-btn-icon x-grid-page-last",
7830             disabled: true,
7831             handler: this.onClick.createDelegate(this, ["last"])
7832         });
7833         //this.addSeparator();
7834         this.loading = this.addButton({
7835             tooltip: this.refreshText,
7836             cls: "x-btn-icon x-grid-loading",
7837             handler: this.onClick.createDelegate(this, ["refresh"])
7838         });
7839
7840         if(this.displayInfo){
7841             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7842         }
7843     },
7844
7845     // private
7846     updateInfo : function(){
7847         if(this.displayEl){
7848             var count = this.ds.getCount();
7849             var msg = count == 0 ?
7850                 this.emptyMsg :
7851                 String.format(
7852                     this.displayMsg,
7853                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7854                 );
7855             this.displayEl.update(msg);
7856         }
7857     },
7858
7859     // private
7860     onLoad : function(ds, r, o){
7861        this.cursor = o.params ? o.params.start : 0;
7862        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7863
7864        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7865        this.field.dom.value = ap;
7866        this.first.setDisabled(ap == 1);
7867        this.prev.setDisabled(ap == 1);
7868        this.next.setDisabled(ap == ps);
7869        this.last.setDisabled(ap == ps);
7870        this.loading.enable();
7871        this.updateInfo();
7872     },
7873
7874     // private
7875     getPageData : function(){
7876         var total = this.ds.getTotalCount();
7877         return {
7878             total : total,
7879             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7880             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7881         };
7882     },
7883
7884     // private
7885     onLoadError : function(){
7886         this.loading.enable();
7887     },
7888
7889     // private
7890     onPagingKeydown : function(e){
7891         var k = e.getKey();
7892         var d = this.getPageData();
7893         if(k == e.RETURN){
7894             var v = this.field.dom.value, pageNum;
7895             if(!v || isNaN(pageNum = parseInt(v, 10))){
7896                 this.field.dom.value = d.activePage;
7897                 return;
7898             }
7899             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7900             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7901             e.stopEvent();
7902         }
7903         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))
7904         {
7905           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7906           this.field.dom.value = pageNum;
7907           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7908           e.stopEvent();
7909         }
7910         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7911         {
7912           var v = this.field.dom.value, pageNum; 
7913           var increment = (e.shiftKey) ? 10 : 1;
7914           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7915             increment *= -1;
7916           }
7917           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7918             this.field.dom.value = d.activePage;
7919             return;
7920           }
7921           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7922           {
7923             this.field.dom.value = parseInt(v, 10) + increment;
7924             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7925             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7926           }
7927           e.stopEvent();
7928         }
7929     },
7930
7931     // private
7932     beforeLoad : function(){
7933         if(this.loading){
7934             this.loading.disable();
7935         }
7936     },
7937
7938     // private
7939     onClick : function(which){
7940         var ds = this.ds;
7941         switch(which){
7942             case "first":
7943                 ds.load({params:{start: 0, limit: this.pageSize}});
7944             break;
7945             case "prev":
7946                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7947             break;
7948             case "next":
7949                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7950             break;
7951             case "last":
7952                 var total = ds.getTotalCount();
7953                 var extra = total % this.pageSize;
7954                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7955                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7956             break;
7957             case "refresh":
7958                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7959             break;
7960         }
7961     },
7962
7963     /**
7964      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7965      * @param {Roo.data.Store} store The data store to unbind
7966      */
7967     unbind : function(ds){
7968         ds.un("beforeload", this.beforeLoad, this);
7969         ds.un("load", this.onLoad, this);
7970         ds.un("loadexception", this.onLoadError, this);
7971         ds.un("remove", this.updateInfo, this);
7972         ds.un("add", this.updateInfo, this);
7973         this.ds = undefined;
7974     },
7975
7976     /**
7977      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7978      * @param {Roo.data.Store} store The data store to bind
7979      */
7980     bind : function(ds){
7981         ds.on("beforeload", this.beforeLoad, this);
7982         ds.on("load", this.onLoad, this);
7983         ds.on("loadexception", this.onLoadError, this);
7984         ds.on("remove", this.updateInfo, this);
7985         ds.on("add", this.updateInfo, this);
7986         this.ds = ds;
7987     }
7988 });/*
7989  * Based on:
7990  * Ext JS Library 1.1.1
7991  * Copyright(c) 2006-2007, Ext JS, LLC.
7992  *
7993  * Originally Released Under LGPL - original licence link has changed is not relivant.
7994  *
7995  * Fork - LGPL
7996  * <script type="text/javascript">
7997  */
7998
7999 /**
8000  * @class Roo.Resizable
8001  * @extends Roo.util.Observable
8002  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8003  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8004  * 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
8005  * the element will be wrapped for you automatically.</p>
8006  * <p>Here is the list of valid resize handles:</p>
8007  * <pre>
8008 Value   Description
8009 ------  -------------------
8010  'n'     north
8011  's'     south
8012  'e'     east
8013  'w'     west
8014  'nw'    northwest
8015  'sw'    southwest
8016  'se'    southeast
8017  'ne'    northeast
8018  'hd'    horizontal drag
8019  'all'   all
8020 </pre>
8021  * <p>Here's an example showing the creation of a typical Resizable:</p>
8022  * <pre><code>
8023 var resizer = new Roo.Resizable("element-id", {
8024     handles: 'all',
8025     minWidth: 200,
8026     minHeight: 100,
8027     maxWidth: 500,
8028     maxHeight: 400,
8029     pinned: true
8030 });
8031 resizer.on("resize", myHandler);
8032 </code></pre>
8033  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8034  * resizer.east.setDisplayed(false);</p>
8035  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8036  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8037  * resize operation's new size (defaults to [0, 0])
8038  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8039  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8040  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8041  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8042  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8043  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8044  * @cfg {Number} width The width of the element in pixels (defaults to null)
8045  * @cfg {Number} height The height of the element in pixels (defaults to null)
8046  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8047  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8048  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8049  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8050  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8051  * in favor of the handles config option (defaults to false)
8052  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8053  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8054  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8055  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8056  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8057  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8058  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8059  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8060  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8061  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8062  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8063  * @constructor
8064  * Create a new resizable component
8065  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8066  * @param {Object} config configuration options
8067   */
8068 Roo.Resizable = function(el, config)
8069 {
8070     this.el = Roo.get(el);
8071
8072     if(config && config.wrap){
8073         config.resizeChild = this.el;
8074         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8075         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8076         this.el.setStyle("overflow", "hidden");
8077         this.el.setPositioning(config.resizeChild.getPositioning());
8078         config.resizeChild.clearPositioning();
8079         if(!config.width || !config.height){
8080             var csize = config.resizeChild.getSize();
8081             this.el.setSize(csize.width, csize.height);
8082         }
8083         if(config.pinned && !config.adjustments){
8084             config.adjustments = "auto";
8085         }
8086     }
8087
8088     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8089     this.proxy.unselectable();
8090     this.proxy.enableDisplayMode('block');
8091
8092     Roo.apply(this, config);
8093
8094     if(this.pinned){
8095         this.disableTrackOver = true;
8096         this.el.addClass("x-resizable-pinned");
8097     }
8098     // if the element isn't positioned, make it relative
8099     var position = this.el.getStyle("position");
8100     if(position != "absolute" && position != "fixed"){
8101         this.el.setStyle("position", "relative");
8102     }
8103     if(!this.handles){ // no handles passed, must be legacy style
8104         this.handles = 's,e,se';
8105         if(this.multiDirectional){
8106             this.handles += ',n,w';
8107         }
8108     }
8109     if(this.handles == "all"){
8110         this.handles = "n s e w ne nw se sw";
8111     }
8112     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8113     var ps = Roo.Resizable.positions;
8114     for(var i = 0, len = hs.length; i < len; i++){
8115         if(hs[i] && ps[hs[i]]){
8116             var pos = ps[hs[i]];
8117             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8118         }
8119     }
8120     // legacy
8121     this.corner = this.southeast;
8122     
8123     // updateBox = the box can move..
8124     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8125         this.updateBox = true;
8126     }
8127
8128     this.activeHandle = null;
8129
8130     if(this.resizeChild){
8131         if(typeof this.resizeChild == "boolean"){
8132             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8133         }else{
8134             this.resizeChild = Roo.get(this.resizeChild, true);
8135         }
8136     }
8137     
8138     if(this.adjustments == "auto"){
8139         var rc = this.resizeChild;
8140         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8141         if(rc && (hw || hn)){
8142             rc.position("relative");
8143             rc.setLeft(hw ? hw.el.getWidth() : 0);
8144             rc.setTop(hn ? hn.el.getHeight() : 0);
8145         }
8146         this.adjustments = [
8147             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8148             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8149         ];
8150     }
8151
8152     if(this.draggable){
8153         this.dd = this.dynamic ?
8154             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8155         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8156     }
8157
8158     // public events
8159     this.addEvents({
8160         /**
8161          * @event beforeresize
8162          * Fired before resize is allowed. Set enabled to false to cancel resize.
8163          * @param {Roo.Resizable} this
8164          * @param {Roo.EventObject} e The mousedown event
8165          */
8166         "beforeresize" : true,
8167         /**
8168          * @event resizing
8169          * Fired a resizing.
8170          * @param {Roo.Resizable} this
8171          * @param {Number} x The new x position
8172          * @param {Number} y The new y position
8173          * @param {Number} w The new w width
8174          * @param {Number} h The new h hight
8175          * @param {Roo.EventObject} e The mouseup event
8176          */
8177         "resizing" : true,
8178         /**
8179          * @event resize
8180          * Fired after a resize.
8181          * @param {Roo.Resizable} this
8182          * @param {Number} width The new width
8183          * @param {Number} height The new height
8184          * @param {Roo.EventObject} e The mouseup event
8185          */
8186         "resize" : true
8187     });
8188
8189     if(this.width !== null && this.height !== null){
8190         this.resizeTo(this.width, this.height);
8191     }else{
8192         this.updateChildSize();
8193     }
8194     if(Roo.isIE){
8195         this.el.dom.style.zoom = 1;
8196     }
8197     Roo.Resizable.superclass.constructor.call(this);
8198 };
8199
8200 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8201         resizeChild : false,
8202         adjustments : [0, 0],
8203         minWidth : 5,
8204         minHeight : 5,
8205         maxWidth : 10000,
8206         maxHeight : 10000,
8207         enabled : true,
8208         animate : false,
8209         duration : .35,
8210         dynamic : false,
8211         handles : false,
8212         multiDirectional : false,
8213         disableTrackOver : false,
8214         easing : 'easeOutStrong',
8215         widthIncrement : 0,
8216         heightIncrement : 0,
8217         pinned : false,
8218         width : null,
8219         height : null,
8220         preserveRatio : false,
8221         transparent: false,
8222         minX: 0,
8223         minY: 0,
8224         draggable: false,
8225
8226         /**
8227          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8228          */
8229         constrainTo: undefined,
8230         /**
8231          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8232          */
8233         resizeRegion: undefined,
8234
8235
8236     /**
8237      * Perform a manual resize
8238      * @param {Number} width
8239      * @param {Number} height
8240      */
8241     resizeTo : function(width, height){
8242         this.el.setSize(width, height);
8243         this.updateChildSize();
8244         this.fireEvent("resize", this, width, height, null);
8245     },
8246
8247     // private
8248     startSizing : function(e, handle){
8249         this.fireEvent("beforeresize", this, e);
8250         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8251
8252             if(!this.overlay){
8253                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8254                 this.overlay.unselectable();
8255                 this.overlay.enableDisplayMode("block");
8256                 this.overlay.on("mousemove", this.onMouseMove, this);
8257                 this.overlay.on("mouseup", this.onMouseUp, this);
8258             }
8259             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8260
8261             this.resizing = true;
8262             this.startBox = this.el.getBox();
8263             this.startPoint = e.getXY();
8264             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8265                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8266
8267             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8268             this.overlay.show();
8269
8270             if(this.constrainTo) {
8271                 var ct = Roo.get(this.constrainTo);
8272                 this.resizeRegion = ct.getRegion().adjust(
8273                     ct.getFrameWidth('t'),
8274                     ct.getFrameWidth('l'),
8275                     -ct.getFrameWidth('b'),
8276                     -ct.getFrameWidth('r')
8277                 );
8278             }
8279
8280             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8281             this.proxy.show();
8282             this.proxy.setBox(this.startBox);
8283             if(!this.dynamic){
8284                 this.proxy.setStyle('visibility', 'visible');
8285             }
8286         }
8287     },
8288
8289     // private
8290     onMouseDown : function(handle, e){
8291         if(this.enabled){
8292             e.stopEvent();
8293             this.activeHandle = handle;
8294             this.startSizing(e, handle);
8295         }
8296     },
8297
8298     // private
8299     onMouseUp : function(e){
8300         var size = this.resizeElement();
8301         this.resizing = false;
8302         this.handleOut();
8303         this.overlay.hide();
8304         this.proxy.hide();
8305         this.fireEvent("resize", this, size.width, size.height, e);
8306     },
8307
8308     // private
8309     updateChildSize : function(){
8310         
8311         if(this.resizeChild){
8312             var el = this.el;
8313             var child = this.resizeChild;
8314             var adj = this.adjustments;
8315             if(el.dom.offsetWidth){
8316                 var b = el.getSize(true);
8317                 child.setSize(b.width+adj[0], b.height+adj[1]);
8318             }
8319             // Second call here for IE
8320             // The first call enables instant resizing and
8321             // the second call corrects scroll bars if they
8322             // exist
8323             if(Roo.isIE){
8324                 setTimeout(function(){
8325                     if(el.dom.offsetWidth){
8326                         var b = el.getSize(true);
8327                         child.setSize(b.width+adj[0], b.height+adj[1]);
8328                     }
8329                 }, 10);
8330             }
8331         }
8332     },
8333
8334     // private
8335     snap : function(value, inc, min){
8336         if(!inc || !value) {
8337             return value;
8338         }
8339         var newValue = value;
8340         var m = value % inc;
8341         if(m > 0){
8342             if(m > (inc/2)){
8343                 newValue = value + (inc-m);
8344             }else{
8345                 newValue = value - m;
8346             }
8347         }
8348         return Math.max(min, newValue);
8349     },
8350
8351     // private
8352     resizeElement : function(){
8353         var box = this.proxy.getBox();
8354         if(this.updateBox){
8355             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8356         }else{
8357             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8358         }
8359         this.updateChildSize();
8360         if(!this.dynamic){
8361             this.proxy.hide();
8362         }
8363         return box;
8364     },
8365
8366     // private
8367     constrain : function(v, diff, m, mx){
8368         if(v - diff < m){
8369             diff = v - m;
8370         }else if(v - diff > mx){
8371             diff = mx - v;
8372         }
8373         return diff;
8374     },
8375
8376     // private
8377     onMouseMove : function(e){
8378         
8379         if(this.enabled){
8380             try{// try catch so if something goes wrong the user doesn't get hung
8381
8382             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8383                 return;
8384             }
8385
8386             //var curXY = this.startPoint;
8387             var curSize = this.curSize || this.startBox;
8388             var x = this.startBox.x, y = this.startBox.y;
8389             var ox = x, oy = y;
8390             var w = curSize.width, h = curSize.height;
8391             var ow = w, oh = h;
8392             var mw = this.minWidth, mh = this.minHeight;
8393             var mxw = this.maxWidth, mxh = this.maxHeight;
8394             var wi = this.widthIncrement;
8395             var hi = this.heightIncrement;
8396
8397             var eventXY = e.getXY();
8398             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8399             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8400
8401             var pos = this.activeHandle.position;
8402
8403             switch(pos){
8404                 case "east":
8405                     w += diffX;
8406                     w = Math.min(Math.max(mw, w), mxw);
8407                     break;
8408              
8409                 case "south":
8410                     h += diffY;
8411                     h = Math.min(Math.max(mh, h), mxh);
8412                     break;
8413                 case "southeast":
8414                     w += diffX;
8415                     h += diffY;
8416                     w = Math.min(Math.max(mw, w), mxw);
8417                     h = Math.min(Math.max(mh, h), mxh);
8418                     break;
8419                 case "north":
8420                     diffY = this.constrain(h, diffY, mh, mxh);
8421                     y += diffY;
8422                     h -= diffY;
8423                     break;
8424                 case "hdrag":
8425                     
8426                     if (wi) {
8427                         var adiffX = Math.abs(diffX);
8428                         var sub = (adiffX % wi); // how much 
8429                         if (sub > (wi/2)) { // far enough to snap
8430                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8431                         } else {
8432                             // remove difference.. 
8433                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8434                         }
8435                     }
8436                     x += diffX;
8437                     x = Math.max(this.minX, x);
8438                     break;
8439                 case "west":
8440                     diffX = this.constrain(w, diffX, mw, mxw);
8441                     x += diffX;
8442                     w -= diffX;
8443                     break;
8444                 case "northeast":
8445                     w += diffX;
8446                     w = Math.min(Math.max(mw, w), mxw);
8447                     diffY = this.constrain(h, diffY, mh, mxh);
8448                     y += diffY;
8449                     h -= diffY;
8450                     break;
8451                 case "northwest":
8452                     diffX = this.constrain(w, diffX, mw, mxw);
8453                     diffY = this.constrain(h, diffY, mh, mxh);
8454                     y += diffY;
8455                     h -= diffY;
8456                     x += diffX;
8457                     w -= diffX;
8458                     break;
8459                case "southwest":
8460                     diffX = this.constrain(w, diffX, mw, mxw);
8461                     h += diffY;
8462                     h = Math.min(Math.max(mh, h), mxh);
8463                     x += diffX;
8464                     w -= diffX;
8465                     break;
8466             }
8467
8468             var sw = this.snap(w, wi, mw);
8469             var sh = this.snap(h, hi, mh);
8470             if(sw != w || sh != h){
8471                 switch(pos){
8472                     case "northeast":
8473                         y -= sh - h;
8474                     break;
8475                     case "north":
8476                         y -= sh - h;
8477                         break;
8478                     case "southwest":
8479                         x -= sw - w;
8480                     break;
8481                     case "west":
8482                         x -= sw - w;
8483                         break;
8484                     case "northwest":
8485                         x -= sw - w;
8486                         y -= sh - h;
8487                     break;
8488                 }
8489                 w = sw;
8490                 h = sh;
8491             }
8492
8493             if(this.preserveRatio){
8494                 switch(pos){
8495                     case "southeast":
8496                     case "east":
8497                         h = oh * (w/ow);
8498                         h = Math.min(Math.max(mh, h), mxh);
8499                         w = ow * (h/oh);
8500                        break;
8501                     case "south":
8502                         w = ow * (h/oh);
8503                         w = Math.min(Math.max(mw, w), mxw);
8504                         h = oh * (w/ow);
8505                         break;
8506                     case "northeast":
8507                         w = ow * (h/oh);
8508                         w = Math.min(Math.max(mw, w), mxw);
8509                         h = oh * (w/ow);
8510                     break;
8511                     case "north":
8512                         var tw = w;
8513                         w = ow * (h/oh);
8514                         w = Math.min(Math.max(mw, w), mxw);
8515                         h = oh * (w/ow);
8516                         x += (tw - w) / 2;
8517                         break;
8518                     case "southwest":
8519                         h = oh * (w/ow);
8520                         h = Math.min(Math.max(mh, h), mxh);
8521                         var tw = w;
8522                         w = ow * (h/oh);
8523                         x += tw - w;
8524                         break;
8525                     case "west":
8526                         var th = h;
8527                         h = oh * (w/ow);
8528                         h = Math.min(Math.max(mh, h), mxh);
8529                         y += (th - h) / 2;
8530                         var tw = w;
8531                         w = ow * (h/oh);
8532                         x += tw - w;
8533                        break;
8534                     case "northwest":
8535                         var tw = w;
8536                         var th = h;
8537                         h = oh * (w/ow);
8538                         h = Math.min(Math.max(mh, h), mxh);
8539                         w = ow * (h/oh);
8540                         y += th - h;
8541                         x += tw - w;
8542                        break;
8543
8544                 }
8545             }
8546             if (pos == 'hdrag') {
8547                 w = ow;
8548             }
8549             this.proxy.setBounds(x, y, w, h);
8550             if(this.dynamic){
8551                 this.resizeElement();
8552             }
8553             }catch(e){}
8554         }
8555         this.fireEvent("resizing", this, x, y, w, h, e);
8556     },
8557
8558     // private
8559     handleOver : function(){
8560         if(this.enabled){
8561             this.el.addClass("x-resizable-over");
8562         }
8563     },
8564
8565     // private
8566     handleOut : function(){
8567         if(!this.resizing){
8568             this.el.removeClass("x-resizable-over");
8569         }
8570     },
8571
8572     /**
8573      * Returns the element this component is bound to.
8574      * @return {Roo.Element}
8575      */
8576     getEl : function(){
8577         return this.el;
8578     },
8579
8580     /**
8581      * Returns the resizeChild element (or null).
8582      * @return {Roo.Element}
8583      */
8584     getResizeChild : function(){
8585         return this.resizeChild;
8586     },
8587     groupHandler : function()
8588     {
8589         
8590     },
8591     /**
8592      * Destroys this resizable. If the element was wrapped and
8593      * removeEl is not true then the element remains.
8594      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8595      */
8596     destroy : function(removeEl){
8597         this.proxy.remove();
8598         if(this.overlay){
8599             this.overlay.removeAllListeners();
8600             this.overlay.remove();
8601         }
8602         var ps = Roo.Resizable.positions;
8603         for(var k in ps){
8604             if(typeof ps[k] != "function" && this[ps[k]]){
8605                 var h = this[ps[k]];
8606                 h.el.removeAllListeners();
8607                 h.el.remove();
8608             }
8609         }
8610         if(removeEl){
8611             this.el.update("");
8612             this.el.remove();
8613         }
8614     }
8615 });
8616
8617 // private
8618 // hash to map config positions to true positions
8619 Roo.Resizable.positions = {
8620     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8621     hd: "hdrag"
8622 };
8623
8624 // private
8625 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8626     if(!this.tpl){
8627         // only initialize the template if resizable is used
8628         var tpl = Roo.DomHelper.createTemplate(
8629             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8630         );
8631         tpl.compile();
8632         Roo.Resizable.Handle.prototype.tpl = tpl;
8633     }
8634     this.position = pos;
8635     this.rz = rz;
8636     // show north drag fro topdra
8637     var handlepos = pos == 'hdrag' ? 'north' : pos;
8638     
8639     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8640     if (pos == 'hdrag') {
8641         this.el.setStyle('cursor', 'pointer');
8642     }
8643     this.el.unselectable();
8644     if(transparent){
8645         this.el.setOpacity(0);
8646     }
8647     this.el.on("mousedown", this.onMouseDown, this);
8648     if(!disableTrackOver){
8649         this.el.on("mouseover", this.onMouseOver, this);
8650         this.el.on("mouseout", this.onMouseOut, this);
8651     }
8652 };
8653
8654 // private
8655 Roo.Resizable.Handle.prototype = {
8656     afterResize : function(rz){
8657         Roo.log('after?');
8658         // do nothing
8659     },
8660     // private
8661     onMouseDown : function(e){
8662         this.rz.onMouseDown(this, e);
8663     },
8664     // private
8665     onMouseOver : function(e){
8666         this.rz.handleOver(this, e);
8667     },
8668     // private
8669     onMouseOut : function(e){
8670         this.rz.handleOut(this, e);
8671     }
8672 };/*
8673  * Based on:
8674  * Ext JS Library 1.1.1
8675  * Copyright(c) 2006-2007, Ext JS, LLC.
8676  *
8677  * Originally Released Under LGPL - original licence link has changed is not relivant.
8678  *
8679  * Fork - LGPL
8680  * <script type="text/javascript">
8681  */
8682
8683 /**
8684  * @class Roo.Editor
8685  * @extends Roo.Component
8686  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8687  * @constructor
8688  * Create a new Editor
8689  * @param {Roo.form.Field} field The Field object (or descendant)
8690  * @param {Object} config The config object
8691  */
8692 Roo.Editor = function(field, config){
8693     Roo.Editor.superclass.constructor.call(this, config);
8694     this.field = field;
8695     this.addEvents({
8696         /**
8697              * @event beforestartedit
8698              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8699              * false from the handler of this event.
8700              * @param {Editor} this
8701              * @param {Roo.Element} boundEl The underlying element bound to this editor
8702              * @param {Mixed} value The field value being set
8703              */
8704         "beforestartedit" : true,
8705         /**
8706              * @event startedit
8707              * Fires when this editor is displayed
8708              * @param {Roo.Element} boundEl The underlying element bound to this editor
8709              * @param {Mixed} value The starting field value
8710              */
8711         "startedit" : true,
8712         /**
8713              * @event beforecomplete
8714              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8715              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8716              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8717              * event will not fire since no edit actually occurred.
8718              * @param {Editor} this
8719              * @param {Mixed} value The current field value
8720              * @param {Mixed} startValue The original field value
8721              */
8722         "beforecomplete" : true,
8723         /**
8724              * @event complete
8725              * Fires after editing is complete and any changed value has been written to the underlying field.
8726              * @param {Editor} this
8727              * @param {Mixed} value The current field value
8728              * @param {Mixed} startValue The original field value
8729              */
8730         "complete" : true,
8731         /**
8732          * @event specialkey
8733          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8734          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8735          * @param {Roo.form.Field} this
8736          * @param {Roo.EventObject} e The event object
8737          */
8738         "specialkey" : true
8739     });
8740 };
8741
8742 Roo.extend(Roo.Editor, Roo.Component, {
8743     /**
8744      * @cfg {Boolean/String} autosize
8745      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8746      * or "height" to adopt the height only (defaults to false)
8747      */
8748     /**
8749      * @cfg {Boolean} revertInvalid
8750      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8751      * validation fails (defaults to true)
8752      */
8753     /**
8754      * @cfg {Boolean} ignoreNoChange
8755      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8756      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8757      * will never be ignored.
8758      */
8759     /**
8760      * @cfg {Boolean} hideEl
8761      * False to keep the bound element visible while the editor is displayed (defaults to true)
8762      */
8763     /**
8764      * @cfg {Mixed} value
8765      * The data value of the underlying field (defaults to "")
8766      */
8767     value : "",
8768     /**
8769      * @cfg {String} alignment
8770      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8771      */
8772     alignment: "c-c?",
8773     /**
8774      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8775      * for bottom-right shadow (defaults to "frame")
8776      */
8777     shadow : "frame",
8778     /**
8779      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8780      */
8781     constrain : false,
8782     /**
8783      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8784      */
8785     completeOnEnter : false,
8786     /**
8787      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8788      */
8789     cancelOnEsc : false,
8790     /**
8791      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8792      */
8793     updateEl : false,
8794
8795     // private
8796     onRender : function(ct, position){
8797         this.el = new Roo.Layer({
8798             shadow: this.shadow,
8799             cls: "x-editor",
8800             parentEl : ct,
8801             shim : this.shim,
8802             shadowOffset:4,
8803             id: this.id,
8804             constrain: this.constrain
8805         });
8806         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8807         if(this.field.msgTarget != 'title'){
8808             this.field.msgTarget = 'qtip';
8809         }
8810         this.field.render(this.el);
8811         if(Roo.isGecko){
8812             this.field.el.dom.setAttribute('autocomplete', 'off');
8813         }
8814         this.field.on("specialkey", this.onSpecialKey, this);
8815         if(this.swallowKeys){
8816             this.field.el.swallowEvent(['keydown','keypress']);
8817         }
8818         this.field.show();
8819         this.field.on("blur", this.onBlur, this);
8820         if(this.field.grow){
8821             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8822         }
8823     },
8824
8825     onSpecialKey : function(field, e)
8826     {
8827         //Roo.log('editor onSpecialKey');
8828         if(this.completeOnEnter && e.getKey() == e.ENTER){
8829             e.stopEvent();
8830             this.completeEdit();
8831             return;
8832         }
8833         // do not fire special key otherwise it might hide close the editor...
8834         if(e.getKey() == e.ENTER){    
8835             return;
8836         }
8837         if(this.cancelOnEsc && e.getKey() == e.ESC){
8838             this.cancelEdit();
8839             return;
8840         } 
8841         this.fireEvent('specialkey', field, e);
8842     
8843     },
8844
8845     /**
8846      * Starts the editing process and shows the editor.
8847      * @param {String/HTMLElement/Element} el The element to edit
8848      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8849       * to the innerHTML of el.
8850      */
8851     startEdit : function(el, value){
8852         if(this.editing){
8853             this.completeEdit();
8854         }
8855         this.boundEl = Roo.get(el);
8856         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8857         if(!this.rendered){
8858             this.render(this.parentEl || document.body);
8859         }
8860         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8861             return;
8862         }
8863         this.startValue = v;
8864         this.field.setValue(v);
8865         if(this.autoSize){
8866             var sz = this.boundEl.getSize();
8867             switch(this.autoSize){
8868                 case "width":
8869                 this.setSize(sz.width,  "");
8870                 break;
8871                 case "height":
8872                 this.setSize("",  sz.height);
8873                 break;
8874                 default:
8875                 this.setSize(sz.width,  sz.height);
8876             }
8877         }
8878         this.el.alignTo(this.boundEl, this.alignment);
8879         this.editing = true;
8880         if(Roo.QuickTips){
8881             Roo.QuickTips.disable();
8882         }
8883         this.show();
8884     },
8885
8886     /**
8887      * Sets the height and width of this editor.
8888      * @param {Number} width The new width
8889      * @param {Number} height The new height
8890      */
8891     setSize : function(w, h){
8892         this.field.setSize(w, h);
8893         if(this.el){
8894             this.el.sync();
8895         }
8896     },
8897
8898     /**
8899      * Realigns the editor to the bound field based on the current alignment config value.
8900      */
8901     realign : function(){
8902         this.el.alignTo(this.boundEl, this.alignment);
8903     },
8904
8905     /**
8906      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8907      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8908      */
8909     completeEdit : function(remainVisible){
8910         if(!this.editing){
8911             return;
8912         }
8913         var v = this.getValue();
8914         if(this.revertInvalid !== false && !this.field.isValid()){
8915             v = this.startValue;
8916             this.cancelEdit(true);
8917         }
8918         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8919             this.editing = false;
8920             this.hide();
8921             return;
8922         }
8923         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8924             this.editing = false;
8925             if(this.updateEl && this.boundEl){
8926                 this.boundEl.update(v);
8927             }
8928             if(remainVisible !== true){
8929                 this.hide();
8930             }
8931             this.fireEvent("complete", this, v, this.startValue);
8932         }
8933     },
8934
8935     // private
8936     onShow : function(){
8937         this.el.show();
8938         if(this.hideEl !== false){
8939             this.boundEl.hide();
8940         }
8941         this.field.show();
8942         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8943             this.fixIEFocus = true;
8944             this.deferredFocus.defer(50, this);
8945         }else{
8946             this.field.focus();
8947         }
8948         this.fireEvent("startedit", this.boundEl, this.startValue);
8949     },
8950
8951     deferredFocus : function(){
8952         if(this.editing){
8953             this.field.focus();
8954         }
8955     },
8956
8957     /**
8958      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8959      * reverted to the original starting value.
8960      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8961      * cancel (defaults to false)
8962      */
8963     cancelEdit : function(remainVisible){
8964         if(this.editing){
8965             this.setValue(this.startValue);
8966             if(remainVisible !== true){
8967                 this.hide();
8968             }
8969         }
8970     },
8971
8972     // private
8973     onBlur : function(){
8974         if(this.allowBlur !== true && this.editing){
8975             this.completeEdit();
8976         }
8977     },
8978
8979     // private
8980     onHide : function(){
8981         if(this.editing){
8982             this.completeEdit();
8983             return;
8984         }
8985         this.field.blur();
8986         if(this.field.collapse){
8987             this.field.collapse();
8988         }
8989         this.el.hide();
8990         if(this.hideEl !== false){
8991             this.boundEl.show();
8992         }
8993         if(Roo.QuickTips){
8994             Roo.QuickTips.enable();
8995         }
8996     },
8997
8998     /**
8999      * Sets the data value of the editor
9000      * @param {Mixed} value Any valid value supported by the underlying field
9001      */
9002     setValue : function(v){
9003         this.field.setValue(v);
9004     },
9005
9006     /**
9007      * Gets the data value of the editor
9008      * @return {Mixed} The data value
9009      */
9010     getValue : function(){
9011         return this.field.getValue();
9012     }
9013 });/*
9014  * Based on:
9015  * Ext JS Library 1.1.1
9016  * Copyright(c) 2006-2007, Ext JS, LLC.
9017  *
9018  * Originally Released Under LGPL - original licence link has changed is not relivant.
9019  *
9020  * Fork - LGPL
9021  * <script type="text/javascript">
9022  */
9023  
9024 /**
9025  * @class Roo.BasicDialog
9026  * @extends Roo.util.Observable
9027  * @parent none builder
9028  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9029  * <pre><code>
9030 var dlg = new Roo.BasicDialog("my-dlg", {
9031     height: 200,
9032     width: 300,
9033     minHeight: 100,
9034     minWidth: 150,
9035     modal: true,
9036     proxyDrag: true,
9037     shadow: true
9038 });
9039 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9040 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9041 dlg.addButton('Cancel', dlg.hide, dlg);
9042 dlg.show();
9043 </code></pre>
9044   <b>A Dialog should always be a direct child of the body element.</b>
9045  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9046  * @cfg {String} title Default text to display in the title bar (defaults to null)
9047  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9048  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9049  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9050  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9051  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9052  * (defaults to null with no animation)
9053  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9054  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9055  * property for valid values (defaults to 'all')
9056  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9057  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9058  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9059  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9060  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9061  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9062  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9063  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9064  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9065  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9066  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9067  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9068  * draggable = true (defaults to false)
9069  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9070  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9071  * shadow (defaults to false)
9072  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9073  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9074  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9075  * @cfg {Array} buttons Array of buttons
9076  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9077  * @constructor
9078  * Create a new BasicDialog.
9079  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9080  * @param {Object} config Configuration options
9081  */
9082 Roo.BasicDialog = function(el, config){
9083     this.el = Roo.get(el);
9084     var dh = Roo.DomHelper;
9085     if(!this.el && config && config.autoCreate){
9086         if(typeof config.autoCreate == "object"){
9087             if(!config.autoCreate.id){
9088                 config.autoCreate.id = el;
9089             }
9090             this.el = dh.append(document.body,
9091                         config.autoCreate, true);
9092         }else{
9093             this.el = dh.append(document.body,
9094                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9095         }
9096     }
9097     el = this.el;
9098     el.setDisplayed(true);
9099     el.hide = this.hideAction;
9100     this.id = el.id;
9101     el.addClass("x-dlg");
9102
9103     Roo.apply(this, config);
9104
9105     this.proxy = el.createProxy("x-dlg-proxy");
9106     this.proxy.hide = this.hideAction;
9107     this.proxy.setOpacity(.5);
9108     this.proxy.hide();
9109
9110     if(config.width){
9111         el.setWidth(config.width);
9112     }
9113     if(config.height){
9114         el.setHeight(config.height);
9115     }
9116     this.size = el.getSize();
9117     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9118         this.xy = [config.x,config.y];
9119     }else{
9120         this.xy = el.getCenterXY(true);
9121     }
9122     /** The header element @type Roo.Element */
9123     this.header = el.child("> .x-dlg-hd");
9124     /** The body element @type Roo.Element */
9125     this.body = el.child("> .x-dlg-bd");
9126     /** The footer element @type Roo.Element */
9127     this.footer = el.child("> .x-dlg-ft");
9128
9129     if(!this.header){
9130         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9131     }
9132     if(!this.body){
9133         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9134     }
9135
9136     this.header.unselectable();
9137     if(this.title){
9138         this.header.update(this.title);
9139     }
9140     // this element allows the dialog to be focused for keyboard event
9141     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9142     this.focusEl.swallowEvent("click", true);
9143
9144     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9145
9146     // wrap the body and footer for special rendering
9147     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9148     if(this.footer){
9149         this.bwrap.dom.appendChild(this.footer.dom);
9150     }
9151
9152     this.bg = this.el.createChild({
9153         tag: "div", cls:"x-dlg-bg",
9154         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9155     });
9156     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9157
9158
9159     if(this.autoScroll !== false && !this.autoTabs){
9160         this.body.setStyle("overflow", "auto");
9161     }
9162
9163     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9164
9165     if(this.closable !== false){
9166         this.el.addClass("x-dlg-closable");
9167         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9168         this.close.on("click", this.closeClick, this);
9169         this.close.addClassOnOver("x-dlg-close-over");
9170     }
9171     if(this.collapsible !== false){
9172         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9173         this.collapseBtn.on("click", this.collapseClick, this);
9174         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9175         this.header.on("dblclick", this.collapseClick, this);
9176     }
9177     if(this.resizable !== false){
9178         this.el.addClass("x-dlg-resizable");
9179         this.resizer = new Roo.Resizable(el, {
9180             minWidth: this.minWidth || 80,
9181             minHeight:this.minHeight || 80,
9182             handles: this.resizeHandles || "all",
9183             pinned: true
9184         });
9185         this.resizer.on("beforeresize", this.beforeResize, this);
9186         this.resizer.on("resize", this.onResize, this);
9187     }
9188     if(this.draggable !== false){
9189         el.addClass("x-dlg-draggable");
9190         if (!this.proxyDrag) {
9191             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9192         }
9193         else {
9194             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9195         }
9196         dd.setHandleElId(this.header.id);
9197         dd.endDrag = this.endMove.createDelegate(this);
9198         dd.startDrag = this.startMove.createDelegate(this);
9199         dd.onDrag = this.onDrag.createDelegate(this);
9200         dd.scroll = false;
9201         this.dd = dd;
9202     }
9203     if(this.modal){
9204         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9205         this.mask.enableDisplayMode("block");
9206         this.mask.hide();
9207         this.el.addClass("x-dlg-modal");
9208     }
9209     if(this.shadow){
9210         this.shadow = new Roo.Shadow({
9211             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9212             offset : this.shadowOffset
9213         });
9214     }else{
9215         this.shadowOffset = 0;
9216     }
9217     if(Roo.useShims && this.shim !== false){
9218         this.shim = this.el.createShim();
9219         this.shim.hide = this.hideAction;
9220         this.shim.hide();
9221     }else{
9222         this.shim = false;
9223     }
9224     if(this.autoTabs){
9225         this.initTabs();
9226     }
9227     if (this.buttons) { 
9228         var bts= this.buttons;
9229         this.buttons = [];
9230         Roo.each(bts, function(b) {
9231             this.addButton(b);
9232         }, this);
9233     }
9234     
9235     
9236     this.addEvents({
9237         /**
9238          * @event keydown
9239          * Fires when a key is pressed
9240          * @param {Roo.BasicDialog} this
9241          * @param {Roo.EventObject} e
9242          */
9243         "keydown" : true,
9244         /**
9245          * @event move
9246          * Fires when this dialog is moved by the user.
9247          * @param {Roo.BasicDialog} this
9248          * @param {Number} x The new page X
9249          * @param {Number} y The new page Y
9250          */
9251         "move" : true,
9252         /**
9253          * @event resize
9254          * Fires when this dialog is resized by the user.
9255          * @param {Roo.BasicDialog} this
9256          * @param {Number} width The new width
9257          * @param {Number} height The new height
9258          */
9259         "resize" : true,
9260         /**
9261          * @event beforehide
9262          * Fires before this dialog is hidden.
9263          * @param {Roo.BasicDialog} this
9264          */
9265         "beforehide" : true,
9266         /**
9267          * @event hide
9268          * Fires when this dialog is hidden.
9269          * @param {Roo.BasicDialog} this
9270          */
9271         "hide" : true,
9272         /**
9273          * @event beforeshow
9274          * Fires before this dialog is shown.
9275          * @param {Roo.BasicDialog} this
9276          */
9277         "beforeshow" : true,
9278         /**
9279          * @event show
9280          * Fires when this dialog is shown.
9281          * @param {Roo.BasicDialog} this
9282          */
9283         "show" : true
9284     });
9285     el.on("keydown", this.onKeyDown, this);
9286     el.on("mousedown", this.toFront, this);
9287     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9288     this.el.hide();
9289     Roo.DialogManager.register(this);
9290     Roo.BasicDialog.superclass.constructor.call(this);
9291 };
9292
9293 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9294     shadowOffset: Roo.isIE ? 6 : 5,
9295     minHeight: 80,
9296     minWidth: 200,
9297     minButtonWidth: 75,
9298     defaultButton: null,
9299     buttonAlign: "right",
9300     tabTag: 'div',
9301     firstShow: true,
9302
9303     /**
9304      * Sets the dialog title text
9305      * @param {String} text The title text to display
9306      * @return {Roo.BasicDialog} this
9307      */
9308     setTitle : function(text){
9309         this.header.update(text);
9310         return this;
9311     },
9312
9313     // private
9314     closeClick : function(){
9315         this.hide();
9316     },
9317
9318     // private
9319     collapseClick : function(){
9320         this[this.collapsed ? "expand" : "collapse"]();
9321     },
9322
9323     /**
9324      * Collapses the dialog to its minimized state (only the title bar is visible).
9325      * Equivalent to the user clicking the collapse dialog button.
9326      */
9327     collapse : function(){
9328         if(!this.collapsed){
9329             this.collapsed = true;
9330             this.el.addClass("x-dlg-collapsed");
9331             this.restoreHeight = this.el.getHeight();
9332             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9333         }
9334     },
9335
9336     /**
9337      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9338      * clicking the expand dialog button.
9339      */
9340     expand : function(){
9341         if(this.collapsed){
9342             this.collapsed = false;
9343             this.el.removeClass("x-dlg-collapsed");
9344             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9345         }
9346     },
9347
9348     /**
9349      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9350      * @return {Roo.TabPanel} The tabs component
9351      */
9352     initTabs : function(){
9353         var tabs = this.getTabs();
9354         while(tabs.getTab(0)){
9355             tabs.removeTab(0);
9356         }
9357         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9358             var dom = el.dom;
9359             tabs.addTab(Roo.id(dom), dom.title);
9360             dom.title = "";
9361         });
9362         tabs.activate(0);
9363         return tabs;
9364     },
9365
9366     // private
9367     beforeResize : function(){
9368         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9369     },
9370
9371     // private
9372     onResize : function(){
9373         this.refreshSize();
9374         this.syncBodyHeight();
9375         this.adjustAssets();
9376         this.focus();
9377         this.fireEvent("resize", this, this.size.width, this.size.height);
9378     },
9379
9380     // private
9381     onKeyDown : function(e){
9382         if(this.isVisible()){
9383             this.fireEvent("keydown", this, e);
9384         }
9385     },
9386
9387     /**
9388      * Resizes the dialog.
9389      * @param {Number} width
9390      * @param {Number} height
9391      * @return {Roo.BasicDialog} this
9392      */
9393     resizeTo : function(width, height){
9394         this.el.setSize(width, height);
9395         this.size = {width: width, height: height};
9396         this.syncBodyHeight();
9397         if(this.fixedcenter){
9398             this.center();
9399         }
9400         if(this.isVisible()){
9401             this.constrainXY();
9402             this.adjustAssets();
9403         }
9404         this.fireEvent("resize", this, width, height);
9405         return this;
9406     },
9407
9408
9409     /**
9410      * Resizes the dialog to fit the specified content size.
9411      * @param {Number} width
9412      * @param {Number} height
9413      * @return {Roo.BasicDialog} this
9414      */
9415     setContentSize : function(w, h){
9416         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9417         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9418         //if(!this.el.isBorderBox()){
9419             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9420             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9421         //}
9422         if(this.tabs){
9423             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9424             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9425         }
9426         this.resizeTo(w, h);
9427         return this;
9428     },
9429
9430     /**
9431      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9432      * executed in response to a particular key being pressed while the dialog is active.
9433      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9434      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9435      * @param {Function} fn The function to call
9436      * @param {Object} scope (optional) The scope of the function
9437      * @return {Roo.BasicDialog} this
9438      */
9439     addKeyListener : function(key, fn, scope){
9440         var keyCode, shift, ctrl, alt;
9441         if(typeof key == "object" && !(key instanceof Array)){
9442             keyCode = key["key"];
9443             shift = key["shift"];
9444             ctrl = key["ctrl"];
9445             alt = key["alt"];
9446         }else{
9447             keyCode = key;
9448         }
9449         var handler = function(dlg, e){
9450             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9451                 var k = e.getKey();
9452                 if(keyCode instanceof Array){
9453                     for(var i = 0, len = keyCode.length; i < len; i++){
9454                         if(keyCode[i] == k){
9455                           fn.call(scope || window, dlg, k, e);
9456                           return;
9457                         }
9458                     }
9459                 }else{
9460                     if(k == keyCode){
9461                         fn.call(scope || window, dlg, k, e);
9462                     }
9463                 }
9464             }
9465         };
9466         this.on("keydown", handler);
9467         return this;
9468     },
9469
9470     /**
9471      * Returns the TabPanel component (creates it if it doesn't exist).
9472      * Note: If you wish to simply check for the existence of tabs without creating them,
9473      * check for a null 'tabs' property.
9474      * @return {Roo.TabPanel} The tabs component
9475      */
9476     getTabs : function(){
9477         if(!this.tabs){
9478             this.el.addClass("x-dlg-auto-tabs");
9479             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9480             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9481         }
9482         return this.tabs;
9483     },
9484
9485     /**
9486      * Adds a button to the footer section of the dialog.
9487      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9488      * object or a valid Roo.DomHelper element config
9489      * @param {Function} handler The function called when the button is clicked
9490      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9491      * @return {Roo.Button} The new button
9492      */
9493     addButton : function(config, handler, scope){
9494         var dh = Roo.DomHelper;
9495         if(!this.footer){
9496             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9497         }
9498         if(!this.btnContainer){
9499             var tb = this.footer.createChild({
9500
9501                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9502                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9503             }, null, true);
9504             this.btnContainer = tb.firstChild.firstChild.firstChild;
9505         }
9506         var bconfig = {
9507             handler: handler,
9508             scope: scope,
9509             minWidth: this.minButtonWidth,
9510             hideParent:true
9511         };
9512         if(typeof config == "string"){
9513             bconfig.text = config;
9514         }else{
9515             if(config.tag){
9516                 bconfig.dhconfig = config;
9517             }else{
9518                 Roo.apply(bconfig, config);
9519             }
9520         }
9521         var fc = false;
9522         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9523             bconfig.position = Math.max(0, bconfig.position);
9524             fc = this.btnContainer.childNodes[bconfig.position];
9525         }
9526          
9527         var btn = new Roo.Button(
9528             fc ? 
9529                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9530                 : this.btnContainer.appendChild(document.createElement("td")),
9531             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9532             bconfig
9533         );
9534         this.syncBodyHeight();
9535         if(!this.buttons){
9536             /**
9537              * Array of all the buttons that have been added to this dialog via addButton
9538              * @type Array
9539              */
9540             this.buttons = [];
9541         }
9542         this.buttons.push(btn);
9543         return btn;
9544     },
9545
9546     /**
9547      * Sets the default button to be focused when the dialog is displayed.
9548      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9549      * @return {Roo.BasicDialog} this
9550      */
9551     setDefaultButton : function(btn){
9552         this.defaultButton = btn;
9553         return this;
9554     },
9555
9556     // private
9557     getHeaderFooterHeight : function(safe){
9558         var height = 0;
9559         if(this.header){
9560            height += this.header.getHeight();
9561         }
9562         if(this.footer){
9563            var fm = this.footer.getMargins();
9564             height += (this.footer.getHeight()+fm.top+fm.bottom);
9565         }
9566         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9567         height += this.centerBg.getPadding("tb");
9568         return height;
9569     },
9570
9571     // private
9572     syncBodyHeight : function()
9573     {
9574         var bd = this.body, // the text
9575             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9576             bw = this.bwrap;
9577         var height = this.size.height - this.getHeaderFooterHeight(false);
9578         bd.setHeight(height-bd.getMargins("tb"));
9579         var hh = this.header.getHeight();
9580         var h = this.size.height-hh;
9581         cb.setHeight(h);
9582         
9583         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9584         bw.setHeight(h-cb.getPadding("tb"));
9585         
9586         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9587         bd.setWidth(bw.getWidth(true));
9588         if(this.tabs){
9589             this.tabs.syncHeight();
9590             if(Roo.isIE){
9591                 this.tabs.el.repaint();
9592             }
9593         }
9594     },
9595
9596     /**
9597      * Restores the previous state of the dialog if Roo.state is configured.
9598      * @return {Roo.BasicDialog} this
9599      */
9600     restoreState : function(){
9601         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9602         if(box && box.width){
9603             this.xy = [box.x, box.y];
9604             this.resizeTo(box.width, box.height);
9605         }
9606         return this;
9607     },
9608
9609     // private
9610     beforeShow : function(){
9611         this.expand();
9612         if(this.fixedcenter){
9613             this.xy = this.el.getCenterXY(true);
9614         }
9615         if(this.modal){
9616             Roo.get(document.body).addClass("x-body-masked");
9617             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9618             this.mask.show();
9619         }
9620         this.constrainXY();
9621     },
9622
9623     // private
9624     animShow : function(){
9625         var b = Roo.get(this.animateTarget).getBox();
9626         this.proxy.setSize(b.width, b.height);
9627         this.proxy.setLocation(b.x, b.y);
9628         this.proxy.show();
9629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9630                     true, .35, this.showEl.createDelegate(this));
9631     },
9632
9633     /**
9634      * Shows the dialog.
9635      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9636      * @return {Roo.BasicDialog} this
9637      */
9638     show : function(animateTarget){
9639         if (this.fireEvent("beforeshow", this) === false){
9640             return;
9641         }
9642         if(this.syncHeightBeforeShow){
9643             this.syncBodyHeight();
9644         }else if(this.firstShow){
9645             this.firstShow = false;
9646             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9647         }
9648         this.animateTarget = animateTarget || this.animateTarget;
9649         if(!this.el.isVisible()){
9650             this.beforeShow();
9651             if(this.animateTarget && Roo.get(this.animateTarget)){
9652                 this.animShow();
9653             }else{
9654                 this.showEl();
9655             }
9656         }
9657         return this;
9658     },
9659
9660     // private
9661     showEl : function(){
9662         this.proxy.hide();
9663         this.el.setXY(this.xy);
9664         this.el.show();
9665         this.adjustAssets(true);
9666         this.toFront();
9667         this.focus();
9668         // IE peekaboo bug - fix found by Dave Fenwick
9669         if(Roo.isIE){
9670             this.el.repaint();
9671         }
9672         this.fireEvent("show", this);
9673     },
9674
9675     /**
9676      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9677      * dialog itself will receive focus.
9678      */
9679     focus : function(){
9680         if(this.defaultButton){
9681             this.defaultButton.focus();
9682         }else{
9683             this.focusEl.focus();
9684         }
9685     },
9686
9687     // private
9688     constrainXY : function(){
9689         if(this.constraintoviewport !== false){
9690             if(!this.viewSize){
9691                 if(this.container){
9692                     var s = this.container.getSize();
9693                     this.viewSize = [s.width, s.height];
9694                 }else{
9695                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9696                 }
9697             }
9698             var s = Roo.get(this.container||document).getScroll();
9699
9700             var x = this.xy[0], y = this.xy[1];
9701             var w = this.size.width, h = this.size.height;
9702             var vw = this.viewSize[0], vh = this.viewSize[1];
9703             // only move it if it needs it
9704             var moved = false;
9705             // first validate right/bottom
9706             if(x + w > vw+s.left){
9707                 x = vw - w;
9708                 moved = true;
9709             }
9710             if(y + h > vh+s.top){
9711                 y = vh - h;
9712                 moved = true;
9713             }
9714             // then make sure top/left isn't negative
9715             if(x < s.left){
9716                 x = s.left;
9717                 moved = true;
9718             }
9719             if(y < s.top){
9720                 y = s.top;
9721                 moved = true;
9722             }
9723             if(moved){
9724                 // cache xy
9725                 this.xy = [x, y];
9726                 if(this.isVisible()){
9727                     this.el.setLocation(x, y);
9728                     this.adjustAssets();
9729                 }
9730             }
9731         }
9732     },
9733
9734     // private
9735     onDrag : function(){
9736         if(!this.proxyDrag){
9737             this.xy = this.el.getXY();
9738             this.adjustAssets();
9739         }
9740     },
9741
9742     // private
9743     adjustAssets : function(doShow){
9744         var x = this.xy[0], y = this.xy[1];
9745         var w = this.size.width, h = this.size.height;
9746         if(doShow === true){
9747             if(this.shadow){
9748                 this.shadow.show(this.el);
9749             }
9750             if(this.shim){
9751                 this.shim.show();
9752             }
9753         }
9754         if(this.shadow && this.shadow.isVisible()){
9755             this.shadow.show(this.el);
9756         }
9757         if(this.shim && this.shim.isVisible()){
9758             this.shim.setBounds(x, y, w, h);
9759         }
9760     },
9761
9762     // private
9763     adjustViewport : function(w, h){
9764         if(!w || !h){
9765             w = Roo.lib.Dom.getViewWidth();
9766             h = Roo.lib.Dom.getViewHeight();
9767         }
9768         // cache the size
9769         this.viewSize = [w, h];
9770         if(this.modal && this.mask.isVisible()){
9771             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9772             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9773         }
9774         if(this.isVisible()){
9775             this.constrainXY();
9776         }
9777     },
9778
9779     /**
9780      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9781      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9782      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9783      */
9784     destroy : function(removeEl){
9785         if(this.isVisible()){
9786             this.animateTarget = null;
9787             this.hide();
9788         }
9789         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9790         if(this.tabs){
9791             this.tabs.destroy(removeEl);
9792         }
9793         Roo.destroy(
9794              this.shim,
9795              this.proxy,
9796              this.resizer,
9797              this.close,
9798              this.mask
9799         );
9800         if(this.dd){
9801             this.dd.unreg();
9802         }
9803         if(this.buttons){
9804            for(var i = 0, len = this.buttons.length; i < len; i++){
9805                this.buttons[i].destroy();
9806            }
9807         }
9808         this.el.removeAllListeners();
9809         if(removeEl === true){
9810             this.el.update("");
9811             this.el.remove();
9812         }
9813         Roo.DialogManager.unregister(this);
9814     },
9815
9816     // private
9817     startMove : function(){
9818         if(this.proxyDrag){
9819             this.proxy.show();
9820         }
9821         if(this.constraintoviewport !== false){
9822             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9823         }
9824     },
9825
9826     // private
9827     endMove : function(){
9828         if(!this.proxyDrag){
9829             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9830         }else{
9831             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9832             this.proxy.hide();
9833         }
9834         this.refreshSize();
9835         this.adjustAssets();
9836         this.focus();
9837         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9838     },
9839
9840     /**
9841      * Brings this dialog to the front of any other visible dialogs
9842      * @return {Roo.BasicDialog} this
9843      */
9844     toFront : function(){
9845         Roo.DialogManager.bringToFront(this);
9846         return this;
9847     },
9848
9849     /**
9850      * Sends this dialog to the back (under) of any other visible dialogs
9851      * @return {Roo.BasicDialog} this
9852      */
9853     toBack : function(){
9854         Roo.DialogManager.sendToBack(this);
9855         return this;
9856     },
9857
9858     /**
9859      * Centers this dialog in the viewport
9860      * @return {Roo.BasicDialog} this
9861      */
9862     center : function(){
9863         var xy = this.el.getCenterXY(true);
9864         this.moveTo(xy[0], xy[1]);
9865         return this;
9866     },
9867
9868     /**
9869      * Moves the dialog's top-left corner to the specified point
9870      * @param {Number} x
9871      * @param {Number} y
9872      * @return {Roo.BasicDialog} this
9873      */
9874     moveTo : function(x, y){
9875         this.xy = [x,y];
9876         if(this.isVisible()){
9877             this.el.setXY(this.xy);
9878             this.adjustAssets();
9879         }
9880         return this;
9881     },
9882
9883     /**
9884      * Aligns the dialog to the specified element
9885      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9886      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9887      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9888      * @return {Roo.BasicDialog} this
9889      */
9890     alignTo : function(element, position, offsets){
9891         this.xy = this.el.getAlignToXY(element, position, offsets);
9892         if(this.isVisible()){
9893             this.el.setXY(this.xy);
9894             this.adjustAssets();
9895         }
9896         return this;
9897     },
9898
9899     /**
9900      * Anchors an element to another element and realigns it when the window is resized.
9901      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9902      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9903      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9904      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9905      * is a number, it is used as the buffer delay (defaults to 50ms).
9906      * @return {Roo.BasicDialog} this
9907      */
9908     anchorTo : function(el, alignment, offsets, monitorScroll){
9909         var action = function(){
9910             this.alignTo(el, alignment, offsets);
9911         };
9912         Roo.EventManager.onWindowResize(action, this);
9913         var tm = typeof monitorScroll;
9914         if(tm != 'undefined'){
9915             Roo.EventManager.on(window, 'scroll', action, this,
9916                 {buffer: tm == 'number' ? monitorScroll : 50});
9917         }
9918         action.call(this);
9919         return this;
9920     },
9921
9922     /**
9923      * Returns true if the dialog is visible
9924      * @return {Boolean}
9925      */
9926     isVisible : function(){
9927         return this.el.isVisible();
9928     },
9929
9930     // private
9931     animHide : function(callback){
9932         var b = Roo.get(this.animateTarget).getBox();
9933         this.proxy.show();
9934         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9935         this.el.hide();
9936         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9937                     this.hideEl.createDelegate(this, [callback]));
9938     },
9939
9940     /**
9941      * Hides the dialog.
9942      * @param {Function} callback (optional) Function to call when the dialog is hidden
9943      * @return {Roo.BasicDialog} this
9944      */
9945     hide : function(callback){
9946         if (this.fireEvent("beforehide", this) === false){
9947             return;
9948         }
9949         if(this.shadow){
9950             this.shadow.hide();
9951         }
9952         if(this.shim) {
9953           this.shim.hide();
9954         }
9955         // sometimes animateTarget seems to get set.. causing problems...
9956         // this just double checks..
9957         if(this.animateTarget && Roo.get(this.animateTarget)) {
9958            this.animHide(callback);
9959         }else{
9960             this.el.hide();
9961             this.hideEl(callback);
9962         }
9963         return this;
9964     },
9965
9966     // private
9967     hideEl : function(callback){
9968         this.proxy.hide();
9969         if(this.modal){
9970             this.mask.hide();
9971             Roo.get(document.body).removeClass("x-body-masked");
9972         }
9973         this.fireEvent("hide", this);
9974         if(typeof callback == "function"){
9975             callback();
9976         }
9977     },
9978
9979     // private
9980     hideAction : function(){
9981         this.setLeft("-10000px");
9982         this.setTop("-10000px");
9983         this.setStyle("visibility", "hidden");
9984     },
9985
9986     // private
9987     refreshSize : function(){
9988         this.size = this.el.getSize();
9989         this.xy = this.el.getXY();
9990         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9991     },
9992
9993     // private
9994     // z-index is managed by the DialogManager and may be overwritten at any time
9995     setZIndex : function(index){
9996         if(this.modal){
9997             this.mask.setStyle("z-index", index);
9998         }
9999         if(this.shim){
10000             this.shim.setStyle("z-index", ++index);
10001         }
10002         if(this.shadow){
10003             this.shadow.setZIndex(++index);
10004         }
10005         this.el.setStyle("z-index", ++index);
10006         if(this.proxy){
10007             this.proxy.setStyle("z-index", ++index);
10008         }
10009         if(this.resizer){
10010             this.resizer.proxy.setStyle("z-index", ++index);
10011         }
10012
10013         this.lastZIndex = index;
10014     },
10015
10016     /**
10017      * Returns the element for this dialog
10018      * @return {Roo.Element} The underlying dialog Element
10019      */
10020     getEl : function(){
10021         return this.el;
10022     }
10023 });
10024
10025 /**
10026  * @class Roo.DialogManager
10027  * Provides global access to BasicDialogs that have been created and
10028  * support for z-indexing (layering) multiple open dialogs.
10029  */
10030 Roo.DialogManager = function(){
10031     var list = {};
10032     var accessList = [];
10033     var front = null;
10034
10035     // private
10036     var sortDialogs = function(d1, d2){
10037         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10038     };
10039
10040     // private
10041     var orderDialogs = function(){
10042         accessList.sort(sortDialogs);
10043         var seed = Roo.DialogManager.zseed;
10044         for(var i = 0, len = accessList.length; i < len; i++){
10045             var dlg = accessList[i];
10046             if(dlg){
10047                 dlg.setZIndex(seed + (i*10));
10048             }
10049         }
10050     };
10051
10052     return {
10053         /**
10054          * The starting z-index for BasicDialogs (defaults to 9000)
10055          * @type Number The z-index value
10056          */
10057         zseed : 9000,
10058
10059         // private
10060         register : function(dlg){
10061             list[dlg.id] = dlg;
10062             accessList.push(dlg);
10063         },
10064
10065         // private
10066         unregister : function(dlg){
10067             delete list[dlg.id];
10068             var i=0;
10069             var len=0;
10070             if(!accessList.indexOf){
10071                 for(  i = 0, len = accessList.length; i < len; i++){
10072                     if(accessList[i] == dlg){
10073                         accessList.splice(i, 1);
10074                         return;
10075                     }
10076                 }
10077             }else{
10078                  i = accessList.indexOf(dlg);
10079                 if(i != -1){
10080                     accessList.splice(i, 1);
10081                 }
10082             }
10083         },
10084
10085         /**
10086          * Gets a registered dialog by id
10087          * @param {String/Object} id The id of the dialog or a dialog
10088          * @return {Roo.BasicDialog} this
10089          */
10090         get : function(id){
10091             return typeof id == "object" ? id : list[id];
10092         },
10093
10094         /**
10095          * Brings the specified dialog to the front
10096          * @param {String/Object} dlg The id of the dialog or a dialog
10097          * @return {Roo.BasicDialog} this
10098          */
10099         bringToFront : function(dlg){
10100             dlg = this.get(dlg);
10101             if(dlg != front){
10102                 front = dlg;
10103                 dlg._lastAccess = new Date().getTime();
10104                 orderDialogs();
10105             }
10106             return dlg;
10107         },
10108
10109         /**
10110          * Sends the specified dialog to the back
10111          * @param {String/Object} dlg The id of the dialog or a dialog
10112          * @return {Roo.BasicDialog} this
10113          */
10114         sendToBack : function(dlg){
10115             dlg = this.get(dlg);
10116             dlg._lastAccess = -(new Date().getTime());
10117             orderDialogs();
10118             return dlg;
10119         },
10120
10121         /**
10122          * Hides all dialogs
10123          */
10124         hideAll : function(){
10125             for(var id in list){
10126                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10127                     list[id].hide();
10128                 }
10129             }
10130         }
10131     };
10132 }();
10133
10134 /**
10135  * @class Roo.LayoutDialog
10136  * @extends Roo.BasicDialog
10137  * @children Roo.ContentPanel
10138  * @parent builder none
10139  * Dialog which provides adjustments for working with a layout in a Dialog.
10140  * Add your necessary layout config options to the dialog's config.<br>
10141  * Example usage (including a nested layout):
10142  * <pre><code>
10143 if(!dialog){
10144     dialog = new Roo.LayoutDialog("download-dlg", {
10145         modal: true,
10146         width:600,
10147         height:450,
10148         shadow:true,
10149         minWidth:500,
10150         minHeight:350,
10151         autoTabs:true,
10152         proxyDrag:true,
10153         // layout config merges with the dialog config
10154         center:{
10155             tabPosition: "top",
10156             alwaysShowTabs: true
10157         }
10158     });
10159     dialog.addKeyListener(27, dialog.hide, dialog);
10160     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10161     dialog.addButton("Build It!", this.getDownload, this);
10162
10163     // we can even add nested layouts
10164     var innerLayout = new Roo.BorderLayout("dl-inner", {
10165         east: {
10166             initialSize: 200,
10167             autoScroll:true,
10168             split:true
10169         },
10170         center: {
10171             autoScroll:true
10172         }
10173     });
10174     innerLayout.beginUpdate();
10175     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10176     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10177     innerLayout.endUpdate(true);
10178
10179     var layout = dialog.getLayout();
10180     layout.beginUpdate();
10181     layout.add("center", new Roo.ContentPanel("standard-panel",
10182                         {title: "Download the Source", fitToFrame:true}));
10183     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10184                {title: "Build your own roo.js"}));
10185     layout.getRegion("center").showPanel(sp);
10186     layout.endUpdate();
10187 }
10188 </code></pre>
10189     * @constructor
10190     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10191     * @param {Object} config configuration options
10192   */
10193 Roo.LayoutDialog = function(el, cfg){
10194     
10195     var config=  cfg;
10196     if (typeof(cfg) == 'undefined') {
10197         config = Roo.apply({}, el);
10198         // not sure why we use documentElement here.. - it should always be body.
10199         // IE7 borks horribly if we use documentElement.
10200         // webkit also does not like documentElement - it creates a body element...
10201         el = Roo.get( document.body || document.documentElement ).createChild();
10202         //config.autoCreate = true;
10203     }
10204     
10205     
10206     config.autoTabs = false;
10207     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10208     this.body.setStyle({overflow:"hidden", position:"relative"});
10209     this.layout = new Roo.BorderLayout(this.body.dom, config);
10210     this.layout.monitorWindowResize = false;
10211     this.el.addClass("x-dlg-auto-layout");
10212     // fix case when center region overwrites center function
10213     this.center = Roo.BasicDialog.prototype.center;
10214     this.on("show", this.layout.layout, this.layout, true);
10215     if (config.items) {
10216         var xitems = config.items;
10217         delete config.items;
10218         Roo.each(xitems, this.addxtype, this);
10219     }
10220     
10221     
10222 };
10223 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10224     
10225     
10226     /**
10227      * @cfg {Roo.LayoutRegion} east  
10228      */
10229     /**
10230      * @cfg {Roo.LayoutRegion} west
10231      */
10232     /**
10233      * @cfg {Roo.LayoutRegion} south
10234      */
10235     /**
10236      * @cfg {Roo.LayoutRegion} north
10237      */
10238     /**
10239      * @cfg {Roo.LayoutRegion} center
10240      */
10241     /**
10242      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10243      */
10244     
10245     
10246     /**
10247      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10248      * @deprecated
10249      */
10250     endUpdate : function(){
10251         this.layout.endUpdate();
10252     },
10253
10254     /**
10255      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10256      *  @deprecated
10257      */
10258     beginUpdate : function(){
10259         this.layout.beginUpdate();
10260     },
10261
10262     /**
10263      * Get the BorderLayout for this dialog
10264      * @return {Roo.BorderLayout}
10265      */
10266     getLayout : function(){
10267         return this.layout;
10268     },
10269
10270     showEl : function(){
10271         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10272         if(Roo.isIE7){
10273             this.layout.layout();
10274         }
10275     },
10276
10277     // private
10278     // Use the syncHeightBeforeShow config option to control this automatically
10279     syncBodyHeight : function(){
10280         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10281         if(this.layout){this.layout.layout();}
10282     },
10283     
10284       /**
10285      * Add an xtype element (actually adds to the layout.)
10286      * @return {Object} xdata xtype object data.
10287      */
10288     
10289     addxtype : function(c) {
10290         return this.layout.addxtype(c);
10291     }
10292 });/*
10293  * Based on:
10294  * Ext JS Library 1.1.1
10295  * Copyright(c) 2006-2007, Ext JS, LLC.
10296  *
10297  * Originally Released Under LGPL - original licence link has changed is not relivant.
10298  *
10299  * Fork - LGPL
10300  * <script type="text/javascript">
10301  */
10302  
10303 /**
10304  * @class Roo.MessageBox
10305  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10306  * Example usage:
10307  *<pre><code>
10308 // Basic alert:
10309 Roo.Msg.alert('Status', 'Changes saved successfully.');
10310
10311 // Prompt for user data:
10312 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10313     if (btn == 'ok'){
10314         // process text value...
10315     }
10316 });
10317
10318 // Show a dialog using config options:
10319 Roo.Msg.show({
10320    title:'Save Changes?',
10321    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10322    buttons: Roo.Msg.YESNOCANCEL,
10323    fn: processResult,
10324    animEl: 'elId'
10325 });
10326 </code></pre>
10327  * @static
10328  */
10329 Roo.MessageBox = function(){
10330     var dlg, opt, mask, waitTimer;
10331     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10332     var buttons, activeTextEl, bwidth;
10333
10334     // private
10335     var handleButton = function(button){
10336         dlg.hide();
10337         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10338     };
10339
10340     // private
10341     var handleHide = function(){
10342         if(opt && opt.cls){
10343             dlg.el.removeClass(opt.cls);
10344         }
10345         if(waitTimer){
10346             Roo.TaskMgr.stop(waitTimer);
10347             waitTimer = null;
10348         }
10349     };
10350
10351     // private
10352     var updateButtons = function(b){
10353         var width = 0;
10354         if(!b){
10355             buttons["ok"].hide();
10356             buttons["cancel"].hide();
10357             buttons["yes"].hide();
10358             buttons["no"].hide();
10359             dlg.footer.dom.style.display = 'none';
10360             return width;
10361         }
10362         dlg.footer.dom.style.display = '';
10363         for(var k in buttons){
10364             if(typeof buttons[k] != "function"){
10365                 if(b[k]){
10366                     buttons[k].show();
10367                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10368                     width += buttons[k].el.getWidth()+15;
10369                 }else{
10370                     buttons[k].hide();
10371                 }
10372             }
10373         }
10374         return width;
10375     };
10376
10377     // private
10378     var handleEsc = function(d, k, e){
10379         if(opt && opt.closable !== false){
10380             dlg.hide();
10381         }
10382         if(e){
10383             e.stopEvent();
10384         }
10385     };
10386
10387     return {
10388         /**
10389          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10390          * @return {Roo.BasicDialog} The BasicDialog element
10391          */
10392         getDialog : function(){
10393            if(!dlg){
10394                 dlg = new Roo.BasicDialog("x-msg-box", {
10395                     autoCreate : true,
10396                     shadow: true,
10397                     draggable: true,
10398                     resizable:false,
10399                     constraintoviewport:false,
10400                     fixedcenter:true,
10401                     collapsible : false,
10402                     shim:true,
10403                     modal: true,
10404                     width:400, height:100,
10405                     buttonAlign:"center",
10406                     closeClick : function(){
10407                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10408                             handleButton("no");
10409                         }else{
10410                             handleButton("cancel");
10411                         }
10412                     }
10413                 });
10414                 dlg.on("hide", handleHide);
10415                 mask = dlg.mask;
10416                 dlg.addKeyListener(27, handleEsc);
10417                 buttons = {};
10418                 var bt = this.buttonText;
10419                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10420                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10421                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10422                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10423                 bodyEl = dlg.body.createChild({
10424
10425                     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>'
10426                 });
10427                 msgEl = bodyEl.dom.firstChild;
10428                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10429                 textboxEl.enableDisplayMode();
10430                 textboxEl.addKeyListener([10,13], function(){
10431                     if(dlg.isVisible() && opt && opt.buttons){
10432                         if(opt.buttons.ok){
10433                             handleButton("ok");
10434                         }else if(opt.buttons.yes){
10435                             handleButton("yes");
10436                         }
10437                     }
10438                 });
10439                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10440                 textareaEl.enableDisplayMode();
10441                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10442                 progressEl.enableDisplayMode();
10443                 var pf = progressEl.dom.firstChild;
10444                 if (pf) {
10445                     pp = Roo.get(pf.firstChild);
10446                     pp.setHeight(pf.offsetHeight);
10447                 }
10448                 
10449             }
10450             return dlg;
10451         },
10452
10453         /**
10454          * Updates the message box body text
10455          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10456          * the XHTML-compliant non-breaking space character '&amp;#160;')
10457          * @return {Roo.MessageBox} This message box
10458          */
10459         updateText : function(text){
10460             if(!dlg.isVisible() && !opt.width){
10461                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10462             }
10463             msgEl.innerHTML = text || '&#160;';
10464       
10465             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10466             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10467             var w = Math.max(
10468                     Math.min(opt.width || cw , this.maxWidth), 
10469                     Math.max(opt.minWidth || this.minWidth, bwidth)
10470             );
10471             if(opt.prompt){
10472                 activeTextEl.setWidth(w);
10473             }
10474             if(dlg.isVisible()){
10475                 dlg.fixedcenter = false;
10476             }
10477             // to big, make it scroll. = But as usual stupid IE does not support
10478             // !important..
10479             
10480             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10481                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10482                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10483             } else {
10484                 bodyEl.dom.style.height = '';
10485                 bodyEl.dom.style.overflowY = '';
10486             }
10487             if (cw > w) {
10488                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10489             } else {
10490                 bodyEl.dom.style.overflowX = '';
10491             }
10492             
10493             dlg.setContentSize(w, bodyEl.getHeight());
10494             if(dlg.isVisible()){
10495                 dlg.fixedcenter = true;
10496             }
10497             return this;
10498         },
10499
10500         /**
10501          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10502          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10503          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10504          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10505          * @return {Roo.MessageBox} This message box
10506          */
10507         updateProgress : function(value, text){
10508             if(text){
10509                 this.updateText(text);
10510             }
10511             if (pp) { // weird bug on my firefox - for some reason this is not defined
10512                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10513             }
10514             return this;
10515         },        
10516
10517         /**
10518          * Returns true if the message box is currently displayed
10519          * @return {Boolean} True if the message box is visible, else false
10520          */
10521         isVisible : function(){
10522             return dlg && dlg.isVisible();  
10523         },
10524
10525         /**
10526          * Hides the message box if it is displayed
10527          */
10528         hide : function(){
10529             if(this.isVisible()){
10530                 dlg.hide();
10531             }  
10532         },
10533
10534         /**
10535          * Displays a new message box, or reinitializes an existing message box, based on the config options
10536          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10537          * The following config object properties are supported:
10538          * <pre>
10539 Property    Type             Description
10540 ----------  ---------------  ------------------------------------------------------------------------------------
10541 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10542                                    closes (defaults to undefined)
10543 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10544                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10545 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10546                                    progress and wait dialogs will ignore this property and always hide the
10547                                    close button as they can only be closed programmatically.
10548 cls               String           A custom CSS class to apply to the message box element
10549 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10550                                    displayed (defaults to 75)
10551 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10552                                    function will be btn (the name of the button that was clicked, if applicable,
10553                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10554                                    Progress and wait dialogs will ignore this option since they do not respond to
10555                                    user actions and can only be closed programmatically, so any required function
10556                                    should be called by the same code after it closes the dialog.
10557 icon              String           A CSS class that provides a background image to be used as an icon for
10558                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10559 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10560 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10561 modal             Boolean          False to allow user interaction with the page while the message box is
10562                                    displayed (defaults to true)
10563 msg               String           A string that will replace the existing message box body text (defaults
10564                                    to the XHTML-compliant non-breaking space character '&#160;')
10565 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10566 progress          Boolean          True to display a progress bar (defaults to false)
10567 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10568 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10569 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10570 title             String           The title text
10571 value             String           The string value to set into the active textbox element if displayed
10572 wait              Boolean          True to display a progress bar (defaults to false)
10573 width             Number           The width of the dialog in pixels
10574 </pre>
10575          *
10576          * Example usage:
10577          * <pre><code>
10578 Roo.Msg.show({
10579    title: 'Address',
10580    msg: 'Please enter your address:',
10581    width: 300,
10582    buttons: Roo.MessageBox.OKCANCEL,
10583    multiline: true,
10584    fn: saveAddress,
10585    animEl: 'addAddressBtn'
10586 });
10587 </code></pre>
10588          * @param {Object} config Configuration options
10589          * @return {Roo.MessageBox} This message box
10590          */
10591         show : function(options)
10592         {
10593             
10594             // this causes nightmares if you show one dialog after another
10595             // especially on callbacks..
10596              
10597             if(this.isVisible()){
10598                 
10599                 this.hide();
10600                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10601                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10602                 Roo.log("New Dialog Message:" +  options.msg )
10603                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10604                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10605                 
10606             }
10607             var d = this.getDialog();
10608             opt = options;
10609             d.setTitle(opt.title || "&#160;");
10610             d.close.setDisplayed(opt.closable !== false);
10611             activeTextEl = textboxEl;
10612             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10613             if(opt.prompt){
10614                 if(opt.multiline){
10615                     textboxEl.hide();
10616                     textareaEl.show();
10617                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10618                         opt.multiline : this.defaultTextHeight);
10619                     activeTextEl = textareaEl;
10620                 }else{
10621                     textboxEl.show();
10622                     textareaEl.hide();
10623                 }
10624             }else{
10625                 textboxEl.hide();
10626                 textareaEl.hide();
10627             }
10628             progressEl.setDisplayed(opt.progress === true);
10629             this.updateProgress(0);
10630             activeTextEl.dom.value = opt.value || "";
10631             if(opt.prompt){
10632                 dlg.setDefaultButton(activeTextEl);
10633             }else{
10634                 var bs = opt.buttons;
10635                 var db = null;
10636                 if(bs && bs.ok){
10637                     db = buttons["ok"];
10638                 }else if(bs && bs.yes){
10639                     db = buttons["yes"];
10640                 }
10641                 dlg.setDefaultButton(db);
10642             }
10643             bwidth = updateButtons(opt.buttons);
10644             this.updateText(opt.msg);
10645             if(opt.cls){
10646                 d.el.addClass(opt.cls);
10647             }
10648             d.proxyDrag = opt.proxyDrag === true;
10649             d.modal = opt.modal !== false;
10650             d.mask = opt.modal !== false ? mask : false;
10651             if(!d.isVisible()){
10652                 // force it to the end of the z-index stack so it gets a cursor in FF
10653                 document.body.appendChild(dlg.el.dom);
10654                 d.animateTarget = null;
10655                 d.show(options.animEl);
10656             }
10657             return this;
10658         },
10659
10660         /**
10661          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10662          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10663          * and closing the message box when the process is complete.
10664          * @param {String} title The title bar text
10665          * @param {String} msg The message box body text
10666          * @return {Roo.MessageBox} This message box
10667          */
10668         progress : function(title, msg){
10669             this.show({
10670                 title : title,
10671                 msg : msg,
10672                 buttons: false,
10673                 progress:true,
10674                 closable:false,
10675                 minWidth: this.minProgressWidth,
10676                 modal : true
10677             });
10678             return this;
10679         },
10680
10681         /**
10682          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10683          * If a callback function is passed it will be called after the user clicks the button, and the
10684          * id of the button that was clicked will be passed as the only parameter to the callback
10685          * (could also be the top-right close button).
10686          * @param {String} title The title bar text
10687          * @param {String} msg The message box body text
10688          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10689          * @param {Object} scope (optional) The scope of the callback function
10690          * @return {Roo.MessageBox} This message box
10691          */
10692         alert : function(title, msg, fn, scope){
10693             this.show({
10694                 title : title,
10695                 msg : msg,
10696                 buttons: this.OK,
10697                 fn: fn,
10698                 scope : scope,
10699                 modal : true
10700             });
10701             return this;
10702         },
10703
10704         /**
10705          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10706          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10707          * You are responsible for closing the message box when the process is complete.
10708          * @param {String} msg The message box body text
10709          * @param {String} title (optional) The title bar text
10710          * @return {Roo.MessageBox} This message box
10711          */
10712         wait : function(msg, title){
10713             this.show({
10714                 title : title,
10715                 msg : msg,
10716                 buttons: false,
10717                 closable:false,
10718                 progress:true,
10719                 modal:true,
10720                 width:300,
10721                 wait:true
10722             });
10723             waitTimer = Roo.TaskMgr.start({
10724                 run: function(i){
10725                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10726                 },
10727                 interval: 1000
10728             });
10729             return this;
10730         },
10731
10732         /**
10733          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10734          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10735          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10736          * @param {String} title The title bar text
10737          * @param {String} msg The message box body text
10738          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10739          * @param {Object} scope (optional) The scope of the callback function
10740          * @return {Roo.MessageBox} This message box
10741          */
10742         confirm : function(title, msg, fn, scope){
10743             this.show({
10744                 title : title,
10745                 msg : msg,
10746                 buttons: this.YESNO,
10747                 fn: fn,
10748                 scope : scope,
10749                 modal : true
10750             });
10751             return this;
10752         },
10753
10754         /**
10755          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10756          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10757          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10758          * (could also be the top-right close button) and the text that was entered will be passed as the two
10759          * parameters to the callback.
10760          * @param {String} title The title bar text
10761          * @param {String} msg The message box body text
10762          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10763          * @param {Object} scope (optional) The scope of the callback function
10764          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10765          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10766          * @return {Roo.MessageBox} This message box
10767          */
10768         prompt : function(title, msg, fn, scope, multiline){
10769             this.show({
10770                 title : title,
10771                 msg : msg,
10772                 buttons: this.OKCANCEL,
10773                 fn: fn,
10774                 minWidth:250,
10775                 scope : scope,
10776                 prompt:true,
10777                 multiline: multiline,
10778                 modal : true
10779             });
10780             return this;
10781         },
10782
10783         /**
10784          * Button config that displays a single OK button
10785          * @type Object
10786          */
10787         OK : {ok:true},
10788         /**
10789          * Button config that displays Yes and No buttons
10790          * @type Object
10791          */
10792         YESNO : {yes:true, no:true},
10793         /**
10794          * Button config that displays OK and Cancel buttons
10795          * @type Object
10796          */
10797         OKCANCEL : {ok:true, cancel:true},
10798         /**
10799          * Button config that displays Yes, No and Cancel buttons
10800          * @type Object
10801          */
10802         YESNOCANCEL : {yes:true, no:true, cancel:true},
10803
10804         /**
10805          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10806          * @type Number
10807          */
10808         defaultTextHeight : 75,
10809         /**
10810          * The maximum width in pixels of the message box (defaults to 600)
10811          * @type Number
10812          */
10813         maxWidth : 600,
10814         /**
10815          * The minimum width in pixels of the message box (defaults to 100)
10816          * @type Number
10817          */
10818         minWidth : 100,
10819         /**
10820          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10821          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10822          * @type Number
10823          */
10824         minProgressWidth : 250,
10825         /**
10826          * An object containing the default button text strings that can be overriden for localized language support.
10827          * Supported properties are: ok, cancel, yes and no.
10828          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10829          * @type Object
10830          */
10831         buttonText : {
10832             ok : "OK",
10833             cancel : "Cancel",
10834             yes : "Yes",
10835             no : "No"
10836         }
10837     };
10838 }();
10839
10840 /**
10841  * Shorthand for {@link Roo.MessageBox}
10842  */
10843 Roo.Msg = Roo.MessageBox;/*
10844  * Based on:
10845  * Ext JS Library 1.1.1
10846  * Copyright(c) 2006-2007, Ext JS, LLC.
10847  *
10848  * Originally Released Under LGPL - original licence link has changed is not relivant.
10849  *
10850  * Fork - LGPL
10851  * <script type="text/javascript">
10852  */
10853 /**
10854  * @class Roo.QuickTips
10855  * Provides attractive and customizable tooltips for any element.
10856  * @static
10857  */
10858 Roo.QuickTips = function(){
10859     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10860     var ce, bd, xy, dd;
10861     var visible = false, disabled = true, inited = false;
10862     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10863     
10864     var onOver = function(e){
10865         if(disabled){
10866             return;
10867         }
10868         var t = e.getTarget();
10869         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10870             return;
10871         }
10872         if(ce && t == ce.el){
10873             clearTimeout(hideProc);
10874             return;
10875         }
10876         if(t && tagEls[t.id]){
10877             tagEls[t.id].el = t;
10878             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10879             return;
10880         }
10881         var ttp, et = Roo.fly(t);
10882         var ns = cfg.namespace;
10883         if(tm.interceptTitles && t.title){
10884             ttp = t.title;
10885             t.qtip = ttp;
10886             t.removeAttribute("title");
10887             e.preventDefault();
10888         }else{
10889             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10890         }
10891         if(ttp){
10892             showProc = show.defer(tm.showDelay, tm, [{
10893                 el: t, 
10894                 text: ttp.replace(/\\n/g,'<br/>'),
10895                 width: et.getAttributeNS(ns, cfg.width),
10896                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10897                 title: et.getAttributeNS(ns, cfg.title),
10898                     cls: et.getAttributeNS(ns, cfg.cls)
10899             }]);
10900         }
10901     };
10902     
10903     var onOut = function(e){
10904         clearTimeout(showProc);
10905         var t = e.getTarget();
10906         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10907             hideProc = setTimeout(hide, tm.hideDelay);
10908         }
10909     };
10910     
10911     var onMove = function(e){
10912         if(disabled){
10913             return;
10914         }
10915         xy = e.getXY();
10916         xy[1] += 18;
10917         if(tm.trackMouse && ce){
10918             el.setXY(xy);
10919         }
10920     };
10921     
10922     var onDown = function(e){
10923         clearTimeout(showProc);
10924         clearTimeout(hideProc);
10925         if(!e.within(el)){
10926             if(tm.hideOnClick){
10927                 hide();
10928                 tm.disable();
10929                 tm.enable.defer(100, tm);
10930             }
10931         }
10932     };
10933     
10934     var getPad = function(){
10935         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10936     };
10937
10938     var show = function(o){
10939         if(disabled){
10940             return;
10941         }
10942         clearTimeout(dismissProc);
10943         ce = o;
10944         if(removeCls){ // in case manually hidden
10945             el.removeClass(removeCls);
10946             removeCls = null;
10947         }
10948         if(ce.cls){
10949             el.addClass(ce.cls);
10950             removeCls = ce.cls;
10951         }
10952         if(ce.title){
10953             tipTitle.update(ce.title);
10954             tipTitle.show();
10955         }else{
10956             tipTitle.update('');
10957             tipTitle.hide();
10958         }
10959         el.dom.style.width  = tm.maxWidth+'px';
10960         //tipBody.dom.style.width = '';
10961         tipBodyText.update(o.text);
10962         var p = getPad(), w = ce.width;
10963         if(!w){
10964             var td = tipBodyText.dom;
10965             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10966             if(aw > tm.maxWidth){
10967                 w = tm.maxWidth;
10968             }else if(aw < tm.minWidth){
10969                 w = tm.minWidth;
10970             }else{
10971                 w = aw;
10972             }
10973         }
10974         //tipBody.setWidth(w);
10975         el.setWidth(parseInt(w, 10) + p);
10976         if(ce.autoHide === false){
10977             close.setDisplayed(true);
10978             if(dd){
10979                 dd.unlock();
10980             }
10981         }else{
10982             close.setDisplayed(false);
10983             if(dd){
10984                 dd.lock();
10985             }
10986         }
10987         if(xy){
10988             el.avoidY = xy[1]-18;
10989             el.setXY(xy);
10990         }
10991         if(tm.animate){
10992             el.setOpacity(.1);
10993             el.setStyle("visibility", "visible");
10994             el.fadeIn({callback: afterShow});
10995         }else{
10996             afterShow();
10997         }
10998     };
10999     
11000     var afterShow = function(){
11001         if(ce){
11002             el.show();
11003             esc.enable();
11004             if(tm.autoDismiss && ce.autoHide !== false){
11005                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11006             }
11007         }
11008     };
11009     
11010     var hide = function(noanim){
11011         clearTimeout(dismissProc);
11012         clearTimeout(hideProc);
11013         ce = null;
11014         if(el.isVisible()){
11015             esc.disable();
11016             if(noanim !== true && tm.animate){
11017                 el.fadeOut({callback: afterHide});
11018             }else{
11019                 afterHide();
11020             } 
11021         }
11022     };
11023     
11024     var afterHide = function(){
11025         el.hide();
11026         if(removeCls){
11027             el.removeClass(removeCls);
11028             removeCls = null;
11029         }
11030     };
11031     
11032     return {
11033         /**
11034         * @cfg {Number} minWidth
11035         * The minimum width of the quick tip (defaults to 40)
11036         */
11037        minWidth : 40,
11038         /**
11039         * @cfg {Number} maxWidth
11040         * The maximum width of the quick tip (defaults to 300)
11041         */
11042        maxWidth : 300,
11043         /**
11044         * @cfg {Boolean} interceptTitles
11045         * True to automatically use the element's DOM title value if available (defaults to false)
11046         */
11047        interceptTitles : false,
11048         /**
11049         * @cfg {Boolean} trackMouse
11050         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11051         */
11052        trackMouse : false,
11053         /**
11054         * @cfg {Boolean} hideOnClick
11055         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11056         */
11057        hideOnClick : true,
11058         /**
11059         * @cfg {Number} showDelay
11060         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11061         */
11062        showDelay : 500,
11063         /**
11064         * @cfg {Number} hideDelay
11065         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11066         */
11067        hideDelay : 200,
11068         /**
11069         * @cfg {Boolean} autoHide
11070         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11071         * Used in conjunction with hideDelay.
11072         */
11073        autoHide : true,
11074         /**
11075         * @cfg {Boolean}
11076         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11077         * (defaults to true).  Used in conjunction with autoDismissDelay.
11078         */
11079        autoDismiss : true,
11080         /**
11081         * @cfg {Number}
11082         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11083         */
11084        autoDismissDelay : 5000,
11085        /**
11086         * @cfg {Boolean} animate
11087         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11088         */
11089        animate : false,
11090
11091        /**
11092         * @cfg {String} title
11093         * Title text to display (defaults to '').  This can be any valid HTML markup.
11094         */
11095         title: '',
11096        /**
11097         * @cfg {String} text
11098         * Body text to display (defaults to '').  This can be any valid HTML markup.
11099         */
11100         text : '',
11101        /**
11102         * @cfg {String} cls
11103         * A CSS class to apply to the base quick tip element (defaults to '').
11104         */
11105         cls : '',
11106        /**
11107         * @cfg {Number} width
11108         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11109         * minWidth or maxWidth.
11110         */
11111         width : null,
11112
11113     /**
11114      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11115      * or display QuickTips in a page.
11116      */
11117        init : function(){
11118           tm = Roo.QuickTips;
11119           cfg = tm.tagConfig;
11120           if(!inited){
11121               if(!Roo.isReady){ // allow calling of init() before onReady
11122                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11123                   return;
11124               }
11125               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11126               el.fxDefaults = {stopFx: true};
11127               // maximum custom styling
11128               //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>');
11129               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>');              
11130               tipTitle = el.child('h3');
11131               tipTitle.enableDisplayMode("block");
11132               tipBody = el.child('div.x-tip-bd');
11133               tipBodyText = el.child('div.x-tip-bd-inner');
11134               //bdLeft = el.child('div.x-tip-bd-left');
11135               //bdRight = el.child('div.x-tip-bd-right');
11136               close = el.child('div.x-tip-close');
11137               close.enableDisplayMode("block");
11138               close.on("click", hide);
11139               var d = Roo.get(document);
11140               d.on("mousedown", onDown);
11141               d.on("mouseover", onOver);
11142               d.on("mouseout", onOut);
11143               d.on("mousemove", onMove);
11144               esc = d.addKeyListener(27, hide);
11145               esc.disable();
11146               if(Roo.dd.DD){
11147                   dd = el.initDD("default", null, {
11148                       onDrag : function(){
11149                           el.sync();  
11150                       }
11151                   });
11152                   dd.setHandleElId(tipTitle.id);
11153                   dd.lock();
11154               }
11155               inited = true;
11156           }
11157           this.enable(); 
11158        },
11159
11160     /**
11161      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11162      * are supported:
11163      * <pre>
11164 Property    Type                   Description
11165 ----------  ---------------------  ------------------------------------------------------------------------
11166 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11167      * </ul>
11168      * @param {Object} config The config object
11169      */
11170        register : function(config){
11171            var cs = config instanceof Array ? config : arguments;
11172            for(var i = 0, len = cs.length; i < len; i++) {
11173                var c = cs[i];
11174                var target = c.target;
11175                if(target){
11176                    if(target instanceof Array){
11177                        for(var j = 0, jlen = target.length; j < jlen; j++){
11178                            tagEls[target[j]] = c;
11179                        }
11180                    }else{
11181                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11182                    }
11183                }
11184            }
11185        },
11186
11187     /**
11188      * Removes this quick tip from its element and destroys it.
11189      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11190      */
11191        unregister : function(el){
11192            delete tagEls[Roo.id(el)];
11193        },
11194
11195     /**
11196      * Enable this quick tip.
11197      */
11198        enable : function(){
11199            if(inited && disabled){
11200                locks.pop();
11201                if(locks.length < 1){
11202                    disabled = false;
11203                }
11204            }
11205        },
11206
11207     /**
11208      * Disable this quick tip.
11209      */
11210        disable : function(){
11211           disabled = true;
11212           clearTimeout(showProc);
11213           clearTimeout(hideProc);
11214           clearTimeout(dismissProc);
11215           if(ce){
11216               hide(true);
11217           }
11218           locks.push(1);
11219        },
11220
11221     /**
11222      * Returns true if the quick tip is enabled, else false.
11223      */
11224        isEnabled : function(){
11225             return !disabled;
11226        },
11227
11228         // private
11229        tagConfig : {
11230            namespace : "roo", // was ext?? this may break..
11231            alt_namespace : "ext",
11232            attribute : "qtip",
11233            width : "width",
11234            target : "target",
11235            title : "qtitle",
11236            hide : "hide",
11237            cls : "qclass"
11238        }
11239    };
11240 }();
11241
11242 // backwards compat
11243 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11244  * Based on:
11245  * Ext JS Library 1.1.1
11246  * Copyright(c) 2006-2007, Ext JS, LLC.
11247  *
11248  * Originally Released Under LGPL - original licence link has changed is not relivant.
11249  *
11250  * Fork - LGPL
11251  * <script type="text/javascript">
11252  */
11253  
11254
11255 /**
11256  * @class Roo.tree.TreePanel
11257  * @extends Roo.data.Tree
11258  * @cfg {Roo.tree.TreeNode} root The root node
11259  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11260  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11261  * @cfg {Boolean} enableDD true to enable drag and drop
11262  * @cfg {Boolean} enableDrag true to enable just drag
11263  * @cfg {Boolean} enableDrop true to enable just drop
11264  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11265  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11266  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11267  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11268  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11269  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11270  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11271  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11272  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11273  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11274  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11275  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11276  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11277  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11278  * @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>
11279  * @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>
11280  * 
11281  * @constructor
11282  * @param {String/HTMLElement/Element} el The container element
11283  * @param {Object} config
11284  */
11285 Roo.tree.TreePanel = function(el, config){
11286     var root = false;
11287     var loader = false;
11288     if (config.root) {
11289         root = config.root;
11290         delete config.root;
11291     }
11292     if (config.loader) {
11293         loader = config.loader;
11294         delete config.loader;
11295     }
11296     
11297     Roo.apply(this, config);
11298     Roo.tree.TreePanel.superclass.constructor.call(this);
11299     this.el = Roo.get(el);
11300     this.el.addClass('x-tree');
11301     //console.log(root);
11302     if (root) {
11303         this.setRootNode( Roo.factory(root, Roo.tree));
11304     }
11305     if (loader) {
11306         this.loader = Roo.factory(loader, Roo.tree);
11307     }
11308    /**
11309     * Read-only. The id of the container element becomes this TreePanel's id.
11310     */
11311     this.id = this.el.id;
11312     this.addEvents({
11313         /**
11314         * @event beforeload
11315         * Fires before a node is loaded, return false to cancel
11316         * @param {Node} node The node being loaded
11317         */
11318         "beforeload" : true,
11319         /**
11320         * @event load
11321         * Fires when a node is loaded
11322         * @param {Node} node The node that was loaded
11323         */
11324         "load" : true,
11325         /**
11326         * @event textchange
11327         * Fires when the text for a node is changed
11328         * @param {Node} node The node
11329         * @param {String} text The new text
11330         * @param {String} oldText The old text
11331         */
11332         "textchange" : true,
11333         /**
11334         * @event beforeexpand
11335         * Fires before a node is expanded, return false to cancel.
11336         * @param {Node} node The node
11337         * @param {Boolean} deep
11338         * @param {Boolean} anim
11339         */
11340         "beforeexpand" : true,
11341         /**
11342         * @event beforecollapse
11343         * Fires before a node is collapsed, return false to cancel.
11344         * @param {Node} node The node
11345         * @param {Boolean} deep
11346         * @param {Boolean} anim
11347         */
11348         "beforecollapse" : true,
11349         /**
11350         * @event expand
11351         * Fires when a node is expanded
11352         * @param {Node} node The node
11353         */
11354         "expand" : true,
11355         /**
11356         * @event disabledchange
11357         * Fires when the disabled status of a node changes
11358         * @param {Node} node The node
11359         * @param {Boolean} disabled
11360         */
11361         "disabledchange" : true,
11362         /**
11363         * @event collapse
11364         * Fires when a node is collapsed
11365         * @param {Node} node The node
11366         */
11367         "collapse" : true,
11368         /**
11369         * @event beforeclick
11370         * Fires before click processing on a node. Return false to cancel the default action.
11371         * @param {Node} node The node
11372         * @param {Roo.EventObject} e The event object
11373         */
11374         "beforeclick":true,
11375         /**
11376         * @event checkchange
11377         * Fires when a node with a checkbox's checked property changes
11378         * @param {Node} this This node
11379         * @param {Boolean} checked
11380         */
11381         "checkchange":true,
11382         /**
11383         * @event click
11384         * Fires when a node is clicked
11385         * @param {Node} node The node
11386         * @param {Roo.EventObject} e The event object
11387         */
11388         "click":true,
11389         /**
11390         * @event dblclick
11391         * Fires when a node is double clicked
11392         * @param {Node} node The node
11393         * @param {Roo.EventObject} e The event object
11394         */
11395         "dblclick":true,
11396         /**
11397         * @event contextmenu
11398         * Fires when a node is right clicked
11399         * @param {Node} node The node
11400         * @param {Roo.EventObject} e The event object
11401         */
11402         "contextmenu":true,
11403         /**
11404         * @event beforechildrenrendered
11405         * Fires right before the child nodes for a node are rendered
11406         * @param {Node} node The node
11407         */
11408         "beforechildrenrendered":true,
11409         /**
11410         * @event startdrag
11411         * Fires when a node starts being dragged
11412         * @param {Roo.tree.TreePanel} this
11413         * @param {Roo.tree.TreeNode} node
11414         * @param {event} e The raw browser event
11415         */ 
11416        "startdrag" : true,
11417        /**
11418         * @event enddrag
11419         * Fires when a drag operation is complete
11420         * @param {Roo.tree.TreePanel} this
11421         * @param {Roo.tree.TreeNode} node
11422         * @param {event} e The raw browser event
11423         */
11424        "enddrag" : true,
11425        /**
11426         * @event dragdrop
11427         * Fires when a dragged node is dropped on a valid DD target
11428         * @param {Roo.tree.TreePanel} this
11429         * @param {Roo.tree.TreeNode} node
11430         * @param {DD} dd The dd it was dropped on
11431         * @param {event} e The raw browser event
11432         */
11433        "dragdrop" : true,
11434        /**
11435         * @event beforenodedrop
11436         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11437         * passed to handlers has the following properties:<br />
11438         * <ul style="padding:5px;padding-left:16px;">
11439         * <li>tree - The TreePanel</li>
11440         * <li>target - The node being targeted for the drop</li>
11441         * <li>data - The drag data from the drag source</li>
11442         * <li>point - The point of the drop - append, above or below</li>
11443         * <li>source - The drag source</li>
11444         * <li>rawEvent - Raw mouse event</li>
11445         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11446         * to be inserted by setting them on this object.</li>
11447         * <li>cancel - Set this to true to cancel the drop.</li>
11448         * </ul>
11449         * @param {Object} dropEvent
11450         */
11451        "beforenodedrop" : true,
11452        /**
11453         * @event nodedrop
11454         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11455         * passed to handlers has the following properties:<br />
11456         * <ul style="padding:5px;padding-left:16px;">
11457         * <li>tree - The TreePanel</li>
11458         * <li>target - The node being targeted for the drop</li>
11459         * <li>data - The drag data from the drag source</li>
11460         * <li>point - The point of the drop - append, above or below</li>
11461         * <li>source - The drag source</li>
11462         * <li>rawEvent - Raw mouse event</li>
11463         * <li>dropNode - Dropped node(s).</li>
11464         * </ul>
11465         * @param {Object} dropEvent
11466         */
11467        "nodedrop" : true,
11468         /**
11469         * @event nodedragover
11470         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11471         * passed to handlers has the following properties:<br />
11472         * <ul style="padding:5px;padding-left:16px;">
11473         * <li>tree - The TreePanel</li>
11474         * <li>target - The node being targeted for the drop</li>
11475         * <li>data - The drag data from the drag source</li>
11476         * <li>point - The point of the drop - append, above or below</li>
11477         * <li>source - The drag source</li>
11478         * <li>rawEvent - Raw mouse event</li>
11479         * <li>dropNode - Drop node(s) provided by the source.</li>
11480         * <li>cancel - Set this to true to signal drop not allowed.</li>
11481         * </ul>
11482         * @param {Object} dragOverEvent
11483         */
11484        "nodedragover" : true,
11485        /**
11486         * @event appendnode
11487         * Fires when append node to the tree
11488         * @param {Roo.tree.TreePanel} this
11489         * @param {Roo.tree.TreeNode} node
11490         * @param {Number} index The index of the newly appended node
11491         */
11492        "appendnode" : true
11493         
11494     });
11495     if(this.singleExpand){
11496        this.on("beforeexpand", this.restrictExpand, this);
11497     }
11498     if (this.editor) {
11499         this.editor.tree = this;
11500         this.editor = Roo.factory(this.editor, Roo.tree);
11501     }
11502     
11503     if (this.selModel) {
11504         this.selModel = Roo.factory(this.selModel, Roo.tree);
11505     }
11506    
11507 };
11508 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11509     rootVisible : true,
11510     animate: Roo.enableFx,
11511     lines : true,
11512     enableDD : false,
11513     hlDrop : Roo.enableFx,
11514   
11515     renderer: false,
11516     
11517     rendererTip: false,
11518     // private
11519     restrictExpand : function(node){
11520         var p = node.parentNode;
11521         if(p){
11522             if(p.expandedChild && p.expandedChild.parentNode == p){
11523                 p.expandedChild.collapse();
11524             }
11525             p.expandedChild = node;
11526         }
11527     },
11528
11529     // private override
11530     setRootNode : function(node){
11531         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11532         if(!this.rootVisible){
11533             node.ui = new Roo.tree.RootTreeNodeUI(node);
11534         }
11535         return node;
11536     },
11537
11538     /**
11539      * Returns the container element for this TreePanel
11540      */
11541     getEl : function(){
11542         return this.el;
11543     },
11544
11545     /**
11546      * Returns the default TreeLoader for this TreePanel
11547      */
11548     getLoader : function(){
11549         return this.loader;
11550     },
11551
11552     /**
11553      * Expand all nodes
11554      */
11555     expandAll : function(){
11556         this.root.expand(true);
11557     },
11558
11559     /**
11560      * Collapse all nodes
11561      */
11562     collapseAll : function(){
11563         this.root.collapse(true);
11564     },
11565
11566     /**
11567      * Returns the selection model used by this TreePanel
11568      */
11569     getSelectionModel : function(){
11570         if(!this.selModel){
11571             this.selModel = new Roo.tree.DefaultSelectionModel();
11572         }
11573         return this.selModel;
11574     },
11575
11576     /**
11577      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11578      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11579      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11580      * @return {Array}
11581      */
11582     getChecked : function(a, startNode){
11583         startNode = startNode || this.root;
11584         var r = [];
11585         var f = function(){
11586             if(this.attributes.checked){
11587                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11588             }
11589         }
11590         startNode.cascade(f);
11591         return r;
11592     },
11593
11594     /**
11595      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11596      * @param {String} path
11597      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11598      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11599      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11600      */
11601     expandPath : function(path, attr, callback){
11602         attr = attr || "id";
11603         var keys = path.split(this.pathSeparator);
11604         var curNode = this.root;
11605         if(curNode.attributes[attr] != keys[1]){ // invalid root
11606             if(callback){
11607                 callback(false, null);
11608             }
11609             return;
11610         }
11611         var index = 1;
11612         var f = function(){
11613             if(++index == keys.length){
11614                 if(callback){
11615                     callback(true, curNode);
11616                 }
11617                 return;
11618             }
11619             var c = curNode.findChild(attr, keys[index]);
11620             if(!c){
11621                 if(callback){
11622                     callback(false, curNode);
11623                 }
11624                 return;
11625             }
11626             curNode = c;
11627             c.expand(false, false, f);
11628         };
11629         curNode.expand(false, false, f);
11630     },
11631
11632     /**
11633      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11634      * @param {String} path
11635      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11636      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11637      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11638      */
11639     selectPath : function(path, attr, callback){
11640         attr = attr || "id";
11641         var keys = path.split(this.pathSeparator);
11642         var v = keys.pop();
11643         if(keys.length > 0){
11644             var f = function(success, node){
11645                 if(success && node){
11646                     var n = node.findChild(attr, v);
11647                     if(n){
11648                         n.select();
11649                         if(callback){
11650                             callback(true, n);
11651                         }
11652                     }else if(callback){
11653                         callback(false, n);
11654                     }
11655                 }else{
11656                     if(callback){
11657                         callback(false, n);
11658                     }
11659                 }
11660             };
11661             this.expandPath(keys.join(this.pathSeparator), attr, f);
11662         }else{
11663             this.root.select();
11664             if(callback){
11665                 callback(true, this.root);
11666             }
11667         }
11668     },
11669
11670     getTreeEl : function(){
11671         return this.el;
11672     },
11673
11674     /**
11675      * Trigger rendering of this TreePanel
11676      */
11677     render : function(){
11678         if (this.innerCt) {
11679             return this; // stop it rendering more than once!!
11680         }
11681         
11682         this.innerCt = this.el.createChild({tag:"ul",
11683                cls:"x-tree-root-ct " +
11684                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11685
11686         if(this.containerScroll){
11687             Roo.dd.ScrollManager.register(this.el);
11688         }
11689         if((this.enableDD || this.enableDrop) && !this.dropZone){
11690            /**
11691             * The dropZone used by this tree if drop is enabled
11692             * @type Roo.tree.TreeDropZone
11693             */
11694              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11695                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11696            });
11697         }
11698         if((this.enableDD || this.enableDrag) && !this.dragZone){
11699            /**
11700             * The dragZone used by this tree if drag is enabled
11701             * @type Roo.tree.TreeDragZone
11702             */
11703             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11704                ddGroup: this.ddGroup || "TreeDD",
11705                scroll: this.ddScroll
11706            });
11707         }
11708         this.getSelectionModel().init(this);
11709         if (!this.root) {
11710             Roo.log("ROOT not set in tree");
11711             return this;
11712         }
11713         this.root.render();
11714         if(!this.rootVisible){
11715             this.root.renderChildren();
11716         }
11717         return this;
11718     }
11719 });/*
11720  * Based on:
11721  * Ext JS Library 1.1.1
11722  * Copyright(c) 2006-2007, Ext JS, LLC.
11723  *
11724  * Originally Released Under LGPL - original licence link has changed is not relivant.
11725  *
11726  * Fork - LGPL
11727  * <script type="text/javascript">
11728  */
11729  
11730
11731 /**
11732  * @class Roo.tree.DefaultSelectionModel
11733  * @extends Roo.util.Observable
11734  * The default single selection for a TreePanel.
11735  * @param {Object} cfg Configuration
11736  */
11737 Roo.tree.DefaultSelectionModel = function(cfg){
11738    this.selNode = null;
11739    
11740    
11741    
11742    this.addEvents({
11743        /**
11744         * @event selectionchange
11745         * Fires when the selected node changes
11746         * @param {DefaultSelectionModel} this
11747         * @param {TreeNode} node the new selection
11748         */
11749        "selectionchange" : true,
11750
11751        /**
11752         * @event beforeselect
11753         * Fires before the selected node changes, return false to cancel the change
11754         * @param {DefaultSelectionModel} this
11755         * @param {TreeNode} node the new selection
11756         * @param {TreeNode} node the old selection
11757         */
11758        "beforeselect" : true
11759    });
11760    
11761     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11762 };
11763
11764 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11765     init : function(tree){
11766         this.tree = tree;
11767         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11768         tree.on("click", this.onNodeClick, this);
11769     },
11770     
11771     onNodeClick : function(node, e){
11772         if (e.ctrlKey && this.selNode == node)  {
11773             this.unselect(node);
11774             return;
11775         }
11776         this.select(node);
11777     },
11778     
11779     /**
11780      * Select a node.
11781      * @param {TreeNode} node The node to select
11782      * @return {TreeNode} The selected node
11783      */
11784     select : function(node){
11785         var last = this.selNode;
11786         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11787             if(last){
11788                 last.ui.onSelectedChange(false);
11789             }
11790             this.selNode = node;
11791             node.ui.onSelectedChange(true);
11792             this.fireEvent("selectionchange", this, node, last);
11793         }
11794         return node;
11795     },
11796     
11797     /**
11798      * Deselect a node.
11799      * @param {TreeNode} node The node to unselect
11800      */
11801     unselect : function(node){
11802         if(this.selNode == node){
11803             this.clearSelections();
11804         }    
11805     },
11806     
11807     /**
11808      * Clear all selections
11809      */
11810     clearSelections : function(){
11811         var n = this.selNode;
11812         if(n){
11813             n.ui.onSelectedChange(false);
11814             this.selNode = null;
11815             this.fireEvent("selectionchange", this, null);
11816         }
11817         return n;
11818     },
11819     
11820     /**
11821      * Get the selected node
11822      * @return {TreeNode} The selected node
11823      */
11824     getSelectedNode : function(){
11825         return this.selNode;    
11826     },
11827     
11828     /**
11829      * Returns true if the node is selected
11830      * @param {TreeNode} node The node to check
11831      * @return {Boolean}
11832      */
11833     isSelected : function(node){
11834         return this.selNode == node;  
11835     },
11836
11837     /**
11838      * Selects the node above the selected node in the tree, intelligently walking the nodes
11839      * @return TreeNode The new selection
11840      */
11841     selectPrevious : function(){
11842         var s = this.selNode || this.lastSelNode;
11843         if(!s){
11844             return null;
11845         }
11846         var ps = s.previousSibling;
11847         if(ps){
11848             if(!ps.isExpanded() || ps.childNodes.length < 1){
11849                 return this.select(ps);
11850             } else{
11851                 var lc = ps.lastChild;
11852                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11853                     lc = lc.lastChild;
11854                 }
11855                 return this.select(lc);
11856             }
11857         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11858             return this.select(s.parentNode);
11859         }
11860         return null;
11861     },
11862
11863     /**
11864      * Selects the node above the selected node in the tree, intelligently walking the nodes
11865      * @return TreeNode The new selection
11866      */
11867     selectNext : function(){
11868         var s = this.selNode || this.lastSelNode;
11869         if(!s){
11870             return null;
11871         }
11872         if(s.firstChild && s.isExpanded()){
11873              return this.select(s.firstChild);
11874          }else if(s.nextSibling){
11875              return this.select(s.nextSibling);
11876          }else if(s.parentNode){
11877             var newS = null;
11878             s.parentNode.bubble(function(){
11879                 if(this.nextSibling){
11880                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11881                     return false;
11882                 }
11883             });
11884             return newS;
11885          }
11886         return null;
11887     },
11888
11889     onKeyDown : function(e){
11890         var s = this.selNode || this.lastSelNode;
11891         // undesirable, but required
11892         var sm = this;
11893         if(!s){
11894             return;
11895         }
11896         var k = e.getKey();
11897         switch(k){
11898              case e.DOWN:
11899                  e.stopEvent();
11900                  this.selectNext();
11901              break;
11902              case e.UP:
11903                  e.stopEvent();
11904                  this.selectPrevious();
11905              break;
11906              case e.RIGHT:
11907                  e.preventDefault();
11908                  if(s.hasChildNodes()){
11909                      if(!s.isExpanded()){
11910                          s.expand();
11911                      }else if(s.firstChild){
11912                          this.select(s.firstChild, e);
11913                      }
11914                  }
11915              break;
11916              case e.LEFT:
11917                  e.preventDefault();
11918                  if(s.hasChildNodes() && s.isExpanded()){
11919                      s.collapse();
11920                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11921                      this.select(s.parentNode, e);
11922                  }
11923              break;
11924         };
11925     }
11926 });
11927
11928 /**
11929  * @class Roo.tree.MultiSelectionModel
11930  * @extends Roo.util.Observable
11931  * Multi selection for a TreePanel.
11932  * @param {Object} cfg Configuration
11933  */
11934 Roo.tree.MultiSelectionModel = function(){
11935    this.selNodes = [];
11936    this.selMap = {};
11937    this.addEvents({
11938        /**
11939         * @event selectionchange
11940         * Fires when the selected nodes change
11941         * @param {MultiSelectionModel} this
11942         * @param {Array} nodes Array of the selected nodes
11943         */
11944        "selectionchange" : true
11945    });
11946    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11947    
11948 };
11949
11950 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11951     init : function(tree){
11952         this.tree = tree;
11953         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11954         tree.on("click", this.onNodeClick, this);
11955     },
11956     
11957     onNodeClick : function(node, e){
11958         this.select(node, e, e.ctrlKey);
11959     },
11960     
11961     /**
11962      * Select a node.
11963      * @param {TreeNode} node The node to select
11964      * @param {EventObject} e (optional) An event associated with the selection
11965      * @param {Boolean} keepExisting True to retain existing selections
11966      * @return {TreeNode} The selected node
11967      */
11968     select : function(node, e, keepExisting){
11969         if(keepExisting !== true){
11970             this.clearSelections(true);
11971         }
11972         if(this.isSelected(node)){
11973             this.lastSelNode = node;
11974             return node;
11975         }
11976         this.selNodes.push(node);
11977         this.selMap[node.id] = node;
11978         this.lastSelNode = node;
11979         node.ui.onSelectedChange(true);
11980         this.fireEvent("selectionchange", this, this.selNodes);
11981         return node;
11982     },
11983     
11984     /**
11985      * Deselect a node.
11986      * @param {TreeNode} node The node to unselect
11987      */
11988     unselect : function(node){
11989         if(this.selMap[node.id]){
11990             node.ui.onSelectedChange(false);
11991             var sn = this.selNodes;
11992             var index = -1;
11993             if(sn.indexOf){
11994                 index = sn.indexOf(node);
11995             }else{
11996                 for(var i = 0, len = sn.length; i < len; i++){
11997                     if(sn[i] == node){
11998                         index = i;
11999                         break;
12000                     }
12001                 }
12002             }
12003             if(index != -1){
12004                 this.selNodes.splice(index, 1);
12005             }
12006             delete this.selMap[node.id];
12007             this.fireEvent("selectionchange", this, this.selNodes);
12008         }
12009     },
12010     
12011     /**
12012      * Clear all selections
12013      */
12014     clearSelections : function(suppressEvent){
12015         var sn = this.selNodes;
12016         if(sn.length > 0){
12017             for(var i = 0, len = sn.length; i < len; i++){
12018                 sn[i].ui.onSelectedChange(false);
12019             }
12020             this.selNodes = [];
12021             this.selMap = {};
12022             if(suppressEvent !== true){
12023                 this.fireEvent("selectionchange", this, this.selNodes);
12024             }
12025         }
12026     },
12027     
12028     /**
12029      * Returns true if the node is selected
12030      * @param {TreeNode} node The node to check
12031      * @return {Boolean}
12032      */
12033     isSelected : function(node){
12034         return this.selMap[node.id] ? true : false;  
12035     },
12036     
12037     /**
12038      * Returns an array of the selected nodes
12039      * @return {Array}
12040      */
12041     getSelectedNodes : function(){
12042         return this.selNodes;    
12043     },
12044
12045     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12046
12047     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12048
12049     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12050 });/*
12051  * Based on:
12052  * Ext JS Library 1.1.1
12053  * Copyright(c) 2006-2007, Ext JS, LLC.
12054  *
12055  * Originally Released Under LGPL - original licence link has changed is not relivant.
12056  *
12057  * Fork - LGPL
12058  * <script type="text/javascript">
12059  */
12060  
12061 /**
12062  * @class Roo.tree.TreeNode
12063  * @extends Roo.data.Node
12064  * @cfg {String} text The text for this node
12065  * @cfg {Boolean} expanded true to start the node expanded
12066  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12067  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12068  * @cfg {Boolean} disabled true to start the node disabled
12069  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12070  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12071  * @cfg {String} cls A css class to be added to the node
12072  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12073  * @cfg {String} href URL of the link used for the node (defaults to #)
12074  * @cfg {String} hrefTarget target frame for the link
12075  * @cfg {String} qtip An Ext QuickTip for the node
12076  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12077  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12078  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12079  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12080  * (defaults to undefined with no checkbox rendered)
12081  * @constructor
12082  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12083  */
12084 Roo.tree.TreeNode = function(attributes){
12085     attributes = attributes || {};
12086     if(typeof attributes == "string"){
12087         attributes = {text: attributes};
12088     }
12089     this.childrenRendered = false;
12090     this.rendered = false;
12091     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12092     this.expanded = attributes.expanded === true;
12093     this.isTarget = attributes.isTarget !== false;
12094     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12095     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12096
12097     /**
12098      * Read-only. The text for this node. To change it use setText().
12099      * @type String
12100      */
12101     this.text = attributes.text;
12102     /**
12103      * True if this node is disabled.
12104      * @type Boolean
12105      */
12106     this.disabled = attributes.disabled === true;
12107
12108     this.addEvents({
12109         /**
12110         * @event textchange
12111         * Fires when the text for this node is changed
12112         * @param {Node} this This node
12113         * @param {String} text The new text
12114         * @param {String} oldText The old text
12115         */
12116         "textchange" : true,
12117         /**
12118         * @event beforeexpand
12119         * Fires before this node is expanded, return false to cancel.
12120         * @param {Node} this This node
12121         * @param {Boolean} deep
12122         * @param {Boolean} anim
12123         */
12124         "beforeexpand" : true,
12125         /**
12126         * @event beforecollapse
12127         * Fires before this node is collapsed, return false to cancel.
12128         * @param {Node} this This node
12129         * @param {Boolean} deep
12130         * @param {Boolean} anim
12131         */
12132         "beforecollapse" : true,
12133         /**
12134         * @event expand
12135         * Fires when this node is expanded
12136         * @param {Node} this This node
12137         */
12138         "expand" : true,
12139         /**
12140         * @event disabledchange
12141         * Fires when the disabled status of this node changes
12142         * @param {Node} this This node
12143         * @param {Boolean} disabled
12144         */
12145         "disabledchange" : true,
12146         /**
12147         * @event collapse
12148         * Fires when this node is collapsed
12149         * @param {Node} this This node
12150         */
12151         "collapse" : true,
12152         /**
12153         * @event beforeclick
12154         * Fires before click processing. Return false to cancel the default action.
12155         * @param {Node} this This node
12156         * @param {Roo.EventObject} e The event object
12157         */
12158         "beforeclick":true,
12159         /**
12160         * @event checkchange
12161         * Fires when a node with a checkbox's checked property changes
12162         * @param {Node} this This node
12163         * @param {Boolean} checked
12164         */
12165         "checkchange":true,
12166         /**
12167         * @event click
12168         * Fires when this node is clicked
12169         * @param {Node} this This node
12170         * @param {Roo.EventObject} e The event object
12171         */
12172         "click":true,
12173         /**
12174         * @event dblclick
12175         * Fires when this node is double clicked
12176         * @param {Node} this This node
12177         * @param {Roo.EventObject} e The event object
12178         */
12179         "dblclick":true,
12180         /**
12181         * @event contextmenu
12182         * Fires when this node is right clicked
12183         * @param {Node} this This node
12184         * @param {Roo.EventObject} e The event object
12185         */
12186         "contextmenu":true,
12187         /**
12188         * @event beforechildrenrendered
12189         * Fires right before the child nodes for this node are rendered
12190         * @param {Node} this This node
12191         */
12192         "beforechildrenrendered":true
12193     });
12194
12195     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12196
12197     /**
12198      * Read-only. The UI for this node
12199      * @type TreeNodeUI
12200      */
12201     this.ui = new uiClass(this);
12202     
12203     // finally support items[]
12204     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12205         return;
12206     }
12207     
12208     
12209     Roo.each(this.attributes.items, function(c) {
12210         this.appendChild(Roo.factory(c,Roo.Tree));
12211     }, this);
12212     delete this.attributes.items;
12213     
12214     
12215     
12216 };
12217 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12218     preventHScroll: true,
12219     /**
12220      * Returns true if this node is expanded
12221      * @return {Boolean}
12222      */
12223     isExpanded : function(){
12224         return this.expanded;
12225     },
12226
12227     /**
12228      * Returns the UI object for this node
12229      * @return {TreeNodeUI}
12230      */
12231     getUI : function(){
12232         return this.ui;
12233     },
12234
12235     // private override
12236     setFirstChild : function(node){
12237         var of = this.firstChild;
12238         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12239         if(this.childrenRendered && of && node != of){
12240             of.renderIndent(true, true);
12241         }
12242         if(this.rendered){
12243             this.renderIndent(true, true);
12244         }
12245     },
12246
12247     // private override
12248     setLastChild : function(node){
12249         var ol = this.lastChild;
12250         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12251         if(this.childrenRendered && ol && node != ol){
12252             ol.renderIndent(true, true);
12253         }
12254         if(this.rendered){
12255             this.renderIndent(true, true);
12256         }
12257     },
12258
12259     // these methods are overridden to provide lazy rendering support
12260     // private override
12261     appendChild : function()
12262     {
12263         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12264         if(node && this.childrenRendered){
12265             node.render();
12266         }
12267         this.ui.updateExpandIcon();
12268         return node;
12269     },
12270
12271     // private override
12272     removeChild : function(node){
12273         this.ownerTree.getSelectionModel().unselect(node);
12274         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12275         // if it's been rendered remove dom node
12276         if(this.childrenRendered){
12277             node.ui.remove();
12278         }
12279         if(this.childNodes.length < 1){
12280             this.collapse(false, false);
12281         }else{
12282             this.ui.updateExpandIcon();
12283         }
12284         if(!this.firstChild) {
12285             this.childrenRendered = false;
12286         }
12287         return node;
12288     },
12289
12290     // private override
12291     insertBefore : function(node, refNode){
12292         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12293         if(newNode && refNode && this.childrenRendered){
12294             node.render();
12295         }
12296         this.ui.updateExpandIcon();
12297         return newNode;
12298     },
12299
12300     /**
12301      * Sets the text for this node
12302      * @param {String} text
12303      */
12304     setText : function(text){
12305         var oldText = this.text;
12306         this.text = text;
12307         this.attributes.text = text;
12308         if(this.rendered){ // event without subscribing
12309             this.ui.onTextChange(this, text, oldText);
12310         }
12311         this.fireEvent("textchange", this, text, oldText);
12312     },
12313
12314     /**
12315      * Triggers selection of this node
12316      */
12317     select : function(){
12318         this.getOwnerTree().getSelectionModel().select(this);
12319     },
12320
12321     /**
12322      * Triggers deselection of this node
12323      */
12324     unselect : function(){
12325         this.getOwnerTree().getSelectionModel().unselect(this);
12326     },
12327
12328     /**
12329      * Returns true if this node is selected
12330      * @return {Boolean}
12331      */
12332     isSelected : function(){
12333         return this.getOwnerTree().getSelectionModel().isSelected(this);
12334     },
12335
12336     /**
12337      * Expand this node.
12338      * @param {Boolean} deep (optional) True to expand all children as well
12339      * @param {Boolean} anim (optional) false to cancel the default animation
12340      * @param {Function} callback (optional) A callback to be called when
12341      * expanding this node completes (does not wait for deep expand to complete).
12342      * Called with 1 parameter, this node.
12343      */
12344     expand : function(deep, anim, callback){
12345         if(!this.expanded){
12346             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12347                 return;
12348             }
12349             if(!this.childrenRendered){
12350                 this.renderChildren();
12351             }
12352             this.expanded = true;
12353             
12354             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12355                 this.ui.animExpand(function(){
12356                     this.fireEvent("expand", this);
12357                     if(typeof callback == "function"){
12358                         callback(this);
12359                     }
12360                     if(deep === true){
12361                         this.expandChildNodes(true);
12362                     }
12363                 }.createDelegate(this));
12364                 return;
12365             }else{
12366                 this.ui.expand();
12367                 this.fireEvent("expand", this);
12368                 if(typeof callback == "function"){
12369                     callback(this);
12370                 }
12371             }
12372         }else{
12373            if(typeof callback == "function"){
12374                callback(this);
12375            }
12376         }
12377         if(deep === true){
12378             this.expandChildNodes(true);
12379         }
12380     },
12381
12382     isHiddenRoot : function(){
12383         return this.isRoot && !this.getOwnerTree().rootVisible;
12384     },
12385
12386     /**
12387      * Collapse this node.
12388      * @param {Boolean} deep (optional) True to collapse all children as well
12389      * @param {Boolean} anim (optional) false to cancel the default animation
12390      */
12391     collapse : function(deep, anim){
12392         if(this.expanded && !this.isHiddenRoot()){
12393             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12394                 return;
12395             }
12396             this.expanded = false;
12397             if((this.getOwnerTree().animate && anim !== false) || anim){
12398                 this.ui.animCollapse(function(){
12399                     this.fireEvent("collapse", this);
12400                     if(deep === true){
12401                         this.collapseChildNodes(true);
12402                     }
12403                 }.createDelegate(this));
12404                 return;
12405             }else{
12406                 this.ui.collapse();
12407                 this.fireEvent("collapse", this);
12408             }
12409         }
12410         if(deep === true){
12411             var cs = this.childNodes;
12412             for(var i = 0, len = cs.length; i < len; i++) {
12413                 cs[i].collapse(true, false);
12414             }
12415         }
12416     },
12417
12418     // private
12419     delayedExpand : function(delay){
12420         if(!this.expandProcId){
12421             this.expandProcId = this.expand.defer(delay, this);
12422         }
12423     },
12424
12425     // private
12426     cancelExpand : function(){
12427         if(this.expandProcId){
12428             clearTimeout(this.expandProcId);
12429         }
12430         this.expandProcId = false;
12431     },
12432
12433     /**
12434      * Toggles expanded/collapsed state of the node
12435      */
12436     toggle : function(){
12437         if(this.expanded){
12438             this.collapse();
12439         }else{
12440             this.expand();
12441         }
12442     },
12443
12444     /**
12445      * Ensures all parent nodes are expanded
12446      */
12447     ensureVisible : function(callback){
12448         var tree = this.getOwnerTree();
12449         tree.expandPath(this.parentNode.getPath(), false, function(){
12450             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12451             Roo.callback(callback);
12452         }.createDelegate(this));
12453     },
12454
12455     /**
12456      * Expand all child nodes
12457      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12458      */
12459     expandChildNodes : function(deep){
12460         var cs = this.childNodes;
12461         for(var i = 0, len = cs.length; i < len; i++) {
12462                 cs[i].expand(deep);
12463         }
12464     },
12465
12466     /**
12467      * Collapse all child nodes
12468      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12469      */
12470     collapseChildNodes : function(deep){
12471         var cs = this.childNodes;
12472         for(var i = 0, len = cs.length; i < len; i++) {
12473                 cs[i].collapse(deep);
12474         }
12475     },
12476
12477     /**
12478      * Disables this node
12479      */
12480     disable : function(){
12481         this.disabled = true;
12482         this.unselect();
12483         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12484             this.ui.onDisableChange(this, true);
12485         }
12486         this.fireEvent("disabledchange", this, true);
12487     },
12488
12489     /**
12490      * Enables this node
12491      */
12492     enable : function(){
12493         this.disabled = false;
12494         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12495             this.ui.onDisableChange(this, false);
12496         }
12497         this.fireEvent("disabledchange", this, false);
12498     },
12499
12500     // private
12501     renderChildren : function(suppressEvent){
12502         if(suppressEvent !== false){
12503             this.fireEvent("beforechildrenrendered", this);
12504         }
12505         var cs = this.childNodes;
12506         for(var i = 0, len = cs.length; i < len; i++){
12507             cs[i].render(true);
12508         }
12509         this.childrenRendered = true;
12510     },
12511
12512     // private
12513     sort : function(fn, scope){
12514         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12515         if(this.childrenRendered){
12516             var cs = this.childNodes;
12517             for(var i = 0, len = cs.length; i < len; i++){
12518                 cs[i].render(true);
12519             }
12520         }
12521     },
12522
12523     // private
12524     render : function(bulkRender){
12525         this.ui.render(bulkRender);
12526         if(!this.rendered){
12527             this.rendered = true;
12528             if(this.expanded){
12529                 this.expanded = false;
12530                 this.expand(false, false);
12531             }
12532         }
12533     },
12534
12535     // private
12536     renderIndent : function(deep, refresh){
12537         if(refresh){
12538             this.ui.childIndent = null;
12539         }
12540         this.ui.renderIndent();
12541         if(deep === true && this.childrenRendered){
12542             var cs = this.childNodes;
12543             for(var i = 0, len = cs.length; i < len; i++){
12544                 cs[i].renderIndent(true, refresh);
12545             }
12546         }
12547     }
12548 });/*
12549  * Based on:
12550  * Ext JS Library 1.1.1
12551  * Copyright(c) 2006-2007, Ext JS, LLC.
12552  *
12553  * Originally Released Under LGPL - original licence link has changed is not relivant.
12554  *
12555  * Fork - LGPL
12556  * <script type="text/javascript">
12557  */
12558  
12559 /**
12560  * @class Roo.tree.AsyncTreeNode
12561  * @extends Roo.tree.TreeNode
12562  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12563  * @constructor
12564  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12565  */
12566  Roo.tree.AsyncTreeNode = function(config){
12567     this.loaded = false;
12568     this.loading = false;
12569     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12570     /**
12571     * @event beforeload
12572     * Fires before this node is loaded, return false to cancel
12573     * @param {Node} this This node
12574     */
12575     this.addEvents({'beforeload':true, 'load': true});
12576     /**
12577     * @event load
12578     * Fires when this node is loaded
12579     * @param {Node} this This node
12580     */
12581     /**
12582      * The loader used by this node (defaults to using the tree's defined loader)
12583      * @type TreeLoader
12584      * @property loader
12585      */
12586 };
12587 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12588     expand : function(deep, anim, callback){
12589         if(this.loading){ // if an async load is already running, waiting til it's done
12590             var timer;
12591             var f = function(){
12592                 if(!this.loading){ // done loading
12593                     clearInterval(timer);
12594                     this.expand(deep, anim, callback);
12595                 }
12596             }.createDelegate(this);
12597             timer = setInterval(f, 200);
12598             return;
12599         }
12600         if(!this.loaded){
12601             if(this.fireEvent("beforeload", this) === false){
12602                 return;
12603             }
12604             this.loading = true;
12605             this.ui.beforeLoad(this);
12606             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12607             if(loader){
12608                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12609                 return;
12610             }
12611         }
12612         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12613     },
12614     
12615     /**
12616      * Returns true if this node is currently loading
12617      * @return {Boolean}
12618      */
12619     isLoading : function(){
12620         return this.loading;  
12621     },
12622     
12623     loadComplete : function(deep, anim, callback){
12624         this.loading = false;
12625         this.loaded = true;
12626         this.ui.afterLoad(this);
12627         this.fireEvent("load", this);
12628         this.expand(deep, anim, callback);
12629     },
12630     
12631     /**
12632      * Returns true if this node has been loaded
12633      * @return {Boolean}
12634      */
12635     isLoaded : function(){
12636         return this.loaded;
12637     },
12638     
12639     hasChildNodes : function(){
12640         if(!this.isLeaf() && !this.loaded){
12641             return true;
12642         }else{
12643             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12644         }
12645     },
12646
12647     /**
12648      * Trigger a reload for this node
12649      * @param {Function} callback
12650      */
12651     reload : function(callback){
12652         this.collapse(false, false);
12653         while(this.firstChild){
12654             this.removeChild(this.firstChild);
12655         }
12656         this.childrenRendered = false;
12657         this.loaded = false;
12658         if(this.isHiddenRoot()){
12659             this.expanded = false;
12660         }
12661         this.expand(false, false, callback);
12662     }
12663 });/*
12664  * Based on:
12665  * Ext JS Library 1.1.1
12666  * Copyright(c) 2006-2007, Ext JS, LLC.
12667  *
12668  * Originally Released Under LGPL - original licence link has changed is not relivant.
12669  *
12670  * Fork - LGPL
12671  * <script type="text/javascript">
12672  */
12673  
12674 /**
12675  * @class Roo.tree.TreeNodeUI
12676  * @constructor
12677  * @param {Object} node The node to render
12678  * The TreeNode UI implementation is separate from the
12679  * tree implementation. Unless you are customizing the tree UI,
12680  * you should never have to use this directly.
12681  */
12682 Roo.tree.TreeNodeUI = function(node){
12683     this.node = node;
12684     this.rendered = false;
12685     this.animating = false;
12686     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12687 };
12688
12689 Roo.tree.TreeNodeUI.prototype = {
12690     removeChild : function(node){
12691         if(this.rendered){
12692             this.ctNode.removeChild(node.ui.getEl());
12693         }
12694     },
12695
12696     beforeLoad : function(){
12697          this.addClass("x-tree-node-loading");
12698     },
12699
12700     afterLoad : function(){
12701          this.removeClass("x-tree-node-loading");
12702     },
12703
12704     onTextChange : function(node, text, oldText){
12705         if(this.rendered){
12706             this.textNode.innerHTML = text;
12707         }
12708     },
12709
12710     onDisableChange : function(node, state){
12711         this.disabled = state;
12712         if(state){
12713             this.addClass("x-tree-node-disabled");
12714         }else{
12715             this.removeClass("x-tree-node-disabled");
12716         }
12717     },
12718
12719     onSelectedChange : function(state){
12720         if(state){
12721             this.focus();
12722             this.addClass("x-tree-selected");
12723         }else{
12724             //this.blur();
12725             this.removeClass("x-tree-selected");
12726         }
12727     },
12728
12729     onMove : function(tree, node, oldParent, newParent, index, refNode){
12730         this.childIndent = null;
12731         if(this.rendered){
12732             var targetNode = newParent.ui.getContainer();
12733             if(!targetNode){//target not rendered
12734                 this.holder = document.createElement("div");
12735                 this.holder.appendChild(this.wrap);
12736                 return;
12737             }
12738             var insertBefore = refNode ? refNode.ui.getEl() : null;
12739             if(insertBefore){
12740                 targetNode.insertBefore(this.wrap, insertBefore);
12741             }else{
12742                 targetNode.appendChild(this.wrap);
12743             }
12744             this.node.renderIndent(true);
12745         }
12746     },
12747
12748     addClass : function(cls){
12749         if(this.elNode){
12750             Roo.fly(this.elNode).addClass(cls);
12751         }
12752     },
12753
12754     removeClass : function(cls){
12755         if(this.elNode){
12756             Roo.fly(this.elNode).removeClass(cls);
12757         }
12758     },
12759
12760     remove : function(){
12761         if(this.rendered){
12762             this.holder = document.createElement("div");
12763             this.holder.appendChild(this.wrap);
12764         }
12765     },
12766
12767     fireEvent : function(){
12768         return this.node.fireEvent.apply(this.node, arguments);
12769     },
12770
12771     initEvents : function(){
12772         this.node.on("move", this.onMove, this);
12773         var E = Roo.EventManager;
12774         var a = this.anchor;
12775
12776         var el = Roo.fly(a, '_treeui');
12777
12778         if(Roo.isOpera){ // opera render bug ignores the CSS
12779             el.setStyle("text-decoration", "none");
12780         }
12781
12782         el.on("click", this.onClick, this);
12783         el.on("dblclick", this.onDblClick, this);
12784
12785         if(this.checkbox){
12786             Roo.EventManager.on(this.checkbox,
12787                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12788         }
12789
12790         el.on("contextmenu", this.onContextMenu, this);
12791
12792         var icon = Roo.fly(this.iconNode);
12793         icon.on("click", this.onClick, this);
12794         icon.on("dblclick", this.onDblClick, this);
12795         icon.on("contextmenu", this.onContextMenu, this);
12796         E.on(this.ecNode, "click", this.ecClick, this, true);
12797
12798         if(this.node.disabled){
12799             this.addClass("x-tree-node-disabled");
12800         }
12801         if(this.node.hidden){
12802             this.addClass("x-tree-node-disabled");
12803         }
12804         var ot = this.node.getOwnerTree();
12805         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12806         if(dd && (!this.node.isRoot || ot.rootVisible)){
12807             Roo.dd.Registry.register(this.elNode, {
12808                 node: this.node,
12809                 handles: this.getDDHandles(),
12810                 isHandle: false
12811             });
12812         }
12813     },
12814
12815     getDDHandles : function(){
12816         return [this.iconNode, this.textNode];
12817     },
12818
12819     hide : function(){
12820         if(this.rendered){
12821             this.wrap.style.display = "none";
12822         }
12823     },
12824
12825     show : function(){
12826         if(this.rendered){
12827             this.wrap.style.display = "";
12828         }
12829     },
12830
12831     onContextMenu : function(e){
12832         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12833             e.preventDefault();
12834             this.focus();
12835             this.fireEvent("contextmenu", this.node, e);
12836         }
12837     },
12838
12839     onClick : function(e){
12840         if(this.dropping){
12841             e.stopEvent();
12842             return;
12843         }
12844         if(this.fireEvent("beforeclick", this.node, e) !== false){
12845             if(!this.disabled && this.node.attributes.href){
12846                 this.fireEvent("click", this.node, e);
12847                 return;
12848             }
12849             e.preventDefault();
12850             if(this.disabled){
12851                 return;
12852             }
12853
12854             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12855                 this.node.toggle();
12856             }
12857
12858             this.fireEvent("click", this.node, e);
12859         }else{
12860             e.stopEvent();
12861         }
12862     },
12863
12864     onDblClick : function(e){
12865         e.preventDefault();
12866         if(this.disabled){
12867             return;
12868         }
12869         if(this.checkbox){
12870             this.toggleCheck();
12871         }
12872         if(!this.animating && this.node.hasChildNodes()){
12873             this.node.toggle();
12874         }
12875         this.fireEvent("dblclick", this.node, e);
12876     },
12877
12878     onCheckChange : function(){
12879         var checked = this.checkbox.checked;
12880         this.node.attributes.checked = checked;
12881         this.fireEvent('checkchange', this.node, checked);
12882     },
12883
12884     ecClick : function(e){
12885         if(!this.animating && this.node.hasChildNodes()){
12886             this.node.toggle();
12887         }
12888     },
12889
12890     startDrop : function(){
12891         this.dropping = true;
12892     },
12893
12894     // delayed drop so the click event doesn't get fired on a drop
12895     endDrop : function(){
12896        setTimeout(function(){
12897            this.dropping = false;
12898        }.createDelegate(this), 50);
12899     },
12900
12901     expand : function(){
12902         this.updateExpandIcon();
12903         this.ctNode.style.display = "";
12904     },
12905
12906     focus : function(){
12907         if(!this.node.preventHScroll){
12908             try{this.anchor.focus();
12909             }catch(e){}
12910         }else if(!Roo.isIE){
12911             try{
12912                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12913                 var l = noscroll.scrollLeft;
12914                 this.anchor.focus();
12915                 noscroll.scrollLeft = l;
12916             }catch(e){}
12917         }
12918     },
12919
12920     toggleCheck : function(value){
12921         var cb = this.checkbox;
12922         if(cb){
12923             cb.checked = (value === undefined ? !cb.checked : value);
12924         }
12925     },
12926
12927     blur : function(){
12928         try{
12929             this.anchor.blur();
12930         }catch(e){}
12931     },
12932
12933     animExpand : function(callback){
12934         var ct = Roo.get(this.ctNode);
12935         ct.stopFx();
12936         if(!this.node.hasChildNodes()){
12937             this.updateExpandIcon();
12938             this.ctNode.style.display = "";
12939             Roo.callback(callback);
12940             return;
12941         }
12942         this.animating = true;
12943         this.updateExpandIcon();
12944
12945         ct.slideIn('t', {
12946            callback : function(){
12947                this.animating = false;
12948                Roo.callback(callback);
12949             },
12950             scope: this,
12951             duration: this.node.ownerTree.duration || .25
12952         });
12953     },
12954
12955     highlight : function(){
12956         var tree = this.node.getOwnerTree();
12957         Roo.fly(this.wrap).highlight(
12958             tree.hlColor || "C3DAF9",
12959             {endColor: tree.hlBaseColor}
12960         );
12961     },
12962
12963     collapse : function(){
12964         this.updateExpandIcon();
12965         this.ctNode.style.display = "none";
12966     },
12967
12968     animCollapse : function(callback){
12969         var ct = Roo.get(this.ctNode);
12970         ct.enableDisplayMode('block');
12971         ct.stopFx();
12972
12973         this.animating = true;
12974         this.updateExpandIcon();
12975
12976         ct.slideOut('t', {
12977             callback : function(){
12978                this.animating = false;
12979                Roo.callback(callback);
12980             },
12981             scope: this,
12982             duration: this.node.ownerTree.duration || .25
12983         });
12984     },
12985
12986     getContainer : function(){
12987         return this.ctNode;
12988     },
12989
12990     getEl : function(){
12991         return this.wrap;
12992     },
12993
12994     appendDDGhost : function(ghostNode){
12995         ghostNode.appendChild(this.elNode.cloneNode(true));
12996     },
12997
12998     getDDRepairXY : function(){
12999         return Roo.lib.Dom.getXY(this.iconNode);
13000     },
13001
13002     onRender : function(){
13003         this.render();
13004     },
13005
13006     render : function(bulkRender){
13007         var n = this.node, a = n.attributes;
13008         var targetNode = n.parentNode ?
13009               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13010
13011         if(!this.rendered){
13012             this.rendered = true;
13013
13014             this.renderElements(n, a, targetNode, bulkRender);
13015
13016             if(a.qtip){
13017                if(this.textNode.setAttributeNS){
13018                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13019                    if(a.qtipTitle){
13020                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13021                    }
13022                }else{
13023                    this.textNode.setAttribute("ext:qtip", a.qtip);
13024                    if(a.qtipTitle){
13025                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13026                    }
13027                }
13028             }else if(a.qtipCfg){
13029                 a.qtipCfg.target = Roo.id(this.textNode);
13030                 Roo.QuickTips.register(a.qtipCfg);
13031             }
13032             this.initEvents();
13033             if(!this.node.expanded){
13034                 this.updateExpandIcon();
13035             }
13036         }else{
13037             if(bulkRender === true) {
13038                 targetNode.appendChild(this.wrap);
13039             }
13040         }
13041     },
13042
13043     renderElements : function(n, a, targetNode, bulkRender)
13044     {
13045         // add some indent caching, this helps performance when rendering a large tree
13046         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13047         var t = n.getOwnerTree();
13048         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13049         if (typeof(n.attributes.html) != 'undefined') {
13050             txt = n.attributes.html;
13051         }
13052         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13053         var cb = typeof a.checked == 'boolean';
13054         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13055         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13056             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13057             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13058             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13059             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13060             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13061              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13062                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13063             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13064             "</li>"];
13065
13066         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13067             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13068                                 n.nextSibling.ui.getEl(), buf.join(""));
13069         }else{
13070             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13071         }
13072
13073         this.elNode = this.wrap.childNodes[0];
13074         this.ctNode = this.wrap.childNodes[1];
13075         var cs = this.elNode.childNodes;
13076         this.indentNode = cs[0];
13077         this.ecNode = cs[1];
13078         this.iconNode = cs[2];
13079         var index = 3;
13080         if(cb){
13081             this.checkbox = cs[3];
13082             index++;
13083         }
13084         this.anchor = cs[index];
13085         this.textNode = cs[index].firstChild;
13086     },
13087
13088     getAnchor : function(){
13089         return this.anchor;
13090     },
13091
13092     getTextEl : function(){
13093         return this.textNode;
13094     },
13095
13096     getIconEl : function(){
13097         return this.iconNode;
13098     },
13099
13100     isChecked : function(){
13101         return this.checkbox ? this.checkbox.checked : false;
13102     },
13103
13104     updateExpandIcon : function(){
13105         if(this.rendered){
13106             var n = this.node, c1, c2;
13107             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13108             var hasChild = n.hasChildNodes();
13109             if(hasChild){
13110                 if(n.expanded){
13111                     cls += "-minus";
13112                     c1 = "x-tree-node-collapsed";
13113                     c2 = "x-tree-node-expanded";
13114                 }else{
13115                     cls += "-plus";
13116                     c1 = "x-tree-node-expanded";
13117                     c2 = "x-tree-node-collapsed";
13118                 }
13119                 if(this.wasLeaf){
13120                     this.removeClass("x-tree-node-leaf");
13121                     this.wasLeaf = false;
13122                 }
13123                 if(this.c1 != c1 || this.c2 != c2){
13124                     Roo.fly(this.elNode).replaceClass(c1, c2);
13125                     this.c1 = c1; this.c2 = c2;
13126                 }
13127             }else{
13128                 // this changes non-leafs into leafs if they have no children.
13129                 // it's not very rational behaviour..
13130                 
13131                 if(!this.wasLeaf && this.node.leaf){
13132                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13133                     delete this.c1;
13134                     delete this.c2;
13135                     this.wasLeaf = true;
13136                 }
13137             }
13138             var ecc = "x-tree-ec-icon "+cls;
13139             if(this.ecc != ecc){
13140                 this.ecNode.className = ecc;
13141                 this.ecc = ecc;
13142             }
13143         }
13144     },
13145
13146     getChildIndent : function(){
13147         if(!this.childIndent){
13148             var buf = [];
13149             var p = this.node;
13150             while(p){
13151                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13152                     if(!p.isLast()) {
13153                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13154                     } else {
13155                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13156                     }
13157                 }
13158                 p = p.parentNode;
13159             }
13160             this.childIndent = buf.join("");
13161         }
13162         return this.childIndent;
13163     },
13164
13165     renderIndent : function(){
13166         if(this.rendered){
13167             var indent = "";
13168             var p = this.node.parentNode;
13169             if(p){
13170                 indent = p.ui.getChildIndent();
13171             }
13172             if(this.indentMarkup != indent){ // don't rerender if not required
13173                 this.indentNode.innerHTML = indent;
13174                 this.indentMarkup = indent;
13175             }
13176             this.updateExpandIcon();
13177         }
13178     }
13179 };
13180
13181 Roo.tree.RootTreeNodeUI = function(){
13182     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13183 };
13184 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13185     render : function(){
13186         if(!this.rendered){
13187             var targetNode = this.node.ownerTree.innerCt.dom;
13188             this.node.expanded = true;
13189             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13190             this.wrap = this.ctNode = targetNode.firstChild;
13191         }
13192     },
13193     collapse : function(){
13194     },
13195     expand : function(){
13196     }
13197 });/*
13198  * Based on:
13199  * Ext JS Library 1.1.1
13200  * Copyright(c) 2006-2007, Ext JS, LLC.
13201  *
13202  * Originally Released Under LGPL - original licence link has changed is not relivant.
13203  *
13204  * Fork - LGPL
13205  * <script type="text/javascript">
13206  */
13207 /**
13208  * @class Roo.tree.TreeLoader
13209  * @extends Roo.util.Observable
13210  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13211  * nodes from a specified URL. The response must be a javascript Array definition
13212  * who's elements are node definition objects. eg:
13213  * <pre><code>
13214 {  success : true,
13215    data :      [
13216    
13217     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13218     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13219     ]
13220 }
13221
13222
13223 </code></pre>
13224  * <br><br>
13225  * The old style respose with just an array is still supported, but not recommended.
13226  * <br><br>
13227  *
13228  * A server request is sent, and child nodes are loaded only when a node is expanded.
13229  * The loading node's id is passed to the server under the parameter name "node" to
13230  * enable the server to produce the correct child nodes.
13231  * <br><br>
13232  * To pass extra parameters, an event handler may be attached to the "beforeload"
13233  * event, and the parameters specified in the TreeLoader's baseParams property:
13234  * <pre><code>
13235     myTreeLoader.on("beforeload", function(treeLoader, node) {
13236         this.baseParams.category = node.attributes.category;
13237     }, this);
13238     
13239 </code></pre>
13240  *
13241  * This would pass an HTTP parameter called "category" to the server containing
13242  * the value of the Node's "category" attribute.
13243  * @constructor
13244  * Creates a new Treeloader.
13245  * @param {Object} config A config object containing config properties.
13246  */
13247 Roo.tree.TreeLoader = function(config){
13248     this.baseParams = {};
13249     this.requestMethod = "POST";
13250     Roo.apply(this, config);
13251
13252     this.addEvents({
13253     
13254         /**
13255          * @event beforeload
13256          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13257          * @param {Object} This TreeLoader object.
13258          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13259          * @param {Object} callback The callback function specified in the {@link #load} call.
13260          */
13261         beforeload : true,
13262         /**
13263          * @event load
13264          * Fires when the node has been successfuly loaded.
13265          * @param {Object} This TreeLoader object.
13266          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13267          * @param {Object} response The response object containing the data from the server.
13268          */
13269         load : true,
13270         /**
13271          * @event loadexception
13272          * Fires if the network request failed.
13273          * @param {Object} This TreeLoader object.
13274          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13275          * @param {Object} response The response object containing the data from the server.
13276          */
13277         loadexception : true,
13278         /**
13279          * @event create
13280          * Fires before a node is created, enabling you to return custom Node types 
13281          * @param {Object} This TreeLoader object.
13282          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13283          */
13284         create : true
13285     });
13286
13287     Roo.tree.TreeLoader.superclass.constructor.call(this);
13288 };
13289
13290 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13291     /**
13292     * @cfg {String} dataUrl The URL from which to request a Json string which
13293     * specifies an array of node definition object representing the child nodes
13294     * to be loaded.
13295     */
13296     /**
13297     * @cfg {String} requestMethod either GET or POST
13298     * defaults to POST (due to BC)
13299     * to be loaded.
13300     */
13301     /**
13302     * @cfg {Object} baseParams (optional) An object containing properties which
13303     * specify HTTP parameters to be passed to each request for child nodes.
13304     */
13305     /**
13306     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13307     * created by this loader. If the attributes sent by the server have an attribute in this object,
13308     * they take priority.
13309     */
13310     /**
13311     * @cfg {Object} uiProviders (optional) An object containing properties which
13312     * 
13313     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13314     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13315     * <i>uiProvider</i> attribute of a returned child node is a string rather
13316     * than a reference to a TreeNodeUI implementation, this that string value
13317     * is used as a property name in the uiProviders object. You can define the provider named
13318     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13319     */
13320     uiProviders : {},
13321
13322     /**
13323     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13324     * child nodes before loading.
13325     */
13326     clearOnLoad : true,
13327
13328     /**
13329     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13330     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13331     * Grid query { data : [ .....] }
13332     */
13333     
13334     root : false,
13335      /**
13336     * @cfg {String} queryParam (optional) 
13337     * Name of the query as it will be passed on the querystring (defaults to 'node')
13338     * eg. the request will be ?node=[id]
13339     */
13340     
13341     
13342     queryParam: false,
13343     
13344     /**
13345      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13346      * This is called automatically when a node is expanded, but may be used to reload
13347      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13348      * @param {Roo.tree.TreeNode} node
13349      * @param {Function} callback
13350      */
13351     load : function(node, callback){
13352         if(this.clearOnLoad){
13353             while(node.firstChild){
13354                 node.removeChild(node.firstChild);
13355             }
13356         }
13357         if(node.attributes.children){ // preloaded json children
13358             var cs = node.attributes.children;
13359             for(var i = 0, len = cs.length; i < len; i++){
13360                 node.appendChild(this.createNode(cs[i]));
13361             }
13362             if(typeof callback == "function"){
13363                 callback();
13364             }
13365         }else if(this.dataUrl){
13366             this.requestData(node, callback);
13367         }
13368     },
13369
13370     getParams: function(node){
13371         var buf = [], bp = this.baseParams;
13372         for(var key in bp){
13373             if(typeof bp[key] != "function"){
13374                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13375             }
13376         }
13377         var n = this.queryParam === false ? 'node' : this.queryParam;
13378         buf.push(n + "=", encodeURIComponent(node.id));
13379         return buf.join("");
13380     },
13381
13382     requestData : function(node, callback){
13383         if(this.fireEvent("beforeload", this, node, callback) !== false){
13384             this.transId = Roo.Ajax.request({
13385                 method:this.requestMethod,
13386                 url: this.dataUrl||this.url,
13387                 success: this.handleResponse,
13388                 failure: this.handleFailure,
13389                 scope: this,
13390                 argument: {callback: callback, node: node},
13391                 params: this.getParams(node)
13392             });
13393         }else{
13394             // if the load is cancelled, make sure we notify
13395             // the node that we are done
13396             if(typeof callback == "function"){
13397                 callback();
13398             }
13399         }
13400     },
13401
13402     isLoading : function(){
13403         return this.transId ? true : false;
13404     },
13405
13406     abort : function(){
13407         if(this.isLoading()){
13408             Roo.Ajax.abort(this.transId);
13409         }
13410     },
13411
13412     // private
13413     createNode : function(attr)
13414     {
13415         // apply baseAttrs, nice idea Corey!
13416         if(this.baseAttrs){
13417             Roo.applyIf(attr, this.baseAttrs);
13418         }
13419         if(this.applyLoader !== false){
13420             attr.loader = this;
13421         }
13422         // uiProvider = depreciated..
13423         
13424         if(typeof(attr.uiProvider) == 'string'){
13425            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13426                 /**  eval:var:attr */ eval(attr.uiProvider);
13427         }
13428         if(typeof(this.uiProviders['default']) != 'undefined') {
13429             attr.uiProvider = this.uiProviders['default'];
13430         }
13431         
13432         this.fireEvent('create', this, attr);
13433         
13434         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13435         return(attr.leaf ?
13436                         new Roo.tree.TreeNode(attr) :
13437                         new Roo.tree.AsyncTreeNode(attr));
13438     },
13439
13440     processResponse : function(response, node, callback)
13441     {
13442         var json = response.responseText;
13443         try {
13444             
13445             var o = Roo.decode(json);
13446             
13447             if (this.root === false && typeof(o.success) != undefined) {
13448                 this.root = 'data'; // the default behaviour for list like data..
13449                 }
13450                 
13451             if (this.root !== false &&  !o.success) {
13452                 // it's a failure condition.
13453                 var a = response.argument;
13454                 this.fireEvent("loadexception", this, a.node, response);
13455                 Roo.log("Load failed - should have a handler really");
13456                 return;
13457             }
13458             
13459             
13460             
13461             if (this.root !== false) {
13462                  o = o[this.root];
13463             }
13464             
13465             for(var i = 0, len = o.length; i < len; i++){
13466                 var n = this.createNode(o[i]);
13467                 if(n){
13468                     node.appendChild(n);
13469                 }
13470             }
13471             if(typeof callback == "function"){
13472                 callback(this, node);
13473             }
13474         }catch(e){
13475             this.handleFailure(response);
13476         }
13477     },
13478
13479     handleResponse : function(response){
13480         this.transId = false;
13481         var a = response.argument;
13482         this.processResponse(response, a.node, a.callback);
13483         this.fireEvent("load", this, a.node, response);
13484     },
13485
13486     handleFailure : function(response)
13487     {
13488         // should handle failure better..
13489         this.transId = false;
13490         var a = response.argument;
13491         this.fireEvent("loadexception", this, a.node, response);
13492         if(typeof a.callback == "function"){
13493             a.callback(this, a.node);
13494         }
13495     }
13496 });/*
13497  * Based on:
13498  * Ext JS Library 1.1.1
13499  * Copyright(c) 2006-2007, Ext JS, LLC.
13500  *
13501  * Originally Released Under LGPL - original licence link has changed is not relivant.
13502  *
13503  * Fork - LGPL
13504  * <script type="text/javascript">
13505  */
13506
13507 /**
13508 * @class Roo.tree.TreeFilter
13509 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13510 * @param {TreePanel} tree
13511 * @param {Object} config (optional)
13512  */
13513 Roo.tree.TreeFilter = function(tree, config){
13514     this.tree = tree;
13515     this.filtered = {};
13516     Roo.apply(this, config);
13517 };
13518
13519 Roo.tree.TreeFilter.prototype = {
13520     clearBlank:false,
13521     reverse:false,
13522     autoClear:false,
13523     remove:false,
13524
13525      /**
13526      * Filter the data by a specific attribute.
13527      * @param {String/RegExp} value Either string that the attribute value
13528      * should start with or a RegExp to test against the attribute
13529      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13530      * @param {TreeNode} startNode (optional) The node to start the filter at.
13531      */
13532     filter : function(value, attr, startNode){
13533         attr = attr || "text";
13534         var f;
13535         if(typeof value == "string"){
13536             var vlen = value.length;
13537             // auto clear empty filter
13538             if(vlen == 0 && this.clearBlank){
13539                 this.clear();
13540                 return;
13541             }
13542             value = value.toLowerCase();
13543             f = function(n){
13544                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13545             };
13546         }else if(value.exec){ // regex?
13547             f = function(n){
13548                 return value.test(n.attributes[attr]);
13549             };
13550         }else{
13551             throw 'Illegal filter type, must be string or regex';
13552         }
13553         this.filterBy(f, null, startNode);
13554         },
13555
13556     /**
13557      * Filter by a function. The passed function will be called with each
13558      * node in the tree (or from the startNode). If the function returns true, the node is kept
13559      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13560      * @param {Function} fn The filter function
13561      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13562      */
13563     filterBy : function(fn, scope, startNode){
13564         startNode = startNode || this.tree.root;
13565         if(this.autoClear){
13566             this.clear();
13567         }
13568         var af = this.filtered, rv = this.reverse;
13569         var f = function(n){
13570             if(n == startNode){
13571                 return true;
13572             }
13573             if(af[n.id]){
13574                 return false;
13575             }
13576             var m = fn.call(scope || n, n);
13577             if(!m || rv){
13578                 af[n.id] = n;
13579                 n.ui.hide();
13580                 return false;
13581             }
13582             return true;
13583         };
13584         startNode.cascade(f);
13585         if(this.remove){
13586            for(var id in af){
13587                if(typeof id != "function"){
13588                    var n = af[id];
13589                    if(n && n.parentNode){
13590                        n.parentNode.removeChild(n);
13591                    }
13592                }
13593            }
13594         }
13595     },
13596
13597     /**
13598      * Clears the current filter. Note: with the "remove" option
13599      * set a filter cannot be cleared.
13600      */
13601     clear : function(){
13602         var t = this.tree;
13603         var af = this.filtered;
13604         for(var id in af){
13605             if(typeof id != "function"){
13606                 var n = af[id];
13607                 if(n){
13608                     n.ui.show();
13609                 }
13610             }
13611         }
13612         this.filtered = {};
13613     }
13614 };
13615 /*
13616  * Based on:
13617  * Ext JS Library 1.1.1
13618  * Copyright(c) 2006-2007, Ext JS, LLC.
13619  *
13620  * Originally Released Under LGPL - original licence link has changed is not relivant.
13621  *
13622  * Fork - LGPL
13623  * <script type="text/javascript">
13624  */
13625  
13626
13627 /**
13628  * @class Roo.tree.TreeSorter
13629  * Provides sorting of nodes in a TreePanel
13630  * 
13631  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13632  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13633  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13634  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13635  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13636  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13637  * @constructor
13638  * @param {TreePanel} tree
13639  * @param {Object} config
13640  */
13641 Roo.tree.TreeSorter = function(tree, config){
13642     Roo.apply(this, config);
13643     tree.on("beforechildrenrendered", this.doSort, this);
13644     tree.on("append", this.updateSort, this);
13645     tree.on("insert", this.updateSort, this);
13646     
13647     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13648     var p = this.property || "text";
13649     var sortType = this.sortType;
13650     var fs = this.folderSort;
13651     var cs = this.caseSensitive === true;
13652     var leafAttr = this.leafAttr || 'leaf';
13653
13654     this.sortFn = function(n1, n2){
13655         if(fs){
13656             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13657                 return 1;
13658             }
13659             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13660                 return -1;
13661             }
13662         }
13663         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13664         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13665         if(v1 < v2){
13666                         return dsc ? +1 : -1;
13667                 }else if(v1 > v2){
13668                         return dsc ? -1 : +1;
13669         }else{
13670                 return 0;
13671         }
13672     };
13673 };
13674
13675 Roo.tree.TreeSorter.prototype = {
13676     doSort : function(node){
13677         node.sort(this.sortFn);
13678     },
13679     
13680     compareNodes : function(n1, n2){
13681         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13682     },
13683     
13684     updateSort : function(tree, node){
13685         if(node.childrenRendered){
13686             this.doSort.defer(1, this, [node]);
13687         }
13688     }
13689 };/*
13690  * Based on:
13691  * Ext JS Library 1.1.1
13692  * Copyright(c) 2006-2007, Ext JS, LLC.
13693  *
13694  * Originally Released Under LGPL - original licence link has changed is not relivant.
13695  *
13696  * Fork - LGPL
13697  * <script type="text/javascript">
13698  */
13699
13700 if(Roo.dd.DropZone){
13701     
13702 Roo.tree.TreeDropZone = function(tree, config){
13703     this.allowParentInsert = false;
13704     this.allowContainerDrop = false;
13705     this.appendOnly = false;
13706     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13707     this.tree = tree;
13708     this.lastInsertClass = "x-tree-no-status";
13709     this.dragOverData = {};
13710 };
13711
13712 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13713     ddGroup : "TreeDD",
13714     scroll:  true,
13715     
13716     expandDelay : 1000,
13717     
13718     expandNode : function(node){
13719         if(node.hasChildNodes() && !node.isExpanded()){
13720             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13721         }
13722     },
13723     
13724     queueExpand : function(node){
13725         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13726     },
13727     
13728     cancelExpand : function(){
13729         if(this.expandProcId){
13730             clearTimeout(this.expandProcId);
13731             this.expandProcId = false;
13732         }
13733     },
13734     
13735     isValidDropPoint : function(n, pt, dd, e, data){
13736         if(!n || !data){ return false; }
13737         var targetNode = n.node;
13738         var dropNode = data.node;
13739         // default drop rules
13740         if(!(targetNode && targetNode.isTarget && pt)){
13741             return false;
13742         }
13743         if(pt == "append" && targetNode.allowChildren === false){
13744             return false;
13745         }
13746         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13747             return false;
13748         }
13749         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13750             return false;
13751         }
13752         // reuse the object
13753         var overEvent = this.dragOverData;
13754         overEvent.tree = this.tree;
13755         overEvent.target = targetNode;
13756         overEvent.data = data;
13757         overEvent.point = pt;
13758         overEvent.source = dd;
13759         overEvent.rawEvent = e;
13760         overEvent.dropNode = dropNode;
13761         overEvent.cancel = false;  
13762         var result = this.tree.fireEvent("nodedragover", overEvent);
13763         return overEvent.cancel === false && result !== false;
13764     },
13765     
13766     getDropPoint : function(e, n, dd)
13767     {
13768         var tn = n.node;
13769         if(tn.isRoot){
13770             return tn.allowChildren !== false ? "append" : false; // always append for root
13771         }
13772         var dragEl = n.ddel;
13773         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13774         var y = Roo.lib.Event.getPageY(e);
13775         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13776         
13777         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13778         var noAppend = tn.allowChildren === false;
13779         if(this.appendOnly || tn.parentNode.allowChildren === false){
13780             return noAppend ? false : "append";
13781         }
13782         var noBelow = false;
13783         if(!this.allowParentInsert){
13784             noBelow = tn.hasChildNodes() && tn.isExpanded();
13785         }
13786         var q = (b - t) / (noAppend ? 2 : 3);
13787         if(y >= t && y < (t + q)){
13788             return "above";
13789         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13790             return "below";
13791         }else{
13792             return "append";
13793         }
13794     },
13795     
13796     onNodeEnter : function(n, dd, e, data)
13797     {
13798         this.cancelExpand();
13799     },
13800     
13801     onNodeOver : function(n, dd, e, data)
13802     {
13803        
13804         var pt = this.getDropPoint(e, n, dd);
13805         var node = n.node;
13806         
13807         // auto node expand check
13808         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13809             this.queueExpand(node);
13810         }else if(pt != "append"){
13811             this.cancelExpand();
13812         }
13813         
13814         // set the insert point style on the target node
13815         var returnCls = this.dropNotAllowed;
13816         if(this.isValidDropPoint(n, pt, dd, e, data)){
13817            if(pt){
13818                var el = n.ddel;
13819                var cls;
13820                if(pt == "above"){
13821                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13822                    cls = "x-tree-drag-insert-above";
13823                }else if(pt == "below"){
13824                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13825                    cls = "x-tree-drag-insert-below";
13826                }else{
13827                    returnCls = "x-tree-drop-ok-append";
13828                    cls = "x-tree-drag-append";
13829                }
13830                if(this.lastInsertClass != cls){
13831                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13832                    this.lastInsertClass = cls;
13833                }
13834            }
13835        }
13836        return returnCls;
13837     },
13838     
13839     onNodeOut : function(n, dd, e, data){
13840         
13841         this.cancelExpand();
13842         this.removeDropIndicators(n);
13843     },
13844     
13845     onNodeDrop : function(n, dd, e, data){
13846         var point = this.getDropPoint(e, n, dd);
13847         var targetNode = n.node;
13848         targetNode.ui.startDrop();
13849         if(!this.isValidDropPoint(n, point, dd, e, data)){
13850             targetNode.ui.endDrop();
13851             return false;
13852         }
13853         // first try to find the drop node
13854         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13855         var dropEvent = {
13856             tree : this.tree,
13857             target: targetNode,
13858             data: data,
13859             point: point,
13860             source: dd,
13861             rawEvent: e,
13862             dropNode: dropNode,
13863             cancel: !dropNode   
13864         };
13865         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13866         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13867             targetNode.ui.endDrop();
13868             return false;
13869         }
13870         // allow target changing
13871         targetNode = dropEvent.target;
13872         if(point == "append" && !targetNode.isExpanded()){
13873             targetNode.expand(false, null, function(){
13874                 this.completeDrop(dropEvent);
13875             }.createDelegate(this));
13876         }else{
13877             this.completeDrop(dropEvent);
13878         }
13879         return true;
13880     },
13881     
13882     completeDrop : function(de){
13883         var ns = de.dropNode, p = de.point, t = de.target;
13884         if(!(ns instanceof Array)){
13885             ns = [ns];
13886         }
13887         var n;
13888         for(var i = 0, len = ns.length; i < len; i++){
13889             n = ns[i];
13890             if(p == "above"){
13891                 t.parentNode.insertBefore(n, t);
13892             }else if(p == "below"){
13893                 t.parentNode.insertBefore(n, t.nextSibling);
13894             }else{
13895                 t.appendChild(n);
13896             }
13897         }
13898         n.ui.focus();
13899         if(this.tree.hlDrop){
13900             n.ui.highlight();
13901         }
13902         t.ui.endDrop();
13903         this.tree.fireEvent("nodedrop", de);
13904     },
13905     
13906     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13907         if(this.tree.hlDrop){
13908             dropNode.ui.focus();
13909             dropNode.ui.highlight();
13910         }
13911         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13912     },
13913     
13914     getTree : function(){
13915         return this.tree;
13916     },
13917     
13918     removeDropIndicators : function(n){
13919         if(n && n.ddel){
13920             var el = n.ddel;
13921             Roo.fly(el).removeClass([
13922                     "x-tree-drag-insert-above",
13923                     "x-tree-drag-insert-below",
13924                     "x-tree-drag-append"]);
13925             this.lastInsertClass = "_noclass";
13926         }
13927     },
13928     
13929     beforeDragDrop : function(target, e, id){
13930         this.cancelExpand();
13931         return true;
13932     },
13933     
13934     afterRepair : function(data){
13935         if(data && Roo.enableFx){
13936             data.node.ui.highlight();
13937         }
13938         this.hideProxy();
13939     } 
13940     
13941 });
13942
13943 }
13944 /*
13945  * Based on:
13946  * Ext JS Library 1.1.1
13947  * Copyright(c) 2006-2007, Ext JS, LLC.
13948  *
13949  * Originally Released Under LGPL - original licence link has changed is not relivant.
13950  *
13951  * Fork - LGPL
13952  * <script type="text/javascript">
13953  */
13954  
13955
13956 if(Roo.dd.DragZone){
13957 Roo.tree.TreeDragZone = function(tree, config){
13958     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13959     this.tree = tree;
13960 };
13961
13962 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13963     ddGroup : "TreeDD",
13964    
13965     onBeforeDrag : function(data, e){
13966         var n = data.node;
13967         return n && n.draggable && !n.disabled;
13968     },
13969      
13970     
13971     onInitDrag : function(e){
13972         var data = this.dragData;
13973         this.tree.getSelectionModel().select(data.node);
13974         this.proxy.update("");
13975         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13976         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13977     },
13978     
13979     getRepairXY : function(e, data){
13980         return data.node.ui.getDDRepairXY();
13981     },
13982     
13983     onEndDrag : function(data, e){
13984         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13985         
13986         
13987     },
13988     
13989     onValidDrop : function(dd, e, id){
13990         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13991         this.hideProxy();
13992     },
13993     
13994     beforeInvalidDrop : function(e, id){
13995         // this scrolls the original position back into view
13996         var sm = this.tree.getSelectionModel();
13997         sm.clearSelections();
13998         sm.select(this.dragData.node);
13999     }
14000 });
14001 }/*
14002  * Based on:
14003  * Ext JS Library 1.1.1
14004  * Copyright(c) 2006-2007, Ext JS, LLC.
14005  *
14006  * Originally Released Under LGPL - original licence link has changed is not relivant.
14007  *
14008  * Fork - LGPL
14009  * <script type="text/javascript">
14010  */
14011 /**
14012  * @class Roo.tree.TreeEditor
14013  * @extends Roo.Editor
14014  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14015  * as the editor field.
14016  * @constructor
14017  * @param {Object} config (used to be the tree panel.)
14018  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14019  * 
14020  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14021  * @cfg {Roo.form.TextField} field [required] The field configuration
14022  *
14023  * 
14024  */
14025 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14026     var tree = config;
14027     var field;
14028     if (oldconfig) { // old style..
14029         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14030     } else {
14031         // new style..
14032         tree = config.tree;
14033         config.field = config.field  || {};
14034         config.field.xtype = 'TextField';
14035         field = Roo.factory(config.field, Roo.form);
14036     }
14037     config = config || {};
14038     
14039     
14040     this.addEvents({
14041         /**
14042          * @event beforenodeedit
14043          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14044          * false from the handler of this event.
14045          * @param {Editor} this
14046          * @param {Roo.tree.Node} node 
14047          */
14048         "beforenodeedit" : true
14049     });
14050     
14051     //Roo.log(config);
14052     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14053
14054     this.tree = tree;
14055
14056     tree.on('beforeclick', this.beforeNodeClick, this);
14057     tree.getTreeEl().on('mousedown', this.hide, this);
14058     this.on('complete', this.updateNode, this);
14059     this.on('beforestartedit', this.fitToTree, this);
14060     this.on('startedit', this.bindScroll, this, {delay:10});
14061     this.on('specialkey', this.onSpecialKey, this);
14062 };
14063
14064 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14065     /**
14066      * @cfg {String} alignment
14067      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14068      */
14069     alignment: "l-l",
14070     // inherit
14071     autoSize: false,
14072     /**
14073      * @cfg {Boolean} hideEl
14074      * True to hide the bound element while the editor is displayed (defaults to false)
14075      */
14076     hideEl : false,
14077     /**
14078      * @cfg {String} cls
14079      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14080      */
14081     cls: "x-small-editor x-tree-editor",
14082     /**
14083      * @cfg {Boolean} shim
14084      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14085      */
14086     shim:false,
14087     // inherit
14088     shadow:"frame",
14089     /**
14090      * @cfg {Number} maxWidth
14091      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14092      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14093      * scroll and client offsets into account prior to each edit.
14094      */
14095     maxWidth: 250,
14096
14097     editDelay : 350,
14098
14099     // private
14100     fitToTree : function(ed, el){
14101         var td = this.tree.getTreeEl().dom, nd = el.dom;
14102         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14103             td.scrollLeft = nd.offsetLeft;
14104         }
14105         var w = Math.min(
14106                 this.maxWidth,
14107                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14108         this.setSize(w, '');
14109         
14110         return this.fireEvent('beforenodeedit', this, this.editNode);
14111         
14112     },
14113
14114     // private
14115     triggerEdit : function(node){
14116         this.completeEdit();
14117         this.editNode = node;
14118         this.startEdit(node.ui.textNode, node.text);
14119     },
14120
14121     // private
14122     bindScroll : function(){
14123         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14124     },
14125
14126     // private
14127     beforeNodeClick : function(node, e){
14128         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14129         this.lastClick = new Date();
14130         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14131             e.stopEvent();
14132             this.triggerEdit(node);
14133             return false;
14134         }
14135         return true;
14136     },
14137
14138     // private
14139     updateNode : function(ed, value){
14140         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14141         this.editNode.setText(value);
14142     },
14143
14144     // private
14145     onHide : function(){
14146         Roo.tree.TreeEditor.superclass.onHide.call(this);
14147         if(this.editNode){
14148             this.editNode.ui.focus();
14149         }
14150     },
14151
14152     // private
14153     onSpecialKey : function(field, e){
14154         var k = e.getKey();
14155         if(k == e.ESC){
14156             e.stopEvent();
14157             this.cancelEdit();
14158         }else if(k == e.ENTER && !e.hasModifier()){
14159             e.stopEvent();
14160             this.completeEdit();
14161         }
14162     }
14163 });//<Script type="text/javascript">
14164 /*
14165  * Based on:
14166  * Ext JS Library 1.1.1
14167  * Copyright(c) 2006-2007, Ext JS, LLC.
14168  *
14169  * Originally Released Under LGPL - original licence link has changed is not relivant.
14170  *
14171  * Fork - LGPL
14172  * <script type="text/javascript">
14173  */
14174  
14175 /**
14176  * Not documented??? - probably should be...
14177  */
14178
14179 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14180     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14181     
14182     renderElements : function(n, a, targetNode, bulkRender){
14183         //consel.log("renderElements?");
14184         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14185
14186         var t = n.getOwnerTree();
14187         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14188         
14189         var cols = t.columns;
14190         var bw = t.borderWidth;
14191         var c = cols[0];
14192         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14193          var cb = typeof a.checked == "boolean";
14194         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14195         var colcls = 'x-t-' + tid + '-c0';
14196         var buf = [
14197             '<li class="x-tree-node">',
14198             
14199                 
14200                 '<div class="x-tree-node-el ', a.cls,'">',
14201                     // extran...
14202                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14203                 
14204                 
14205                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14206                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14207                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14208                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14209                            (a.iconCls ? ' '+a.iconCls : ''),
14210                            '" unselectable="on" />',
14211                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14212                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14213                              
14214                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14215                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14216                             '<span unselectable="on" qtip="' + tx + '">',
14217                              tx,
14218                              '</span></a>' ,
14219                     '</div>',
14220                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14221                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14222                  ];
14223         for(var i = 1, len = cols.length; i < len; i++){
14224             c = cols[i];
14225             colcls = 'x-t-' + tid + '-c' +i;
14226             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14227             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14228                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14229                       "</div>");
14230          }
14231          
14232          buf.push(
14233             '</a>',
14234             '<div class="x-clear"></div></div>',
14235             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14236             "</li>");
14237         
14238         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14239             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14240                                 n.nextSibling.ui.getEl(), buf.join(""));
14241         }else{
14242             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14243         }
14244         var el = this.wrap.firstChild;
14245         this.elRow = el;
14246         this.elNode = el.firstChild;
14247         this.ranchor = el.childNodes[1];
14248         this.ctNode = this.wrap.childNodes[1];
14249         var cs = el.firstChild.childNodes;
14250         this.indentNode = cs[0];
14251         this.ecNode = cs[1];
14252         this.iconNode = cs[2];
14253         var index = 3;
14254         if(cb){
14255             this.checkbox = cs[3];
14256             index++;
14257         }
14258         this.anchor = cs[index];
14259         
14260         this.textNode = cs[index].firstChild;
14261         
14262         //el.on("click", this.onClick, this);
14263         //el.on("dblclick", this.onDblClick, this);
14264         
14265         
14266        // console.log(this);
14267     },
14268     initEvents : function(){
14269         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14270         
14271             
14272         var a = this.ranchor;
14273
14274         var el = Roo.get(a);
14275
14276         if(Roo.isOpera){ // opera render bug ignores the CSS
14277             el.setStyle("text-decoration", "none");
14278         }
14279
14280         el.on("click", this.onClick, this);
14281         el.on("dblclick", this.onDblClick, this);
14282         el.on("contextmenu", this.onContextMenu, this);
14283         
14284     },
14285     
14286     /*onSelectedChange : function(state){
14287         if(state){
14288             this.focus();
14289             this.addClass("x-tree-selected");
14290         }else{
14291             //this.blur();
14292             this.removeClass("x-tree-selected");
14293         }
14294     },*/
14295     addClass : function(cls){
14296         if(this.elRow){
14297             Roo.fly(this.elRow).addClass(cls);
14298         }
14299         
14300     },
14301     
14302     
14303     removeClass : function(cls){
14304         if(this.elRow){
14305             Roo.fly(this.elRow).removeClass(cls);
14306         }
14307     }
14308
14309     
14310     
14311 });//<Script type="text/javascript">
14312
14313 /*
14314  * Based on:
14315  * Ext JS Library 1.1.1
14316  * Copyright(c) 2006-2007, Ext JS, LLC.
14317  *
14318  * Originally Released Under LGPL - original licence link has changed is not relivant.
14319  *
14320  * Fork - LGPL
14321  * <script type="text/javascript">
14322  */
14323  
14324
14325 /**
14326  * @class Roo.tree.ColumnTree
14327  * @extends Roo.tree.TreePanel
14328  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14329  * @cfg {int} borderWidth  compined right/left border allowance
14330  * @constructor
14331  * @param {String/HTMLElement/Element} el The container element
14332  * @param {Object} config
14333  */
14334 Roo.tree.ColumnTree =  function(el, config)
14335 {
14336    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14337    this.addEvents({
14338         /**
14339         * @event resize
14340         * Fire this event on a container when it resizes
14341         * @param {int} w Width
14342         * @param {int} h Height
14343         */
14344        "resize" : true
14345     });
14346     this.on('resize', this.onResize, this);
14347 };
14348
14349 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14350     //lines:false,
14351     
14352     
14353     borderWidth: Roo.isBorderBox ? 0 : 2, 
14354     headEls : false,
14355     
14356     render : function(){
14357         // add the header.....
14358        
14359         Roo.tree.ColumnTree.superclass.render.apply(this);
14360         
14361         this.el.addClass('x-column-tree');
14362         
14363         this.headers = this.el.createChild(
14364             {cls:'x-tree-headers'},this.innerCt.dom);
14365    
14366         var cols = this.columns, c;
14367         var totalWidth = 0;
14368         this.headEls = [];
14369         var  len = cols.length;
14370         for(var i = 0; i < len; i++){
14371              c = cols[i];
14372              totalWidth += c.width;
14373             this.headEls.push(this.headers.createChild({
14374                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14375                  cn: {
14376                      cls:'x-tree-hd-text',
14377                      html: c.header
14378                  },
14379                  style:'width:'+(c.width-this.borderWidth)+'px;'
14380              }));
14381         }
14382         this.headers.createChild({cls:'x-clear'});
14383         // prevent floats from wrapping when clipped
14384         this.headers.setWidth(totalWidth);
14385         //this.innerCt.setWidth(totalWidth);
14386         this.innerCt.setStyle({ overflow: 'auto' });
14387         this.onResize(this.width, this.height);
14388              
14389         
14390     },
14391     onResize : function(w,h)
14392     {
14393         this.height = h;
14394         this.width = w;
14395         // resize cols..
14396         this.innerCt.setWidth(this.width);
14397         this.innerCt.setHeight(this.height-20);
14398         
14399         // headers...
14400         var cols = this.columns, c;
14401         var totalWidth = 0;
14402         var expEl = false;
14403         var len = cols.length;
14404         for(var i = 0; i < len; i++){
14405             c = cols[i];
14406             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14407                 // it's the expander..
14408                 expEl  = this.headEls[i];
14409                 continue;
14410             }
14411             totalWidth += c.width;
14412             
14413         }
14414         if (expEl) {
14415             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14416         }
14417         this.headers.setWidth(w-20);
14418
14419         
14420         
14421         
14422     }
14423 });
14424 /*
14425  * Based on:
14426  * Ext JS Library 1.1.1
14427  * Copyright(c) 2006-2007, Ext JS, LLC.
14428  *
14429  * Originally Released Under LGPL - original licence link has changed is not relivant.
14430  *
14431  * Fork - LGPL
14432  * <script type="text/javascript">
14433  */
14434  
14435 /**
14436  * @class Roo.menu.Menu
14437  * @extends Roo.util.Observable
14438  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14439  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14440  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14441  * @constructor
14442  * Creates a new Menu
14443  * @param {Object} config Configuration options
14444  */
14445 Roo.menu.Menu = function(config){
14446     
14447     Roo.menu.Menu.superclass.constructor.call(this, config);
14448     
14449     this.id = this.id || Roo.id();
14450     this.addEvents({
14451         /**
14452          * @event beforeshow
14453          * Fires before this menu is displayed
14454          * @param {Roo.menu.Menu} this
14455          */
14456         beforeshow : true,
14457         /**
14458          * @event beforehide
14459          * Fires before this menu is hidden
14460          * @param {Roo.menu.Menu} this
14461          */
14462         beforehide : true,
14463         /**
14464          * @event show
14465          * Fires after this menu is displayed
14466          * @param {Roo.menu.Menu} this
14467          */
14468         show : true,
14469         /**
14470          * @event hide
14471          * Fires after this menu is hidden
14472          * @param {Roo.menu.Menu} this
14473          */
14474         hide : true,
14475         /**
14476          * @event click
14477          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14478          * @param {Roo.menu.Menu} this
14479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14480          * @param {Roo.EventObject} e
14481          */
14482         click : true,
14483         /**
14484          * @event mouseover
14485          * Fires when the mouse is hovering over this menu
14486          * @param {Roo.menu.Menu} this
14487          * @param {Roo.EventObject} e
14488          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14489          */
14490         mouseover : true,
14491         /**
14492          * @event mouseout
14493          * Fires when the mouse exits this menu
14494          * @param {Roo.menu.Menu} this
14495          * @param {Roo.EventObject} e
14496          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14497          */
14498         mouseout : true,
14499         /**
14500          * @event itemclick
14501          * Fires when a menu item contained in this menu is clicked
14502          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14503          * @param {Roo.EventObject} e
14504          */
14505         itemclick: true
14506     });
14507     if (this.registerMenu) {
14508         Roo.menu.MenuMgr.register(this);
14509     }
14510     
14511     var mis = this.items;
14512     this.items = new Roo.util.MixedCollection();
14513     if(mis){
14514         this.add.apply(this, mis);
14515     }
14516 };
14517
14518 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14519     /**
14520      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14521      */
14522     minWidth : 120,
14523     /**
14524      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14525      * for bottom-right shadow (defaults to "sides")
14526      */
14527     shadow : "sides",
14528     /**
14529      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14530      * this menu (defaults to "tl-tr?")
14531      */
14532     subMenuAlign : "tl-tr?",
14533     /**
14534      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14535      * relative to its element of origin (defaults to "tl-bl?")
14536      */
14537     defaultAlign : "tl-bl?",
14538     /**
14539      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14540      */
14541     allowOtherMenus : false,
14542     /**
14543      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14544      */
14545     registerMenu : true,
14546
14547     hidden:true,
14548
14549     // private
14550     render : function(){
14551         if(this.el){
14552             return;
14553         }
14554         var el = this.el = new Roo.Layer({
14555             cls: "x-menu",
14556             shadow:this.shadow,
14557             constrain: false,
14558             parentEl: this.parentEl || document.body,
14559             zindex:15000
14560         });
14561
14562         this.keyNav = new Roo.menu.MenuNav(this);
14563
14564         if(this.plain){
14565             el.addClass("x-menu-plain");
14566         }
14567         if(this.cls){
14568             el.addClass(this.cls);
14569         }
14570         // generic focus element
14571         this.focusEl = el.createChild({
14572             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14573         });
14574         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14575         //disabling touch- as it's causing issues ..
14576         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14577         ul.on('click'   , this.onClick, this);
14578         
14579         
14580         ul.on("mouseover", this.onMouseOver, this);
14581         ul.on("mouseout", this.onMouseOut, this);
14582         this.items.each(function(item){
14583             if (item.hidden) {
14584                 return;
14585             }
14586             
14587             var li = document.createElement("li");
14588             li.className = "x-menu-list-item";
14589             ul.dom.appendChild(li);
14590             item.render(li, this);
14591         }, this);
14592         this.ul = ul;
14593         this.autoWidth();
14594     },
14595
14596     // private
14597     autoWidth : function(){
14598         var el = this.el, ul = this.ul;
14599         if(!el){
14600             return;
14601         }
14602         var w = this.width;
14603         if(w){
14604             el.setWidth(w);
14605         }else if(Roo.isIE){
14606             el.setWidth(this.minWidth);
14607             var t = el.dom.offsetWidth; // force recalc
14608             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14609         }
14610     },
14611
14612     // private
14613     delayAutoWidth : function(){
14614         if(this.rendered){
14615             if(!this.awTask){
14616                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14617             }
14618             this.awTask.delay(20);
14619         }
14620     },
14621
14622     // private
14623     findTargetItem : function(e){
14624         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14625         if(t && t.menuItemId){
14626             return this.items.get(t.menuItemId);
14627         }
14628     },
14629
14630     // private
14631     onClick : function(e){
14632         Roo.log("menu.onClick");
14633         var t = this.findTargetItem(e);
14634         if(!t){
14635             return;
14636         }
14637         Roo.log(e);
14638         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14639             if(t == this.activeItem && t.shouldDeactivate(e)){
14640                 this.activeItem.deactivate();
14641                 delete this.activeItem;
14642                 return;
14643             }
14644             if(t.canActivate){
14645                 this.setActiveItem(t, true);
14646             }
14647             return;
14648             
14649             
14650         }
14651         
14652         t.onClick(e);
14653         this.fireEvent("click", this, t, e);
14654     },
14655
14656     // private
14657     setActiveItem : function(item, autoExpand){
14658         if(item != this.activeItem){
14659             if(this.activeItem){
14660                 this.activeItem.deactivate();
14661             }
14662             this.activeItem = item;
14663             item.activate(autoExpand);
14664         }else if(autoExpand){
14665             item.expandMenu();
14666         }
14667     },
14668
14669     // private
14670     tryActivate : function(start, step){
14671         var items = this.items;
14672         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14673             var item = items.get(i);
14674             if(!item.disabled && item.canActivate){
14675                 this.setActiveItem(item, false);
14676                 return item;
14677             }
14678         }
14679         return false;
14680     },
14681
14682     // private
14683     onMouseOver : function(e){
14684         var t;
14685         if(t = this.findTargetItem(e)){
14686             if(t.canActivate && !t.disabled){
14687                 this.setActiveItem(t, true);
14688             }
14689         }
14690         this.fireEvent("mouseover", this, e, t);
14691     },
14692
14693     // private
14694     onMouseOut : function(e){
14695         var t;
14696         if(t = this.findTargetItem(e)){
14697             if(t == this.activeItem && t.shouldDeactivate(e)){
14698                 this.activeItem.deactivate();
14699                 delete this.activeItem;
14700             }
14701         }
14702         this.fireEvent("mouseout", this, e, t);
14703     },
14704
14705     /**
14706      * Read-only.  Returns true if the menu is currently displayed, else false.
14707      * @type Boolean
14708      */
14709     isVisible : function(){
14710         return this.el && !this.hidden;
14711     },
14712
14713     /**
14714      * Displays this menu relative to another element
14715      * @param {String/HTMLElement/Roo.Element} element The element to align to
14716      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14717      * the element (defaults to this.defaultAlign)
14718      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14719      */
14720     show : function(el, pos, parentMenu){
14721         this.parentMenu = parentMenu;
14722         if(!this.el){
14723             this.render();
14724         }
14725         this.fireEvent("beforeshow", this);
14726         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14727     },
14728
14729     /**
14730      * Displays this menu at a specific xy position
14731      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14732      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14733      */
14734     showAt : function(xy, parentMenu, /* private: */_e){
14735         this.parentMenu = parentMenu;
14736         if(!this.el){
14737             this.render();
14738         }
14739         if(_e !== false){
14740             this.fireEvent("beforeshow", this);
14741             xy = this.el.adjustForConstraints(xy);
14742         }
14743         this.el.setXY(xy);
14744         this.el.show();
14745         this.hidden = false;
14746         this.focus();
14747         this.fireEvent("show", this);
14748     },
14749
14750     focus : function(){
14751         if(!this.hidden){
14752             this.doFocus.defer(50, this);
14753         }
14754     },
14755
14756     doFocus : function(){
14757         if(!this.hidden){
14758             this.focusEl.focus();
14759         }
14760     },
14761
14762     /**
14763      * Hides this menu and optionally all parent menus
14764      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14765      */
14766     hide : function(deep){
14767         if(this.el && this.isVisible()){
14768             this.fireEvent("beforehide", this);
14769             if(this.activeItem){
14770                 this.activeItem.deactivate();
14771                 this.activeItem = null;
14772             }
14773             this.el.hide();
14774             this.hidden = true;
14775             this.fireEvent("hide", this);
14776         }
14777         if(deep === true && this.parentMenu){
14778             this.parentMenu.hide(true);
14779         }
14780     },
14781
14782     /**
14783      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14784      * Any of the following are valid:
14785      * <ul>
14786      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14787      * <li>An HTMLElement object which will be converted to a menu item</li>
14788      * <li>A menu item config object that will be created as a new menu item</li>
14789      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14790      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14791      * </ul>
14792      * Usage:
14793      * <pre><code>
14794 // Create the menu
14795 var menu = new Roo.menu.Menu();
14796
14797 // Create a menu item to add by reference
14798 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14799
14800 // Add a bunch of items at once using different methods.
14801 // Only the last item added will be returned.
14802 var item = menu.add(
14803     menuItem,                // add existing item by ref
14804     'Dynamic Item',          // new TextItem
14805     '-',                     // new separator
14806     { text: 'Config Item' }  // new item by config
14807 );
14808 </code></pre>
14809      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14810      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14811      */
14812     add : function(){
14813         var a = arguments, l = a.length, item;
14814         for(var i = 0; i < l; i++){
14815             var el = a[i];
14816             if ((typeof(el) == "object") && el.xtype && el.xns) {
14817                 el = Roo.factory(el, Roo.menu);
14818             }
14819             
14820             if(el.render){ // some kind of Item
14821                 item = this.addItem(el);
14822             }else if(typeof el == "string"){ // string
14823                 if(el == "separator" || el == "-"){
14824                     item = this.addSeparator();
14825                 }else{
14826                     item = this.addText(el);
14827                 }
14828             }else if(el.tagName || el.el){ // element
14829                 item = this.addElement(el);
14830             }else if(typeof el == "object"){ // must be menu item config?
14831                 item = this.addMenuItem(el);
14832             }
14833         }
14834         return item;
14835     },
14836
14837     /**
14838      * Returns this menu's underlying {@link Roo.Element} object
14839      * @return {Roo.Element} The element
14840      */
14841     getEl : function(){
14842         if(!this.el){
14843             this.render();
14844         }
14845         return this.el;
14846     },
14847
14848     /**
14849      * Adds a separator bar to the menu
14850      * @return {Roo.menu.Item} The menu item that was added
14851      */
14852     addSeparator : function(){
14853         return this.addItem(new Roo.menu.Separator());
14854     },
14855
14856     /**
14857      * Adds an {@link Roo.Element} object to the menu
14858      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14859      * @return {Roo.menu.Item} The menu item that was added
14860      */
14861     addElement : function(el){
14862         return this.addItem(new Roo.menu.BaseItem(el));
14863     },
14864
14865     /**
14866      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14867      * @param {Roo.menu.Item} item The menu item to add
14868      * @return {Roo.menu.Item} The menu item that was added
14869      */
14870     addItem : function(item){
14871         this.items.add(item);
14872         if(this.ul){
14873             var li = document.createElement("li");
14874             li.className = "x-menu-list-item";
14875             this.ul.dom.appendChild(li);
14876             item.render(li, this);
14877             this.delayAutoWidth();
14878         }
14879         return item;
14880     },
14881
14882     /**
14883      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14884      * @param {Object} config A MenuItem config object
14885      * @return {Roo.menu.Item} The menu item that was added
14886      */
14887     addMenuItem : function(config){
14888         if(!(config instanceof Roo.menu.Item)){
14889             if(typeof config.checked == "boolean"){ // must be check menu item config?
14890                 config = new Roo.menu.CheckItem(config);
14891             }else{
14892                 config = new Roo.menu.Item(config);
14893             }
14894         }
14895         return this.addItem(config);
14896     },
14897
14898     /**
14899      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14900      * @param {String} text The text to display in the menu item
14901      * @return {Roo.menu.Item} The menu item that was added
14902      */
14903     addText : function(text){
14904         return this.addItem(new Roo.menu.TextItem({ text : text }));
14905     },
14906
14907     /**
14908      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14909      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14910      * @param {Roo.menu.Item} item The menu item to add
14911      * @return {Roo.menu.Item} The menu item that was added
14912      */
14913     insert : function(index, item){
14914         this.items.insert(index, item);
14915         if(this.ul){
14916             var li = document.createElement("li");
14917             li.className = "x-menu-list-item";
14918             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14919             item.render(li, this);
14920             this.delayAutoWidth();
14921         }
14922         return item;
14923     },
14924
14925     /**
14926      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14927      * @param {Roo.menu.Item} item The menu item to remove
14928      */
14929     remove : function(item){
14930         this.items.removeKey(item.id);
14931         item.destroy();
14932     },
14933
14934     /**
14935      * Removes and destroys all items in the menu
14936      */
14937     removeAll : function(){
14938         var f;
14939         while(f = this.items.first()){
14940             this.remove(f);
14941         }
14942     }
14943 });
14944
14945 // MenuNav is a private utility class used internally by the Menu
14946 Roo.menu.MenuNav = function(menu){
14947     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14948     this.scope = this.menu = menu;
14949 };
14950
14951 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14952     doRelay : function(e, h){
14953         var k = e.getKey();
14954         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14955             this.menu.tryActivate(0, 1);
14956             return false;
14957         }
14958         return h.call(this.scope || this, e, this.menu);
14959     },
14960
14961     up : function(e, m){
14962         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14963             m.tryActivate(m.items.length-1, -1);
14964         }
14965     },
14966
14967     down : function(e, m){
14968         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14969             m.tryActivate(0, 1);
14970         }
14971     },
14972
14973     right : function(e, m){
14974         if(m.activeItem){
14975             m.activeItem.expandMenu(true);
14976         }
14977     },
14978
14979     left : function(e, m){
14980         m.hide();
14981         if(m.parentMenu && m.parentMenu.activeItem){
14982             m.parentMenu.activeItem.activate();
14983         }
14984     },
14985
14986     enter : function(e, m){
14987         if(m.activeItem){
14988             e.stopPropagation();
14989             m.activeItem.onClick(e);
14990             m.fireEvent("click", this, m.activeItem);
14991             return true;
14992         }
14993     }
14994 });/*
14995  * Based on:
14996  * Ext JS Library 1.1.1
14997  * Copyright(c) 2006-2007, Ext JS, LLC.
14998  *
14999  * Originally Released Under LGPL - original licence link has changed is not relivant.
15000  *
15001  * Fork - LGPL
15002  * <script type="text/javascript">
15003  */
15004  
15005 /**
15006  * @class Roo.menu.MenuMgr
15007  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15008  * @static
15009  */
15010 Roo.menu.MenuMgr = function(){
15011    var menus, active, groups = {}, attached = false, lastShow = new Date();
15012
15013    // private - called when first menu is created
15014    function init(){
15015        menus = {};
15016        active = new Roo.util.MixedCollection();
15017        Roo.get(document).addKeyListener(27, function(){
15018            if(active.length > 0){
15019                hideAll();
15020            }
15021        });
15022    }
15023
15024    // private
15025    function hideAll(){
15026        if(active && active.length > 0){
15027            var c = active.clone();
15028            c.each(function(m){
15029                m.hide();
15030            });
15031        }
15032    }
15033
15034    // private
15035    function onHide(m){
15036        active.remove(m);
15037        if(active.length < 1){
15038            Roo.get(document).un("mousedown", onMouseDown);
15039            attached = false;
15040        }
15041    }
15042
15043    // private
15044    function onShow(m){
15045        var last = active.last();
15046        lastShow = new Date();
15047        active.add(m);
15048        if(!attached){
15049            Roo.get(document).on("mousedown", onMouseDown);
15050            attached = true;
15051        }
15052        if(m.parentMenu){
15053           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15054           m.parentMenu.activeChild = m;
15055        }else if(last && last.isVisible()){
15056           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15057        }
15058    }
15059
15060    // private
15061    function onBeforeHide(m){
15062        if(m.activeChild){
15063            m.activeChild.hide();
15064        }
15065        if(m.autoHideTimer){
15066            clearTimeout(m.autoHideTimer);
15067            delete m.autoHideTimer;
15068        }
15069    }
15070
15071    // private
15072    function onBeforeShow(m){
15073        var pm = m.parentMenu;
15074        if(!pm && !m.allowOtherMenus){
15075            hideAll();
15076        }else if(pm && pm.activeChild && active != m){
15077            pm.activeChild.hide();
15078        }
15079    }
15080
15081    // private
15082    function onMouseDown(e){
15083        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15084            hideAll();
15085        }
15086    }
15087
15088    // private
15089    function onBeforeCheck(mi, state){
15090        if(state){
15091            var g = groups[mi.group];
15092            for(var i = 0, l = g.length; i < l; i++){
15093                if(g[i] != mi){
15094                    g[i].setChecked(false);
15095                }
15096            }
15097        }
15098    }
15099
15100    return {
15101
15102        /**
15103         * Hides all menus that are currently visible
15104         */
15105        hideAll : function(){
15106             hideAll();  
15107        },
15108
15109        // private
15110        register : function(menu){
15111            if(!menus){
15112                init();
15113            }
15114            menus[menu.id] = menu;
15115            menu.on("beforehide", onBeforeHide);
15116            menu.on("hide", onHide);
15117            menu.on("beforeshow", onBeforeShow);
15118            menu.on("show", onShow);
15119            var g = menu.group;
15120            if(g && menu.events["checkchange"]){
15121                if(!groups[g]){
15122                    groups[g] = [];
15123                }
15124                groups[g].push(menu);
15125                menu.on("checkchange", onCheck);
15126            }
15127        },
15128
15129         /**
15130          * Returns a {@link Roo.menu.Menu} object
15131          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15132          * be used to generate and return a new Menu instance.
15133          */
15134        get : function(menu){
15135            if(typeof menu == "string"){ // menu id
15136                return menus[menu];
15137            }else if(menu.events){  // menu instance
15138                return menu;
15139            }else if(typeof menu.length == 'number'){ // array of menu items?
15140                return new Roo.menu.Menu({items:menu});
15141            }else{ // otherwise, must be a config
15142                return new Roo.menu.Menu(menu);
15143            }
15144        },
15145
15146        // private
15147        unregister : function(menu){
15148            delete menus[menu.id];
15149            menu.un("beforehide", onBeforeHide);
15150            menu.un("hide", onHide);
15151            menu.un("beforeshow", onBeforeShow);
15152            menu.un("show", onShow);
15153            var g = menu.group;
15154            if(g && menu.events["checkchange"]){
15155                groups[g].remove(menu);
15156                menu.un("checkchange", onCheck);
15157            }
15158        },
15159
15160        // private
15161        registerCheckable : function(menuItem){
15162            var g = menuItem.group;
15163            if(g){
15164                if(!groups[g]){
15165                    groups[g] = [];
15166                }
15167                groups[g].push(menuItem);
15168                menuItem.on("beforecheckchange", onBeforeCheck);
15169            }
15170        },
15171
15172        // private
15173        unregisterCheckable : function(menuItem){
15174            var g = menuItem.group;
15175            if(g){
15176                groups[g].remove(menuItem);
15177                menuItem.un("beforecheckchange", onBeforeCheck);
15178            }
15179        }
15180    };
15181 }();/*
15182  * Based on:
15183  * Ext JS Library 1.1.1
15184  * Copyright(c) 2006-2007, Ext JS, LLC.
15185  *
15186  * Originally Released Under LGPL - original licence link has changed is not relivant.
15187  *
15188  * Fork - LGPL
15189  * <script type="text/javascript">
15190  */
15191  
15192
15193 /**
15194  * @class Roo.menu.BaseItem
15195  * @extends Roo.Component
15196  * @abstract
15197  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15198  * management and base configuration options shared by all menu components.
15199  * @constructor
15200  * Creates a new BaseItem
15201  * @param {Object} config Configuration options
15202  */
15203 Roo.menu.BaseItem = function(config){
15204     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15205
15206     this.addEvents({
15207         /**
15208          * @event click
15209          * Fires when this item is clicked
15210          * @param {Roo.menu.BaseItem} this
15211          * @param {Roo.EventObject} e
15212          */
15213         click: true,
15214         /**
15215          * @event activate
15216          * Fires when this item is activated
15217          * @param {Roo.menu.BaseItem} this
15218          */
15219         activate : true,
15220         /**
15221          * @event deactivate
15222          * Fires when this item is deactivated
15223          * @param {Roo.menu.BaseItem} this
15224          */
15225         deactivate : true
15226     });
15227
15228     if(this.handler){
15229         this.on("click", this.handler, this.scope, true);
15230     }
15231 };
15232
15233 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15234     /**
15235      * @cfg {Function} handler
15236      * A function that will handle the click event of this menu item (defaults to undefined)
15237      */
15238     /**
15239      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15240      */
15241     canActivate : false,
15242     
15243      /**
15244      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15245      */
15246     hidden: false,
15247     
15248     /**
15249      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15250      */
15251     activeClass : "x-menu-item-active",
15252     /**
15253      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15254      */
15255     hideOnClick : true,
15256     /**
15257      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15258      */
15259     hideDelay : 100,
15260
15261     // private
15262     ctype: "Roo.menu.BaseItem",
15263
15264     // private
15265     actionMode : "container",
15266
15267     // private
15268     render : function(container, parentMenu){
15269         this.parentMenu = parentMenu;
15270         Roo.menu.BaseItem.superclass.render.call(this, container);
15271         this.container.menuItemId = this.id;
15272     },
15273
15274     // private
15275     onRender : function(container, position){
15276         this.el = Roo.get(this.el);
15277         container.dom.appendChild(this.el.dom);
15278     },
15279
15280     // private
15281     onClick : function(e){
15282         if(!this.disabled && this.fireEvent("click", this, e) !== false
15283                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15284             this.handleClick(e);
15285         }else{
15286             e.stopEvent();
15287         }
15288     },
15289
15290     // private
15291     activate : function(){
15292         if(this.disabled){
15293             return false;
15294         }
15295         var li = this.container;
15296         li.addClass(this.activeClass);
15297         this.region = li.getRegion().adjust(2, 2, -2, -2);
15298         this.fireEvent("activate", this);
15299         return true;
15300     },
15301
15302     // private
15303     deactivate : function(){
15304         this.container.removeClass(this.activeClass);
15305         this.fireEvent("deactivate", this);
15306     },
15307
15308     // private
15309     shouldDeactivate : function(e){
15310         return !this.region || !this.region.contains(e.getPoint());
15311     },
15312
15313     // private
15314     handleClick : function(e){
15315         if(this.hideOnClick){
15316             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15317         }
15318     },
15319
15320     // private
15321     expandMenu : function(autoActivate){
15322         // do nothing
15323     },
15324
15325     // private
15326     hideMenu : function(){
15327         // do nothing
15328     }
15329 });/*
15330  * Based on:
15331  * Ext JS Library 1.1.1
15332  * Copyright(c) 2006-2007, Ext JS, LLC.
15333  *
15334  * Originally Released Under LGPL - original licence link has changed is not relivant.
15335  *
15336  * Fork - LGPL
15337  * <script type="text/javascript">
15338  */
15339  
15340 /**
15341  * @class Roo.menu.Adapter
15342  * @extends Roo.menu.BaseItem
15343  * @abstract
15344  * 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.
15345  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15346  * @constructor
15347  * Creates a new Adapter
15348  * @param {Object} config Configuration options
15349  */
15350 Roo.menu.Adapter = function(component, config){
15351     Roo.menu.Adapter.superclass.constructor.call(this, config);
15352     this.component = component;
15353 };
15354 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15355     // private
15356     canActivate : true,
15357
15358     // private
15359     onRender : function(container, position){
15360         this.component.render(container);
15361         this.el = this.component.getEl();
15362     },
15363
15364     // private
15365     activate : function(){
15366         if(this.disabled){
15367             return false;
15368         }
15369         this.component.focus();
15370         this.fireEvent("activate", this);
15371         return true;
15372     },
15373
15374     // private
15375     deactivate : function(){
15376         this.fireEvent("deactivate", this);
15377     },
15378
15379     // private
15380     disable : function(){
15381         this.component.disable();
15382         Roo.menu.Adapter.superclass.disable.call(this);
15383     },
15384
15385     // private
15386     enable : function(){
15387         this.component.enable();
15388         Roo.menu.Adapter.superclass.enable.call(this);
15389     }
15390 });/*
15391  * Based on:
15392  * Ext JS Library 1.1.1
15393  * Copyright(c) 2006-2007, Ext JS, LLC.
15394  *
15395  * Originally Released Under LGPL - original licence link has changed is not relivant.
15396  *
15397  * Fork - LGPL
15398  * <script type="text/javascript">
15399  */
15400
15401 /**
15402  * @class Roo.menu.TextItem
15403  * @extends Roo.menu.BaseItem
15404  * Adds a static text string to a menu, usually used as either a heading or group separator.
15405  * Note: old style constructor with text is still supported.
15406  * 
15407  * @constructor
15408  * Creates a new TextItem
15409  * @param {Object} cfg Configuration
15410  */
15411 Roo.menu.TextItem = function(cfg){
15412     if (typeof(cfg) == 'string') {
15413         this.text = cfg;
15414     } else {
15415         Roo.apply(this,cfg);
15416     }
15417     
15418     Roo.menu.TextItem.superclass.constructor.call(this);
15419 };
15420
15421 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15422     /**
15423      * @cfg {String} text Text to show on item.
15424      */
15425     text : '',
15426     
15427     /**
15428      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15429      */
15430     hideOnClick : false,
15431     /**
15432      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15433      */
15434     itemCls : "x-menu-text",
15435
15436     // private
15437     onRender : function(){
15438         var s = document.createElement("span");
15439         s.className = this.itemCls;
15440         s.innerHTML = this.text;
15441         this.el = s;
15442         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15443     }
15444 });/*
15445  * Based on:
15446  * Ext JS Library 1.1.1
15447  * Copyright(c) 2006-2007, Ext JS, LLC.
15448  *
15449  * Originally Released Under LGPL - original licence link has changed is not relivant.
15450  *
15451  * Fork - LGPL
15452  * <script type="text/javascript">
15453  */
15454
15455 /**
15456  * @class Roo.menu.Separator
15457  * @extends Roo.menu.BaseItem
15458  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15459  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15460  * @constructor
15461  * @param {Object} config Configuration options
15462  */
15463 Roo.menu.Separator = function(config){
15464     Roo.menu.Separator.superclass.constructor.call(this, config);
15465 };
15466
15467 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15468     /**
15469      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15470      */
15471     itemCls : "x-menu-sep",
15472     /**
15473      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15474      */
15475     hideOnClick : false,
15476
15477     // private
15478     onRender : function(li){
15479         var s = document.createElement("span");
15480         s.className = this.itemCls;
15481         s.innerHTML = "&#160;";
15482         this.el = s;
15483         li.addClass("x-menu-sep-li");
15484         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15485     }
15486 });/*
15487  * Based on:
15488  * Ext JS Library 1.1.1
15489  * Copyright(c) 2006-2007, Ext JS, LLC.
15490  *
15491  * Originally Released Under LGPL - original licence link has changed is not relivant.
15492  *
15493  * Fork - LGPL
15494  * <script type="text/javascript">
15495  */
15496 /**
15497  * @class Roo.menu.Item
15498  * @extends Roo.menu.BaseItem
15499  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15500  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15501  * activation and click handling.
15502  * @constructor
15503  * Creates a new Item
15504  * @param {Object} config Configuration options
15505  */
15506 Roo.menu.Item = function(config){
15507     Roo.menu.Item.superclass.constructor.call(this, config);
15508     if(this.menu){
15509         this.menu = Roo.menu.MenuMgr.get(this.menu);
15510     }
15511 };
15512 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15513     /**
15514      * @cfg {Roo.menu.Menu} menu
15515      * A Sub menu
15516      */
15517     /**
15518      * @cfg {String} text
15519      * The text to show on the menu item.
15520      */
15521     text: '',
15522      /**
15523      * @cfg {String} html to render in menu
15524      * The text to show on the menu item (HTML version).
15525      */
15526     html: '',
15527     /**
15528      * @cfg {String} icon
15529      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15530      */
15531     icon: undefined,
15532     /**
15533      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15534      */
15535     itemCls : "x-menu-item",
15536     /**
15537      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15538      */
15539     canActivate : true,
15540     /**
15541      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15542      */
15543     showDelay: 200,
15544     // doc'd in BaseItem
15545     hideDelay: 200,
15546
15547     // private
15548     ctype: "Roo.menu.Item",
15549     
15550     // private
15551     onRender : function(container, position){
15552         var el = document.createElement("a");
15553         el.hideFocus = true;
15554         el.unselectable = "on";
15555         el.href = this.href || "#";
15556         if(this.hrefTarget){
15557             el.target = this.hrefTarget;
15558         }
15559         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15560         
15561         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15562         
15563         el.innerHTML = String.format(
15564                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15565                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15566         this.el = el;
15567         Roo.menu.Item.superclass.onRender.call(this, container, position);
15568     },
15569
15570     /**
15571      * Sets the text to display in this menu item
15572      * @param {String} text The text to display
15573      * @param {Boolean} isHTML true to indicate text is pure html.
15574      */
15575     setText : function(text, isHTML){
15576         if (isHTML) {
15577             this.html = text;
15578         } else {
15579             this.text = text;
15580             this.html = '';
15581         }
15582         if(this.rendered){
15583             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15584      
15585             this.el.update(String.format(
15586                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15587                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15588             this.parentMenu.autoWidth();
15589         }
15590     },
15591
15592     // private
15593     handleClick : function(e){
15594         if(!this.href){ // if no link defined, stop the event automatically
15595             e.stopEvent();
15596         }
15597         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15598     },
15599
15600     // private
15601     activate : function(autoExpand){
15602         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15603             this.focus();
15604             if(autoExpand){
15605                 this.expandMenu();
15606             }
15607         }
15608         return true;
15609     },
15610
15611     // private
15612     shouldDeactivate : function(e){
15613         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15614             if(this.menu && this.menu.isVisible()){
15615                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15616             }
15617             return true;
15618         }
15619         return false;
15620     },
15621
15622     // private
15623     deactivate : function(){
15624         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15625         this.hideMenu();
15626     },
15627
15628     // private
15629     expandMenu : function(autoActivate){
15630         if(!this.disabled && this.menu){
15631             clearTimeout(this.hideTimer);
15632             delete this.hideTimer;
15633             if(!this.menu.isVisible() && !this.showTimer){
15634                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15635             }else if (this.menu.isVisible() && autoActivate){
15636                 this.menu.tryActivate(0, 1);
15637             }
15638         }
15639     },
15640
15641     // private
15642     deferExpand : function(autoActivate){
15643         delete this.showTimer;
15644         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15645         if(autoActivate){
15646             this.menu.tryActivate(0, 1);
15647         }
15648     },
15649
15650     // private
15651     hideMenu : function(){
15652         clearTimeout(this.showTimer);
15653         delete this.showTimer;
15654         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15655             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15656         }
15657     },
15658
15659     // private
15660     deferHide : function(){
15661         delete this.hideTimer;
15662         this.menu.hide();
15663     }
15664 });/*
15665  * Based on:
15666  * Ext JS Library 1.1.1
15667  * Copyright(c) 2006-2007, Ext JS, LLC.
15668  *
15669  * Originally Released Under LGPL - original licence link has changed is not relivant.
15670  *
15671  * Fork - LGPL
15672  * <script type="text/javascript">
15673  */
15674  
15675 /**
15676  * @class Roo.menu.CheckItem
15677  * @extends Roo.menu.Item
15678  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15679  * @constructor
15680  * Creates a new CheckItem
15681  * @param {Object} config Configuration options
15682  */
15683 Roo.menu.CheckItem = function(config){
15684     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15685     this.addEvents({
15686         /**
15687          * @event beforecheckchange
15688          * Fires before the checked value is set, providing an opportunity to cancel if needed
15689          * @param {Roo.menu.CheckItem} this
15690          * @param {Boolean} checked The new checked value that will be set
15691          */
15692         "beforecheckchange" : true,
15693         /**
15694          * @event checkchange
15695          * Fires after the checked value has been set
15696          * @param {Roo.menu.CheckItem} this
15697          * @param {Boolean} checked The checked value that was set
15698          */
15699         "checkchange" : true
15700     });
15701     if(this.checkHandler){
15702         this.on('checkchange', this.checkHandler, this.scope);
15703     }
15704 };
15705 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15706     /**
15707      * @cfg {String} group
15708      * All check items with the same group name will automatically be grouped into a single-select
15709      * radio button group (defaults to '')
15710      */
15711     /**
15712      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15713      */
15714     itemCls : "x-menu-item x-menu-check-item",
15715     /**
15716      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15717      */
15718     groupClass : "x-menu-group-item",
15719
15720     /**
15721      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15722      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15723      * initialized with checked = true will be rendered as checked.
15724      */
15725     checked: false,
15726
15727     // private
15728     ctype: "Roo.menu.CheckItem",
15729
15730     // private
15731     onRender : function(c){
15732         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15733         if(this.group){
15734             this.el.addClass(this.groupClass);
15735         }
15736         Roo.menu.MenuMgr.registerCheckable(this);
15737         if(this.checked){
15738             this.checked = false;
15739             this.setChecked(true, true);
15740         }
15741     },
15742
15743     // private
15744     destroy : function(){
15745         if(this.rendered){
15746             Roo.menu.MenuMgr.unregisterCheckable(this);
15747         }
15748         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15749     },
15750
15751     /**
15752      * Set the checked state of this item
15753      * @param {Boolean} checked The new checked value
15754      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15755      */
15756     setChecked : function(state, suppressEvent){
15757         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15758             if(this.container){
15759                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15760             }
15761             this.checked = state;
15762             if(suppressEvent !== true){
15763                 this.fireEvent("checkchange", this, state);
15764             }
15765         }
15766     },
15767
15768     // private
15769     handleClick : function(e){
15770        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15771            this.setChecked(!this.checked);
15772        }
15773        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15774     }
15775 });/*
15776  * Based on:
15777  * Ext JS Library 1.1.1
15778  * Copyright(c) 2006-2007, Ext JS, LLC.
15779  *
15780  * Originally Released Under LGPL - original licence link has changed is not relivant.
15781  *
15782  * Fork - LGPL
15783  * <script type="text/javascript">
15784  */
15785  
15786 /**
15787  * @class Roo.menu.DateItem
15788  * @extends Roo.menu.Adapter
15789  * A menu item that wraps the {@link Roo.DatPicker} component.
15790  * @constructor
15791  * Creates a new DateItem
15792  * @param {Object} config Configuration options
15793  */
15794 Roo.menu.DateItem = function(config){
15795     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15796     /** The Roo.DatePicker object @type Roo.DatePicker */
15797     this.picker = this.component;
15798     this.addEvents({select: true});
15799     
15800     this.picker.on("render", function(picker){
15801         picker.getEl().swallowEvent("click");
15802         picker.container.addClass("x-menu-date-item");
15803     });
15804
15805     this.picker.on("select", this.onSelect, this);
15806 };
15807
15808 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15809     // private
15810     onSelect : function(picker, date){
15811         this.fireEvent("select", this, date, picker);
15812         Roo.menu.DateItem.superclass.handleClick.call(this);
15813     }
15814 });/*
15815  * Based on:
15816  * Ext JS Library 1.1.1
15817  * Copyright(c) 2006-2007, Ext JS, LLC.
15818  *
15819  * Originally Released Under LGPL - original licence link has changed is not relivant.
15820  *
15821  * Fork - LGPL
15822  * <script type="text/javascript">
15823  */
15824  
15825 /**
15826  * @class Roo.menu.ColorItem
15827  * @extends Roo.menu.Adapter
15828  * A menu item that wraps the {@link Roo.ColorPalette} component.
15829  * @constructor
15830  * Creates a new ColorItem
15831  * @param {Object} config Configuration options
15832  */
15833 Roo.menu.ColorItem = function(config){
15834     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15835     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15836     this.palette = this.component;
15837     this.relayEvents(this.palette, ["select"]);
15838     if(this.selectHandler){
15839         this.on('select', this.selectHandler, this.scope);
15840     }
15841 };
15842 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15843  * Based on:
15844  * Ext JS Library 1.1.1
15845  * Copyright(c) 2006-2007, Ext JS, LLC.
15846  *
15847  * Originally Released Under LGPL - original licence link has changed is not relivant.
15848  *
15849  * Fork - LGPL
15850  * <script type="text/javascript">
15851  */
15852  
15853
15854 /**
15855  * @class Roo.menu.DateMenu
15856  * @extends Roo.menu.Menu
15857  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15858  * @constructor
15859  * Creates a new DateMenu
15860  * @param {Object} config Configuration options
15861  */
15862 Roo.menu.DateMenu = function(config){
15863     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15864     this.plain = true;
15865     var di = new Roo.menu.DateItem(config);
15866     this.add(di);
15867     /**
15868      * The {@link Roo.DatePicker} instance for this DateMenu
15869      * @type DatePicker
15870      */
15871     this.picker = di.picker;
15872     /**
15873      * @event select
15874      * @param {DatePicker} picker
15875      * @param {Date} date
15876      */
15877     this.relayEvents(di, ["select"]);
15878     this.on('beforeshow', function(){
15879         if(this.picker){
15880             this.picker.hideMonthPicker(false);
15881         }
15882     }, this);
15883 };
15884 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15885     cls:'x-date-menu'
15886 });/*
15887  * Based on:
15888  * Ext JS Library 1.1.1
15889  * Copyright(c) 2006-2007, Ext JS, LLC.
15890  *
15891  * Originally Released Under LGPL - original licence link has changed is not relivant.
15892  *
15893  * Fork - LGPL
15894  * <script type="text/javascript">
15895  */
15896  
15897
15898 /**
15899  * @class Roo.menu.ColorMenu
15900  * @extends Roo.menu.Menu
15901  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15902  * @constructor
15903  * Creates a new ColorMenu
15904  * @param {Object} config Configuration options
15905  */
15906 Roo.menu.ColorMenu = function(config){
15907     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15908     this.plain = true;
15909     var ci = new Roo.menu.ColorItem(config);
15910     this.add(ci);
15911     /**
15912      * The {@link Roo.ColorPalette} instance for this ColorMenu
15913      * @type ColorPalette
15914      */
15915     this.palette = ci.palette;
15916     /**
15917      * @event select
15918      * @param {ColorPalette} palette
15919      * @param {String} color
15920      */
15921     this.relayEvents(ci, ["select"]);
15922 };
15923 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15924  * Based on:
15925  * Ext JS Library 1.1.1
15926  * Copyright(c) 2006-2007, Ext JS, LLC.
15927  *
15928  * Originally Released Under LGPL - original licence link has changed is not relivant.
15929  *
15930  * Fork - LGPL
15931  * <script type="text/javascript">
15932  */
15933  
15934 /**
15935  * @class Roo.form.TextItem
15936  * @extends Roo.BoxComponent
15937  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15938  * @constructor
15939  * Creates a new TextItem
15940  * @param {Object} config Configuration options
15941  */
15942 Roo.form.TextItem = function(config){
15943     Roo.form.TextItem.superclass.constructor.call(this, config);
15944 };
15945
15946 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15947     
15948     /**
15949      * @cfg {String} tag the tag for this item (default div)
15950      */
15951     tag : 'div',
15952     /**
15953      * @cfg {String} html the content for this item
15954      */
15955     html : '',
15956     
15957     getAutoCreate : function()
15958     {
15959         var cfg = {
15960             id: this.id,
15961             tag: this.tag,
15962             html: this.html,
15963             cls: 'x-form-item'
15964         };
15965         
15966         return cfg;
15967         
15968     },
15969     
15970     onRender : function(ct, position)
15971     {
15972         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15973         
15974         if(!this.el){
15975             var cfg = this.getAutoCreate();
15976             if(!cfg.name){
15977                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15978             }
15979             if (!cfg.name.length) {
15980                 delete cfg.name;
15981             }
15982             this.el = ct.createChild(cfg, position);
15983         }
15984     },
15985     /*
15986      * setHTML
15987      * @param {String} html update the Contents of the element.
15988      */
15989     setHTML : function(html)
15990     {
15991         this.fieldEl.dom.innerHTML = html;
15992     }
15993     
15994 });/*
15995  * Based on:
15996  * Ext JS Library 1.1.1
15997  * Copyright(c) 2006-2007, Ext JS, LLC.
15998  *
15999  * Originally Released Under LGPL - original licence link has changed is not relivant.
16000  *
16001  * Fork - LGPL
16002  * <script type="text/javascript">
16003  */
16004  
16005 /**
16006  * @class Roo.form.Field
16007  * @extends Roo.BoxComponent
16008  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16009  * @constructor
16010  * Creates a new Field
16011  * @param {Object} config Configuration options
16012  */
16013 Roo.form.Field = function(config){
16014     Roo.form.Field.superclass.constructor.call(this, config);
16015 };
16016
16017 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16018     /**
16019      * @cfg {String} fieldLabel Label to use when rendering a form.
16020      */
16021        /**
16022      * @cfg {String} qtip Mouse over tip
16023      */
16024      
16025     /**
16026      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16027      */
16028     invalidClass : "x-form-invalid",
16029     /**
16030      * @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")
16031      */
16032     invalidText : "The value in this field is invalid",
16033     /**
16034      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16035      */
16036     focusClass : "x-form-focus",
16037     /**
16038      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16039       automatic validation (defaults to "keyup").
16040      */
16041     validationEvent : "keyup",
16042     /**
16043      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16044      */
16045     validateOnBlur : true,
16046     /**
16047      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16048      */
16049     validationDelay : 250,
16050     /**
16051      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16052      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16053      */
16054     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16055     /**
16056      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16057      */
16058     fieldClass : "x-form-field",
16059     /**
16060      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16061      *<pre>
16062 Value         Description
16063 -----------   ----------------------------------------------------------------------
16064 qtip          Display a quick tip when the user hovers over the field
16065 title         Display a default browser title attribute popup
16066 under         Add a block div beneath the field containing the error text
16067 side          Add an error icon to the right of the field with a popup on hover
16068 [element id]  Add the error text directly to the innerHTML of the specified element
16069 </pre>
16070      */
16071     msgTarget : 'qtip',
16072     /**
16073      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16074      */
16075     msgFx : 'normal',
16076
16077     /**
16078      * @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.
16079      */
16080     readOnly : false,
16081
16082     /**
16083      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16084      */
16085     disabled : false,
16086
16087     /**
16088      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16089      */
16090     inputType : undefined,
16091     
16092     /**
16093      * @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).
16094          */
16095         tabIndex : undefined,
16096         
16097     // private
16098     isFormField : true,
16099
16100     // private
16101     hasFocus : false,
16102     /**
16103      * @property {Roo.Element} fieldEl
16104      * Element Containing the rendered Field (with label etc.)
16105      */
16106     /**
16107      * @cfg {Mixed} value A value to initialize this field with.
16108      */
16109     value : undefined,
16110
16111     /**
16112      * @cfg {String} name The field's HTML name attribute.
16113      */
16114     /**
16115      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16116      */
16117     // private
16118     loadedValue : false,
16119      
16120      
16121         // private ??
16122         initComponent : function(){
16123         Roo.form.Field.superclass.initComponent.call(this);
16124         this.addEvents({
16125             /**
16126              * @event focus
16127              * Fires when this field receives input focus.
16128              * @param {Roo.form.Field} this
16129              */
16130             focus : true,
16131             /**
16132              * @event blur
16133              * Fires when this field loses input focus.
16134              * @param {Roo.form.Field} this
16135              */
16136             blur : true,
16137             /**
16138              * @event specialkey
16139              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16140              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16141              * @param {Roo.form.Field} this
16142              * @param {Roo.EventObject} e The event object
16143              */
16144             specialkey : true,
16145             /**
16146              * @event change
16147              * Fires just before the field blurs if the field value has changed.
16148              * @param {Roo.form.Field} this
16149              * @param {Mixed} newValue The new value
16150              * @param {Mixed} oldValue The original value
16151              */
16152             change : true,
16153             /**
16154              * @event invalid
16155              * Fires after the field has been marked as invalid.
16156              * @param {Roo.form.Field} this
16157              * @param {String} msg The validation message
16158              */
16159             invalid : true,
16160             /**
16161              * @event valid
16162              * Fires after the field has been validated with no errors.
16163              * @param {Roo.form.Field} this
16164              */
16165             valid : true,
16166              /**
16167              * @event keyup
16168              * Fires after the key up
16169              * @param {Roo.form.Field} this
16170              * @param {Roo.EventObject}  e The event Object
16171              */
16172             keyup : true
16173         });
16174     },
16175
16176     /**
16177      * Returns the name attribute of the field if available
16178      * @return {String} name The field name
16179      */
16180     getName: function(){
16181          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16182     },
16183
16184     // private
16185     onRender : function(ct, position){
16186         Roo.form.Field.superclass.onRender.call(this, ct, position);
16187         if(!this.el){
16188             var cfg = this.getAutoCreate();
16189             if(!cfg.name){
16190                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16191             }
16192             if (!cfg.name.length) {
16193                 delete cfg.name;
16194             }
16195             if(this.inputType){
16196                 cfg.type = this.inputType;
16197             }
16198             this.el = ct.createChild(cfg, position);
16199         }
16200         var type = this.el.dom.type;
16201         if(type){
16202             if(type == 'password'){
16203                 type = 'text';
16204             }
16205             this.el.addClass('x-form-'+type);
16206         }
16207         if(this.readOnly){
16208             this.el.dom.readOnly = true;
16209         }
16210         if(this.tabIndex !== undefined){
16211             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16212         }
16213
16214         this.el.addClass([this.fieldClass, this.cls]);
16215         this.initValue();
16216     },
16217
16218     /**
16219      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16220      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16221      * @return {Roo.form.Field} this
16222      */
16223     applyTo : function(target){
16224         this.allowDomMove = false;
16225         this.el = Roo.get(target);
16226         this.render(this.el.dom.parentNode);
16227         return this;
16228     },
16229
16230     // private
16231     initValue : function(){
16232         if(this.value !== undefined){
16233             this.setValue(this.value);
16234         }else if(this.el.dom.value.length > 0){
16235             this.setValue(this.el.dom.value);
16236         }
16237     },
16238
16239     /**
16240      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16241      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16242      */
16243     isDirty : function() {
16244         if(this.disabled) {
16245             return false;
16246         }
16247         return String(this.getValue()) !== String(this.originalValue);
16248     },
16249
16250     /**
16251      * stores the current value in loadedValue
16252      */
16253     resetHasChanged : function()
16254     {
16255         this.loadedValue = String(this.getValue());
16256     },
16257     /**
16258      * checks the current value against the 'loaded' value.
16259      * Note - will return false if 'resetHasChanged' has not been called first.
16260      */
16261     hasChanged : function()
16262     {
16263         if(this.disabled || this.readOnly) {
16264             return false;
16265         }
16266         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16267     },
16268     
16269     
16270     
16271     // private
16272     afterRender : function(){
16273         Roo.form.Field.superclass.afterRender.call(this);
16274         this.initEvents();
16275     },
16276
16277     // private
16278     fireKey : function(e){
16279         //Roo.log('field ' + e.getKey());
16280         if(e.isNavKeyPress()){
16281             this.fireEvent("specialkey", this, e);
16282         }
16283     },
16284
16285     /**
16286      * Resets the current field value to the originally loaded value and clears any validation messages
16287      */
16288     reset : function(){
16289         this.setValue(this.resetValue);
16290         this.originalValue = this.getValue();
16291         this.clearInvalid();
16292     },
16293
16294     // private
16295     initEvents : function(){
16296         // safari killled keypress - so keydown is now used..
16297         this.el.on("keydown" , this.fireKey,  this);
16298         this.el.on("focus", this.onFocus,  this);
16299         this.el.on("blur", this.onBlur,  this);
16300         this.el.relayEvent('keyup', this);
16301
16302         // reference to original value for reset
16303         this.originalValue = this.getValue();
16304         this.resetValue =  this.getValue();
16305     },
16306
16307     // private
16308     onFocus : function(){
16309         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16310             this.el.addClass(this.focusClass);
16311         }
16312         if(!this.hasFocus){
16313             this.hasFocus = true;
16314             this.startValue = this.getValue();
16315             this.fireEvent("focus", this);
16316         }
16317     },
16318
16319     beforeBlur : Roo.emptyFn,
16320
16321     // private
16322     onBlur : function(){
16323         this.beforeBlur();
16324         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16325             this.el.removeClass(this.focusClass);
16326         }
16327         this.hasFocus = false;
16328         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16329             this.validate();
16330         }
16331         var v = this.getValue();
16332         if(String(v) !== String(this.startValue)){
16333             this.fireEvent('change', this, v, this.startValue);
16334         }
16335         this.fireEvent("blur", this);
16336     },
16337
16338     /**
16339      * Returns whether or not the field value is currently valid
16340      * @param {Boolean} preventMark True to disable marking the field invalid
16341      * @return {Boolean} True if the value is valid, else false
16342      */
16343     isValid : function(preventMark){
16344         if(this.disabled){
16345             return true;
16346         }
16347         var restore = this.preventMark;
16348         this.preventMark = preventMark === true;
16349         var v = this.validateValue(this.processValue(this.getRawValue()));
16350         this.preventMark = restore;
16351         return v;
16352     },
16353
16354     /**
16355      * Validates the field value
16356      * @return {Boolean} True if the value is valid, else false
16357      */
16358     validate : function(){
16359         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16360             this.clearInvalid();
16361             return true;
16362         }
16363         return false;
16364     },
16365
16366     processValue : function(value){
16367         return value;
16368     },
16369
16370     // private
16371     // Subclasses should provide the validation implementation by overriding this
16372     validateValue : function(value){
16373         return true;
16374     },
16375
16376     /**
16377      * Mark this field as invalid
16378      * @param {String} msg The validation message
16379      */
16380     markInvalid : function(msg){
16381         if(!this.rendered || this.preventMark){ // not rendered
16382             return;
16383         }
16384         
16385         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16386         
16387         obj.el.addClass(this.invalidClass);
16388         msg = msg || this.invalidText;
16389         switch(this.msgTarget){
16390             case 'qtip':
16391                 obj.el.dom.qtip = msg;
16392                 obj.el.dom.qclass = 'x-form-invalid-tip';
16393                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16394                     Roo.QuickTips.enable();
16395                 }
16396                 break;
16397             case 'title':
16398                 this.el.dom.title = msg;
16399                 break;
16400             case 'under':
16401                 if(!this.errorEl){
16402                     var elp = this.el.findParent('.x-form-element', 5, true);
16403                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16404                     this.errorEl.setWidth(elp.getWidth(true)-20);
16405                 }
16406                 this.errorEl.update(msg);
16407                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16408                 break;
16409             case 'side':
16410                 if(!this.errorIcon){
16411                     var elp = this.el.findParent('.x-form-element', 5, true);
16412                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16413                 }
16414                 this.alignErrorIcon();
16415                 this.errorIcon.dom.qtip = msg;
16416                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16417                 this.errorIcon.show();
16418                 this.on('resize', this.alignErrorIcon, this);
16419                 break;
16420             default:
16421                 var t = Roo.getDom(this.msgTarget);
16422                 t.innerHTML = msg;
16423                 t.style.display = this.msgDisplay;
16424                 break;
16425         }
16426         this.fireEvent('invalid', this, msg);
16427     },
16428
16429     // private
16430     alignErrorIcon : function(){
16431         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16432     },
16433
16434     /**
16435      * Clear any invalid styles/messages for this field
16436      */
16437     clearInvalid : function(){
16438         if(!this.rendered || this.preventMark){ // not rendered
16439             return;
16440         }
16441         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16442         
16443         obj.el.removeClass(this.invalidClass);
16444         switch(this.msgTarget){
16445             case 'qtip':
16446                 obj.el.dom.qtip = '';
16447                 break;
16448             case 'title':
16449                 this.el.dom.title = '';
16450                 break;
16451             case 'under':
16452                 if(this.errorEl){
16453                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16454                 }
16455                 break;
16456             case 'side':
16457                 if(this.errorIcon){
16458                     this.errorIcon.dom.qtip = '';
16459                     this.errorIcon.hide();
16460                     this.un('resize', this.alignErrorIcon, this);
16461                 }
16462                 break;
16463             default:
16464                 var t = Roo.getDom(this.msgTarget);
16465                 t.innerHTML = '';
16466                 t.style.display = 'none';
16467                 break;
16468         }
16469         this.fireEvent('valid', this);
16470     },
16471
16472     /**
16473      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16474      * @return {Mixed} value The field value
16475      */
16476     getRawValue : function(){
16477         var v = this.el.getValue();
16478         
16479         return v;
16480     },
16481
16482     /**
16483      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16484      * @return {Mixed} value The field value
16485      */
16486     getValue : function(){
16487         var v = this.el.getValue();
16488          
16489         return v;
16490     },
16491
16492     /**
16493      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16494      * @param {Mixed} value The value to set
16495      */
16496     setRawValue : function(v){
16497         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16498     },
16499
16500     /**
16501      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16502      * @param {Mixed} value The value to set
16503      */
16504     setValue : function(v){
16505         this.value = v;
16506         if(this.rendered){
16507             this.el.dom.value = (v === null || v === undefined ? '' : v);
16508              this.validate();
16509         }
16510     },
16511
16512     adjustSize : function(w, h){
16513         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16514         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16515         return s;
16516     },
16517
16518     adjustWidth : function(tag, w){
16519         tag = tag.toLowerCase();
16520         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16521             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16522                 if(tag == 'input'){
16523                     return w + 2;
16524                 }
16525                 if(tag == 'textarea'){
16526                     return w-2;
16527                 }
16528             }else if(Roo.isOpera){
16529                 if(tag == 'input'){
16530                     return w + 2;
16531                 }
16532                 if(tag == 'textarea'){
16533                     return w-2;
16534                 }
16535             }
16536         }
16537         return w;
16538     }
16539 });
16540
16541
16542 // anything other than normal should be considered experimental
16543 Roo.form.Field.msgFx = {
16544     normal : {
16545         show: function(msgEl, f){
16546             msgEl.setDisplayed('block');
16547         },
16548
16549         hide : function(msgEl, f){
16550             msgEl.setDisplayed(false).update('');
16551         }
16552     },
16553
16554     slide : {
16555         show: function(msgEl, f){
16556             msgEl.slideIn('t', {stopFx:true});
16557         },
16558
16559         hide : function(msgEl, f){
16560             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16561         }
16562     },
16563
16564     slideRight : {
16565         show: function(msgEl, f){
16566             msgEl.fixDisplay();
16567             msgEl.alignTo(f.el, 'tl-tr');
16568             msgEl.slideIn('l', {stopFx:true});
16569         },
16570
16571         hide : function(msgEl, f){
16572             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16573         }
16574     }
16575 };/*
16576  * Based on:
16577  * Ext JS Library 1.1.1
16578  * Copyright(c) 2006-2007, Ext JS, LLC.
16579  *
16580  * Originally Released Under LGPL - original licence link has changed is not relivant.
16581  *
16582  * Fork - LGPL
16583  * <script type="text/javascript">
16584  */
16585  
16586
16587 /**
16588  * @class Roo.form.TextField
16589  * @extends Roo.form.Field
16590  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16591  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16592  * @constructor
16593  * Creates a new TextField
16594  * @param {Object} config Configuration options
16595  */
16596 Roo.form.TextField = function(config){
16597     Roo.form.TextField.superclass.constructor.call(this, config);
16598     this.addEvents({
16599         /**
16600          * @event autosize
16601          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16602          * according to the default logic, but this event provides a hook for the developer to apply additional
16603          * logic at runtime to resize the field if needed.
16604              * @param {Roo.form.Field} this This text field
16605              * @param {Number} width The new field width
16606              */
16607         autosize : true
16608     });
16609 };
16610
16611 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16612     /**
16613      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16614      */
16615     grow : false,
16616     /**
16617      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16618      */
16619     growMin : 30,
16620     /**
16621      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16622      */
16623     growMax : 800,
16624     /**
16625      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16626      */
16627     vtype : null,
16628     /**
16629      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16630      */
16631     maskRe : null,
16632     /**
16633      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16634      */
16635     disableKeyFilter : false,
16636     /**
16637      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16638      */
16639     allowBlank : true,
16640     /**
16641      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16642      */
16643     minLength : 0,
16644     /**
16645      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16646      */
16647     maxLength : Number.MAX_VALUE,
16648     /**
16649      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16650      */
16651     minLengthText : "The minimum length for this field is {0}",
16652     /**
16653      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16654      */
16655     maxLengthText : "The maximum length for this field is {0}",
16656     /**
16657      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16658      */
16659     selectOnFocus : false,
16660     /**
16661      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16662      */    
16663     allowLeadingSpace : false,
16664     /**
16665      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16666      */
16667     blankText : "This field is required",
16668     /**
16669      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16670      * If available, this function will be called only after the basic validators all return true, and will be passed the
16671      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16672      */
16673     validator : null,
16674     /**
16675      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16676      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16677      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16678      */
16679     regex : null,
16680     /**
16681      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16682      */
16683     regexText : "",
16684     /**
16685      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16686      */
16687     emptyText : null,
16688    
16689
16690     // private
16691     initEvents : function()
16692     {
16693         if (this.emptyText) {
16694             this.el.attr('placeholder', this.emptyText);
16695         }
16696         
16697         Roo.form.TextField.superclass.initEvents.call(this);
16698         if(this.validationEvent == 'keyup'){
16699             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16700             this.el.on('keyup', this.filterValidation, this);
16701         }
16702         else if(this.validationEvent !== false){
16703             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16704         }
16705         
16706         if(this.selectOnFocus){
16707             this.on("focus", this.preFocus, this);
16708         }
16709         if (!this.allowLeadingSpace) {
16710             this.on('blur', this.cleanLeadingSpace, this);
16711         }
16712         
16713         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16714             this.el.on("keypress", this.filterKeys, this);
16715         }
16716         if(this.grow){
16717             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16718             this.el.on("click", this.autoSize,  this);
16719         }
16720         if(this.el.is('input[type=password]') && Roo.isSafari){
16721             this.el.on('keydown', this.SafariOnKeyDown, this);
16722         }
16723     },
16724
16725     processValue : function(value){
16726         if(this.stripCharsRe){
16727             var newValue = value.replace(this.stripCharsRe, '');
16728             if(newValue !== value){
16729                 this.setRawValue(newValue);
16730                 return newValue;
16731             }
16732         }
16733         return value;
16734     },
16735
16736     filterValidation : function(e){
16737         if(!e.isNavKeyPress()){
16738             this.validationTask.delay(this.validationDelay);
16739         }
16740     },
16741
16742     // private
16743     onKeyUp : function(e){
16744         if(!e.isNavKeyPress()){
16745             this.autoSize();
16746         }
16747     },
16748     // private - clean the leading white space
16749     cleanLeadingSpace : function(e)
16750     {
16751         if ( this.inputType == 'file') {
16752             return;
16753         }
16754         
16755         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16756     },
16757     /**
16758      * Resets the current field value to the originally-loaded value and clears any validation messages.
16759      *  
16760      */
16761     reset : function(){
16762         Roo.form.TextField.superclass.reset.call(this);
16763        
16764     }, 
16765     // private
16766     preFocus : function(){
16767         
16768         if(this.selectOnFocus){
16769             this.el.dom.select();
16770         }
16771     },
16772
16773     
16774     // private
16775     filterKeys : function(e){
16776         var k = e.getKey();
16777         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16778             return;
16779         }
16780         var c = e.getCharCode(), cc = String.fromCharCode(c);
16781         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16782             return;
16783         }
16784         if(!this.maskRe.test(cc)){
16785             e.stopEvent();
16786         }
16787     },
16788
16789     setValue : function(v){
16790         
16791         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16792         
16793         this.autoSize();
16794     },
16795
16796     /**
16797      * Validates a value according to the field's validation rules and marks the field as invalid
16798      * if the validation fails
16799      * @param {Mixed} value The value to validate
16800      * @return {Boolean} True if the value is valid, else false
16801      */
16802     validateValue : function(value){
16803         if(value.length < 1)  { // if it's blank
16804              if(this.allowBlank){
16805                 this.clearInvalid();
16806                 return true;
16807              }else{
16808                 this.markInvalid(this.blankText);
16809                 return false;
16810              }
16811         }
16812         if(value.length < this.minLength){
16813             this.markInvalid(String.format(this.minLengthText, this.minLength));
16814             return false;
16815         }
16816         if(value.length > this.maxLength){
16817             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16818             return false;
16819         }
16820         if(this.vtype){
16821             var vt = Roo.form.VTypes;
16822             if(!vt[this.vtype](value, this)){
16823                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16824                 return false;
16825             }
16826         }
16827         if(typeof this.validator == "function"){
16828             var msg = this.validator(value);
16829             if(msg !== true){
16830                 this.markInvalid(msg);
16831                 return false;
16832             }
16833         }
16834         if(this.regex && !this.regex.test(value)){
16835             this.markInvalid(this.regexText);
16836             return false;
16837         }
16838         return true;
16839     },
16840
16841     /**
16842      * Selects text in this field
16843      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16844      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16845      */
16846     selectText : function(start, end){
16847         var v = this.getRawValue();
16848         if(v.length > 0){
16849             start = start === undefined ? 0 : start;
16850             end = end === undefined ? v.length : end;
16851             var d = this.el.dom;
16852             if(d.setSelectionRange){
16853                 d.setSelectionRange(start, end);
16854             }else if(d.createTextRange){
16855                 var range = d.createTextRange();
16856                 range.moveStart("character", start);
16857                 range.moveEnd("character", v.length-end);
16858                 range.select();
16859             }
16860         }
16861     },
16862
16863     /**
16864      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16865      * This only takes effect if grow = true, and fires the autosize event.
16866      */
16867     autoSize : function(){
16868         if(!this.grow || !this.rendered){
16869             return;
16870         }
16871         if(!this.metrics){
16872             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16873         }
16874         var el = this.el;
16875         var v = el.dom.value;
16876         var d = document.createElement('div');
16877         d.appendChild(document.createTextNode(v));
16878         v = d.innerHTML;
16879         d = null;
16880         v += "&#160;";
16881         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16882         this.el.setWidth(w);
16883         this.fireEvent("autosize", this, w);
16884     },
16885     
16886     // private
16887     SafariOnKeyDown : function(event)
16888     {
16889         // this is a workaround for a password hang bug on chrome/ webkit.
16890         
16891         var isSelectAll = false;
16892         
16893         if(this.el.dom.selectionEnd > 0){
16894             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16895         }
16896         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16897             event.preventDefault();
16898             this.setValue('');
16899             return;
16900         }
16901         
16902         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16903             
16904             event.preventDefault();
16905             // this is very hacky as keydown always get's upper case.
16906             
16907             var cc = String.fromCharCode(event.getCharCode());
16908             
16909             
16910             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16911             
16912         }
16913         
16914         
16915     }
16916 });/*
16917  * Based on:
16918  * Ext JS Library 1.1.1
16919  * Copyright(c) 2006-2007, Ext JS, LLC.
16920  *
16921  * Originally Released Under LGPL - original licence link has changed is not relivant.
16922  *
16923  * Fork - LGPL
16924  * <script type="text/javascript">
16925  */
16926  
16927 /**
16928  * @class Roo.form.Hidden
16929  * @extends Roo.form.TextField
16930  * Simple Hidden element used on forms 
16931  * 
16932  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16933  * 
16934  * @constructor
16935  * Creates a new Hidden form element.
16936  * @param {Object} config Configuration options
16937  */
16938
16939
16940
16941 // easy hidden field...
16942 Roo.form.Hidden = function(config){
16943     Roo.form.Hidden.superclass.constructor.call(this, config);
16944 };
16945   
16946 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16947     fieldLabel:      '',
16948     inputType:      'hidden',
16949     width:          50,
16950     allowBlank:     true,
16951     labelSeparator: '',
16952     hidden:         true,
16953     itemCls :       'x-form-item-display-none'
16954
16955
16956 });
16957
16958
16959 /*
16960  * Based on:
16961  * Ext JS Library 1.1.1
16962  * Copyright(c) 2006-2007, Ext JS, LLC.
16963  *
16964  * Originally Released Under LGPL - original licence link has changed is not relivant.
16965  *
16966  * Fork - LGPL
16967  * <script type="text/javascript">
16968  */
16969  
16970 /**
16971  * @class Roo.form.TriggerField
16972  * @extends Roo.form.TextField
16973  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16974  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16975  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16976  * for which you can provide a custom implementation.  For example:
16977  * <pre><code>
16978 var trigger = new Roo.form.TriggerField();
16979 trigger.onTriggerClick = myTriggerFn;
16980 trigger.applyTo('my-field');
16981 </code></pre>
16982  *
16983  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16984  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16985  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16986  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16987  * @constructor
16988  * Create a new TriggerField.
16989  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16990  * to the base TextField)
16991  */
16992 Roo.form.TriggerField = function(config){
16993     this.mimicing = false;
16994     Roo.form.TriggerField.superclass.constructor.call(this, config);
16995 };
16996
16997 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16998     /**
16999      * @cfg {String} triggerClass A CSS class to apply to the trigger
17000      */
17001     /**
17002      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17003      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17004      */
17005     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17006     /**
17007      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17008      */
17009     hideTrigger:false,
17010
17011     /** @cfg {Boolean} grow @hide */
17012     /** @cfg {Number} growMin @hide */
17013     /** @cfg {Number} growMax @hide */
17014
17015     /**
17016      * @hide 
17017      * @method
17018      */
17019     autoSize: Roo.emptyFn,
17020     // private
17021     monitorTab : true,
17022     // private
17023     deferHeight : true,
17024
17025     
17026     actionMode : 'wrap',
17027     // private
17028     onResize : function(w, h){
17029         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17030         if(typeof w == 'number'){
17031             var x = w - this.trigger.getWidth();
17032             this.el.setWidth(this.adjustWidth('input', x));
17033             this.trigger.setStyle('left', x+'px');
17034         }
17035     },
17036
17037     // private
17038     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17039
17040     // private
17041     getResizeEl : function(){
17042         return this.wrap;
17043     },
17044
17045     // private
17046     getPositionEl : function(){
17047         return this.wrap;
17048     },
17049
17050     // private
17051     alignErrorIcon : function(){
17052         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17053     },
17054
17055     // private
17056     onRender : function(ct, position){
17057         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17058         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17059         this.trigger = this.wrap.createChild(this.triggerConfig ||
17060                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17061         if(this.hideTrigger){
17062             this.trigger.setDisplayed(false);
17063         }
17064         this.initTrigger();
17065         if(!this.width){
17066             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17067         }
17068     },
17069
17070     // private
17071     initTrigger : function(){
17072         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17073         this.trigger.addClassOnOver('x-form-trigger-over');
17074         this.trigger.addClassOnClick('x-form-trigger-click');
17075     },
17076
17077     // private
17078     onDestroy : function(){
17079         if(this.trigger){
17080             this.trigger.removeAllListeners();
17081             this.trigger.remove();
17082         }
17083         if(this.wrap){
17084             this.wrap.remove();
17085         }
17086         Roo.form.TriggerField.superclass.onDestroy.call(this);
17087     },
17088
17089     // private
17090     onFocus : function(){
17091         Roo.form.TriggerField.superclass.onFocus.call(this);
17092         if(!this.mimicing){
17093             this.wrap.addClass('x-trigger-wrap-focus');
17094             this.mimicing = true;
17095             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17096             if(this.monitorTab){
17097                 this.el.on("keydown", this.checkTab, this);
17098             }
17099         }
17100     },
17101
17102     // private
17103     checkTab : function(e){
17104         if(e.getKey() == e.TAB){
17105             this.triggerBlur();
17106         }
17107     },
17108
17109     // private
17110     onBlur : function(){
17111         // do nothing
17112     },
17113
17114     // private
17115     mimicBlur : function(e, t){
17116         if(!this.wrap.contains(t) && this.validateBlur()){
17117             this.triggerBlur();
17118         }
17119     },
17120
17121     // private
17122     triggerBlur : function(){
17123         this.mimicing = false;
17124         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17125         if(this.monitorTab){
17126             this.el.un("keydown", this.checkTab, this);
17127         }
17128         this.wrap.removeClass('x-trigger-wrap-focus');
17129         Roo.form.TriggerField.superclass.onBlur.call(this);
17130     },
17131
17132     // private
17133     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17134     validateBlur : function(e, t){
17135         return true;
17136     },
17137
17138     // private
17139     onDisable : function(){
17140         Roo.form.TriggerField.superclass.onDisable.call(this);
17141         if(this.wrap){
17142             this.wrap.addClass('x-item-disabled');
17143         }
17144     },
17145
17146     // private
17147     onEnable : function(){
17148         Roo.form.TriggerField.superclass.onEnable.call(this);
17149         if(this.wrap){
17150             this.wrap.removeClass('x-item-disabled');
17151         }
17152     },
17153
17154     // private
17155     onShow : function(){
17156         var ae = this.getActionEl();
17157         
17158         if(ae){
17159             ae.dom.style.display = '';
17160             ae.dom.style.visibility = 'visible';
17161         }
17162     },
17163
17164     // private
17165     
17166     onHide : function(){
17167         var ae = this.getActionEl();
17168         ae.dom.style.display = 'none';
17169     },
17170
17171     /**
17172      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17173      * by an implementing function.
17174      * @method
17175      * @param {EventObject} e
17176      */
17177     onTriggerClick : Roo.emptyFn
17178 });
17179
17180 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17181 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17182 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17183 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17184     initComponent : function(){
17185         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17186
17187         this.triggerConfig = {
17188             tag:'span', cls:'x-form-twin-triggers', cn:[
17189             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17190             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17191         ]};
17192     },
17193
17194     getTrigger : function(index){
17195         return this.triggers[index];
17196     },
17197
17198     initTrigger : function(){
17199         var ts = this.trigger.select('.x-form-trigger', true);
17200         this.wrap.setStyle('overflow', 'hidden');
17201         var triggerField = this;
17202         ts.each(function(t, all, index){
17203             t.hide = function(){
17204                 var w = triggerField.wrap.getWidth();
17205                 this.dom.style.display = 'none';
17206                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17207             };
17208             t.show = function(){
17209                 var w = triggerField.wrap.getWidth();
17210                 this.dom.style.display = '';
17211                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17212             };
17213             var triggerIndex = 'Trigger'+(index+1);
17214
17215             if(this['hide'+triggerIndex]){
17216                 t.dom.style.display = 'none';
17217             }
17218             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17219             t.addClassOnOver('x-form-trigger-over');
17220             t.addClassOnClick('x-form-trigger-click');
17221         }, this);
17222         this.triggers = ts.elements;
17223     },
17224
17225     onTrigger1Click : Roo.emptyFn,
17226     onTrigger2Click : Roo.emptyFn
17227 });/*
17228  * Based on:
17229  * Ext JS Library 1.1.1
17230  * Copyright(c) 2006-2007, Ext JS, LLC.
17231  *
17232  * Originally Released Under LGPL - original licence link has changed is not relivant.
17233  *
17234  * Fork - LGPL
17235  * <script type="text/javascript">
17236  */
17237  
17238 /**
17239  * @class Roo.form.TextArea
17240  * @extends Roo.form.TextField
17241  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17242  * support for auto-sizing.
17243  * @constructor
17244  * Creates a new TextArea
17245  * @param {Object} config Configuration options
17246  */
17247 Roo.form.TextArea = function(config){
17248     Roo.form.TextArea.superclass.constructor.call(this, config);
17249     // these are provided exchanges for backwards compat
17250     // minHeight/maxHeight were replaced by growMin/growMax to be
17251     // compatible with TextField growing config values
17252     if(this.minHeight !== undefined){
17253         this.growMin = this.minHeight;
17254     }
17255     if(this.maxHeight !== undefined){
17256         this.growMax = this.maxHeight;
17257     }
17258 };
17259
17260 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17261     /**
17262      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17263      */
17264     growMin : 60,
17265     /**
17266      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17267      */
17268     growMax: 1000,
17269     /**
17270      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17271      * in the field (equivalent to setting overflow: hidden, defaults to false)
17272      */
17273     preventScrollbars: false,
17274     /**
17275      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17276      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17277      */
17278
17279     // private
17280     onRender : function(ct, position){
17281         if(!this.el){
17282             this.defaultAutoCreate = {
17283                 tag: "textarea",
17284                 style:"width:300px;height:60px;",
17285                 autocomplete: "new-password"
17286             };
17287         }
17288         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17289         if(this.grow){
17290             this.textSizeEl = Roo.DomHelper.append(document.body, {
17291                 tag: "pre", cls: "x-form-grow-sizer"
17292             });
17293             if(this.preventScrollbars){
17294                 this.el.setStyle("overflow", "hidden");
17295             }
17296             this.el.setHeight(this.growMin);
17297         }
17298     },
17299
17300     onDestroy : function(){
17301         if(this.textSizeEl){
17302             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17303         }
17304         Roo.form.TextArea.superclass.onDestroy.call(this);
17305     },
17306
17307     // private
17308     onKeyUp : function(e){
17309         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17310             this.autoSize();
17311         }
17312     },
17313
17314     /**
17315      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17316      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17317      */
17318     autoSize : function(){
17319         if(!this.grow || !this.textSizeEl){
17320             return;
17321         }
17322         var el = this.el;
17323         var v = el.dom.value;
17324         var ts = this.textSizeEl;
17325
17326         ts.innerHTML = '';
17327         ts.appendChild(document.createTextNode(v));
17328         v = ts.innerHTML;
17329
17330         Roo.fly(ts).setWidth(this.el.getWidth());
17331         if(v.length < 1){
17332             v = "&#160;&#160;";
17333         }else{
17334             if(Roo.isIE){
17335                 v = v.replace(/\n/g, '<p>&#160;</p>');
17336             }
17337             v += "&#160;\n&#160;";
17338         }
17339         ts.innerHTML = v;
17340         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17341         if(h != this.lastHeight){
17342             this.lastHeight = h;
17343             this.el.setHeight(h);
17344             this.fireEvent("autosize", this, h);
17345         }
17346     }
17347 });/*
17348  * Based on:
17349  * Ext JS Library 1.1.1
17350  * Copyright(c) 2006-2007, Ext JS, LLC.
17351  *
17352  * Originally Released Under LGPL - original licence link has changed is not relivant.
17353  *
17354  * Fork - LGPL
17355  * <script type="text/javascript">
17356  */
17357  
17358
17359 /**
17360  * @class Roo.form.NumberField
17361  * @extends Roo.form.TextField
17362  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17363  * @constructor
17364  * Creates a new NumberField
17365  * @param {Object} config Configuration options
17366  */
17367 Roo.form.NumberField = function(config){
17368     Roo.form.NumberField.superclass.constructor.call(this, config);
17369 };
17370
17371 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17372     /**
17373      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17374      */
17375     fieldClass: "x-form-field x-form-num-field",
17376     /**
17377      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17378      */
17379     allowDecimals : true,
17380     /**
17381      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17382      */
17383     decimalSeparator : ".",
17384     /**
17385      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17386      */
17387     decimalPrecision : 2,
17388     /**
17389      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17390      */
17391     allowNegative : true,
17392     /**
17393      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17394      */
17395     minValue : Number.NEGATIVE_INFINITY,
17396     /**
17397      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17398      */
17399     maxValue : Number.MAX_VALUE,
17400     /**
17401      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17402      */
17403     minText : "The minimum value for this field is {0}",
17404     /**
17405      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17406      */
17407     maxText : "The maximum value for this field is {0}",
17408     /**
17409      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17410      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17411      */
17412     nanText : "{0} is not a valid number",
17413
17414     // private
17415     initEvents : function(){
17416         Roo.form.NumberField.superclass.initEvents.call(this);
17417         var allowed = "0123456789";
17418         if(this.allowDecimals){
17419             allowed += this.decimalSeparator;
17420         }
17421         if(this.allowNegative){
17422             allowed += "-";
17423         }
17424         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17425         var keyPress = function(e){
17426             var k = e.getKey();
17427             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17428                 return;
17429             }
17430             var c = e.getCharCode();
17431             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17432                 e.stopEvent();
17433             }
17434         };
17435         this.el.on("keypress", keyPress, this);
17436     },
17437
17438     // private
17439     validateValue : function(value){
17440         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17441             return false;
17442         }
17443         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17444              return true;
17445         }
17446         var num = this.parseValue(value);
17447         if(isNaN(num)){
17448             this.markInvalid(String.format(this.nanText, value));
17449             return false;
17450         }
17451         if(num < this.minValue){
17452             this.markInvalid(String.format(this.minText, this.minValue));
17453             return false;
17454         }
17455         if(num > this.maxValue){
17456             this.markInvalid(String.format(this.maxText, this.maxValue));
17457             return false;
17458         }
17459         return true;
17460     },
17461
17462     getValue : function(){
17463         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17464     },
17465
17466     // private
17467     parseValue : function(value){
17468         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17469         return isNaN(value) ? '' : value;
17470     },
17471
17472     // private
17473     fixPrecision : function(value){
17474         var nan = isNaN(value);
17475         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17476             return nan ? '' : value;
17477         }
17478         return parseFloat(value).toFixed(this.decimalPrecision);
17479     },
17480
17481     setValue : function(v){
17482         v = this.fixPrecision(v);
17483         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17484     },
17485
17486     // private
17487     decimalPrecisionFcn : function(v){
17488         return Math.floor(v);
17489     },
17490
17491     beforeBlur : function(){
17492         var v = this.parseValue(this.getRawValue());
17493         if(v){
17494             this.setValue(v);
17495         }
17496     }
17497 });/*
17498  * Based on:
17499  * Ext JS Library 1.1.1
17500  * Copyright(c) 2006-2007, Ext JS, LLC.
17501  *
17502  * Originally Released Under LGPL - original licence link has changed is not relivant.
17503  *
17504  * Fork - LGPL
17505  * <script type="text/javascript">
17506  */
17507  
17508 /**
17509  * @class Roo.form.DateField
17510  * @extends Roo.form.TriggerField
17511  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17512 * @constructor
17513 * Create a new DateField
17514 * @param {Object} config
17515  */
17516 Roo.form.DateField = function(config)
17517 {
17518     Roo.form.DateField.superclass.constructor.call(this, config);
17519     
17520       this.addEvents({
17521          
17522         /**
17523          * @event select
17524          * Fires when a date is selected
17525              * @param {Roo.form.DateField} combo This combo box
17526              * @param {Date} date The date selected
17527              */
17528         'select' : true
17529          
17530     });
17531     
17532     
17533     if(typeof this.minValue == "string") {
17534         this.minValue = this.parseDate(this.minValue);
17535     }
17536     if(typeof this.maxValue == "string") {
17537         this.maxValue = this.parseDate(this.maxValue);
17538     }
17539     this.ddMatch = null;
17540     if(this.disabledDates){
17541         var dd = this.disabledDates;
17542         var re = "(?:";
17543         for(var i = 0; i < dd.length; i++){
17544             re += dd[i];
17545             if(i != dd.length-1) {
17546                 re += "|";
17547             }
17548         }
17549         this.ddMatch = new RegExp(re + ")");
17550     }
17551 };
17552
17553 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17554     /**
17555      * @cfg {String} format
17556      * The default date format string which can be overriden for localization support.  The format must be
17557      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17558      */
17559     format : "m/d/y",
17560     /**
17561      * @cfg {String} altFormats
17562      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17563      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17564      */
17565     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17566     /**
17567      * @cfg {Array} disabledDays
17568      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17569      */
17570     disabledDays : null,
17571     /**
17572      * @cfg {String} disabledDaysText
17573      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17574      */
17575     disabledDaysText : "Disabled",
17576     /**
17577      * @cfg {Array} disabledDates
17578      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17579      * expression so they are very powerful. Some examples:
17580      * <ul>
17581      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17582      * <li>["03/08", "09/16"] would disable those days for every year</li>
17583      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17584      * <li>["03/../2006"] would disable every day in March 2006</li>
17585      * <li>["^03"] would disable every day in every March</li>
17586      * </ul>
17587      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17588      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17589      */
17590     disabledDates : null,
17591     /**
17592      * @cfg {String} disabledDatesText
17593      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17594      */
17595     disabledDatesText : "Disabled",
17596         
17597         
17598         /**
17599      * @cfg {Date/String} zeroValue
17600      * if the date is less that this number, then the field is rendered as empty
17601      * default is 1800
17602      */
17603         zeroValue : '1800-01-01',
17604         
17605         
17606     /**
17607      * @cfg {Date/String} minValue
17608      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17609      * valid format (defaults to null).
17610      */
17611     minValue : null,
17612     /**
17613      * @cfg {Date/String} maxValue
17614      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17615      * valid format (defaults to null).
17616      */
17617     maxValue : null,
17618     /**
17619      * @cfg {String} minText
17620      * The error text to display when the date in the cell is before minValue (defaults to
17621      * 'The date in this field must be after {minValue}').
17622      */
17623     minText : "The date in this field must be equal to or after {0}",
17624     /**
17625      * @cfg {String} maxText
17626      * The error text to display when the date in the cell is after maxValue (defaults to
17627      * 'The date in this field must be before {maxValue}').
17628      */
17629     maxText : "The date in this field must be equal to or before {0}",
17630     /**
17631      * @cfg {String} invalidText
17632      * The error text to display when the date in the field is invalid (defaults to
17633      * '{value} is not a valid date - it must be in the format {format}').
17634      */
17635     invalidText : "{0} is not a valid date - it must be in the format {1}",
17636     /**
17637      * @cfg {String} triggerClass
17638      * An additional CSS class used to style the trigger button.  The trigger will always get the
17639      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17640      * which displays a calendar icon).
17641      */
17642     triggerClass : 'x-form-date-trigger',
17643     
17644
17645     /**
17646      * @cfg {Boolean} useIso
17647      * if enabled, then the date field will use a hidden field to store the 
17648      * real value as iso formated date. default (false)
17649      */ 
17650     useIso : false,
17651     /**
17652      * @cfg {String/Object} autoCreate
17653      * A DomHelper element spec, or true for a default element spec (defaults to
17654      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17655      */ 
17656     // private
17657     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17658     
17659     // private
17660     hiddenField: false,
17661     
17662     onRender : function(ct, position)
17663     {
17664         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17665         if (this.useIso) {
17666             //this.el.dom.removeAttribute('name'); 
17667             Roo.log("Changing name?");
17668             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17669             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17670                     'before', true);
17671             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17672             // prevent input submission
17673             this.hiddenName = this.name;
17674         }
17675             
17676             
17677     },
17678     
17679     // private
17680     validateValue : function(value)
17681     {
17682         value = this.formatDate(value);
17683         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17684             Roo.log('super failed');
17685             return false;
17686         }
17687         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17688              return true;
17689         }
17690         var svalue = value;
17691         value = this.parseDate(value);
17692         if(!value){
17693             Roo.log('parse date failed' + svalue);
17694             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17695             return false;
17696         }
17697         var time = value.getTime();
17698         if(this.minValue && time < this.minValue.getTime()){
17699             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17700             return false;
17701         }
17702         if(this.maxValue && time > this.maxValue.getTime()){
17703             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17704             return false;
17705         }
17706         if(this.disabledDays){
17707             var day = value.getDay();
17708             for(var i = 0; i < this.disabledDays.length; i++) {
17709                 if(day === this.disabledDays[i]){
17710                     this.markInvalid(this.disabledDaysText);
17711                     return false;
17712                 }
17713             }
17714         }
17715         var fvalue = this.formatDate(value);
17716         if(this.ddMatch && this.ddMatch.test(fvalue)){
17717             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17718             return false;
17719         }
17720         return true;
17721     },
17722
17723     // private
17724     // Provides logic to override the default TriggerField.validateBlur which just returns true
17725     validateBlur : function(){
17726         return !this.menu || !this.menu.isVisible();
17727     },
17728     
17729     getName: function()
17730     {
17731         // returns hidden if it's set..
17732         if (!this.rendered) {return ''};
17733         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17734         
17735     },
17736
17737     /**
17738      * Returns the current date value of the date field.
17739      * @return {Date} The date value
17740      */
17741     getValue : function(){
17742         
17743         return  this.hiddenField ?
17744                 this.hiddenField.value :
17745                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17746     },
17747
17748     /**
17749      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17750      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17751      * (the default format used is "m/d/y").
17752      * <br />Usage:
17753      * <pre><code>
17754 //All of these calls set the same date value (May 4, 2006)
17755
17756 //Pass a date object:
17757 var dt = new Date('5/4/06');
17758 dateField.setValue(dt);
17759
17760 //Pass a date string (default format):
17761 dateField.setValue('5/4/06');
17762
17763 //Pass a date string (custom format):
17764 dateField.format = 'Y-m-d';
17765 dateField.setValue('2006-5-4');
17766 </code></pre>
17767      * @param {String/Date} date The date or valid date string
17768      */
17769     setValue : function(date){
17770         if (this.hiddenField) {
17771             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17772         }
17773         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17774         // make sure the value field is always stored as a date..
17775         this.value = this.parseDate(date);
17776         
17777         
17778     },
17779
17780     // private
17781     parseDate : function(value){
17782                 
17783                 if (value instanceof Date) {
17784                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17785                                 return  '';
17786                         }
17787                         return value;
17788                 }
17789                 
17790                 
17791         if(!value || value instanceof Date){
17792             return value;
17793         }
17794         var v = Date.parseDate(value, this.format);
17795          if (!v && this.useIso) {
17796             v = Date.parseDate(value, 'Y-m-d');
17797         }
17798         if(!v && this.altFormats){
17799             if(!this.altFormatsArray){
17800                 this.altFormatsArray = this.altFormats.split("|");
17801             }
17802             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17803                 v = Date.parseDate(value, this.altFormatsArray[i]);
17804             }
17805         }
17806                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17807                         v = '';
17808                 }
17809         return v;
17810     },
17811
17812     // private
17813     formatDate : function(date, fmt){
17814         return (!date || !(date instanceof Date)) ?
17815                date : date.dateFormat(fmt || this.format);
17816     },
17817
17818     // private
17819     menuListeners : {
17820         select: function(m, d){
17821             
17822             this.setValue(d);
17823             this.fireEvent('select', this, d);
17824         },
17825         show : function(){ // retain focus styling
17826             this.onFocus();
17827         },
17828         hide : function(){
17829             this.focus.defer(10, this);
17830             var ml = this.menuListeners;
17831             this.menu.un("select", ml.select,  this);
17832             this.menu.un("show", ml.show,  this);
17833             this.menu.un("hide", ml.hide,  this);
17834         }
17835     },
17836
17837     // private
17838     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17839     onTriggerClick : function(){
17840         if(this.disabled){
17841             return;
17842         }
17843         if(this.menu == null){
17844             this.menu = new Roo.menu.DateMenu();
17845         }
17846         Roo.apply(this.menu.picker,  {
17847             showClear: this.allowBlank,
17848             minDate : this.minValue,
17849             maxDate : this.maxValue,
17850             disabledDatesRE : this.ddMatch,
17851             disabledDatesText : this.disabledDatesText,
17852             disabledDays : this.disabledDays,
17853             disabledDaysText : this.disabledDaysText,
17854             format : this.useIso ? 'Y-m-d' : this.format,
17855             minText : String.format(this.minText, this.formatDate(this.minValue)),
17856             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17857         });
17858         this.menu.on(Roo.apply({}, this.menuListeners, {
17859             scope:this
17860         }));
17861         this.menu.picker.setValue(this.getValue() || new Date());
17862         this.menu.show(this.el, "tl-bl?");
17863     },
17864
17865     beforeBlur : function(){
17866         var v = this.parseDate(this.getRawValue());
17867         if(v){
17868             this.setValue(v);
17869         }
17870     },
17871
17872     /*@
17873      * overide
17874      * 
17875      */
17876     isDirty : function() {
17877         if(this.disabled) {
17878             return false;
17879         }
17880         
17881         if(typeof(this.startValue) === 'undefined'){
17882             return false;
17883         }
17884         
17885         return String(this.getValue()) !== String(this.startValue);
17886         
17887     },
17888     // @overide
17889     cleanLeadingSpace : function(e)
17890     {
17891        return;
17892     }
17893     
17894 });/*
17895  * Based on:
17896  * Ext JS Library 1.1.1
17897  * Copyright(c) 2006-2007, Ext JS, LLC.
17898  *
17899  * Originally Released Under LGPL - original licence link has changed is not relivant.
17900  *
17901  * Fork - LGPL
17902  * <script type="text/javascript">
17903  */
17904  
17905 /**
17906  * @class Roo.form.MonthField
17907  * @extends Roo.form.TriggerField
17908  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17909 * @constructor
17910 * Create a new MonthField
17911 * @param {Object} config
17912  */
17913 Roo.form.MonthField = function(config){
17914     
17915     Roo.form.MonthField.superclass.constructor.call(this, config);
17916     
17917       this.addEvents({
17918          
17919         /**
17920          * @event select
17921          * Fires when a date is selected
17922              * @param {Roo.form.MonthFieeld} combo This combo box
17923              * @param {Date} date The date selected
17924              */
17925         'select' : true
17926          
17927     });
17928     
17929     
17930     if(typeof this.minValue == "string") {
17931         this.minValue = this.parseDate(this.minValue);
17932     }
17933     if(typeof this.maxValue == "string") {
17934         this.maxValue = this.parseDate(this.maxValue);
17935     }
17936     this.ddMatch = null;
17937     if(this.disabledDates){
17938         var dd = this.disabledDates;
17939         var re = "(?:";
17940         for(var i = 0; i < dd.length; i++){
17941             re += dd[i];
17942             if(i != dd.length-1) {
17943                 re += "|";
17944             }
17945         }
17946         this.ddMatch = new RegExp(re + ")");
17947     }
17948 };
17949
17950 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17951     /**
17952      * @cfg {String} format
17953      * The default date format string which can be overriden for localization support.  The format must be
17954      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17955      */
17956     format : "M Y",
17957     /**
17958      * @cfg {String} altFormats
17959      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17960      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17961      */
17962     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17963     /**
17964      * @cfg {Array} disabledDays
17965      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17966      */
17967     disabledDays : [0,1,2,3,4,5,6],
17968     /**
17969      * @cfg {String} disabledDaysText
17970      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17971      */
17972     disabledDaysText : "Disabled",
17973     /**
17974      * @cfg {Array} disabledDates
17975      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17976      * expression so they are very powerful. Some examples:
17977      * <ul>
17978      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17979      * <li>["03/08", "09/16"] would disable those days for every year</li>
17980      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17981      * <li>["03/../2006"] would disable every day in March 2006</li>
17982      * <li>["^03"] would disable every day in every March</li>
17983      * </ul>
17984      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17985      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17986      */
17987     disabledDates : null,
17988     /**
17989      * @cfg {String} disabledDatesText
17990      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17991      */
17992     disabledDatesText : "Disabled",
17993     /**
17994      * @cfg {Date/String} minValue
17995      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17996      * valid format (defaults to null).
17997      */
17998     minValue : null,
17999     /**
18000      * @cfg {Date/String} maxValue
18001      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18002      * valid format (defaults to null).
18003      */
18004     maxValue : null,
18005     /**
18006      * @cfg {String} minText
18007      * The error text to display when the date in the cell is before minValue (defaults to
18008      * 'The date in this field must be after {minValue}').
18009      */
18010     minText : "The date in this field must be equal to or after {0}",
18011     /**
18012      * @cfg {String} maxTextf
18013      * The error text to display when the date in the cell is after maxValue (defaults to
18014      * 'The date in this field must be before {maxValue}').
18015      */
18016     maxText : "The date in this field must be equal to or before {0}",
18017     /**
18018      * @cfg {String} invalidText
18019      * The error text to display when the date in the field is invalid (defaults to
18020      * '{value} is not a valid date - it must be in the format {format}').
18021      */
18022     invalidText : "{0} is not a valid date - it must be in the format {1}",
18023     /**
18024      * @cfg {String} triggerClass
18025      * An additional CSS class used to style the trigger button.  The trigger will always get the
18026      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18027      * which displays a calendar icon).
18028      */
18029     triggerClass : 'x-form-date-trigger',
18030     
18031
18032     /**
18033      * @cfg {Boolean} useIso
18034      * if enabled, then the date field will use a hidden field to store the 
18035      * real value as iso formated date. default (true)
18036      */ 
18037     useIso : true,
18038     /**
18039      * @cfg {String/Object} autoCreate
18040      * A DomHelper element spec, or true for a default element spec (defaults to
18041      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18042      */ 
18043     // private
18044     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18045     
18046     // private
18047     hiddenField: false,
18048     
18049     hideMonthPicker : false,
18050     
18051     onRender : function(ct, position)
18052     {
18053         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18054         if (this.useIso) {
18055             this.el.dom.removeAttribute('name'); 
18056             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18057                     'before', true);
18058             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18059             // prevent input submission
18060             this.hiddenName = this.name;
18061         }
18062             
18063             
18064     },
18065     
18066     // private
18067     validateValue : function(value)
18068     {
18069         value = this.formatDate(value);
18070         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18071             return false;
18072         }
18073         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18074              return true;
18075         }
18076         var svalue = value;
18077         value = this.parseDate(value);
18078         if(!value){
18079             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18080             return false;
18081         }
18082         var time = value.getTime();
18083         if(this.minValue && time < this.minValue.getTime()){
18084             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18085             return false;
18086         }
18087         if(this.maxValue && time > this.maxValue.getTime()){
18088             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18089             return false;
18090         }
18091         /*if(this.disabledDays){
18092             var day = value.getDay();
18093             for(var i = 0; i < this.disabledDays.length; i++) {
18094                 if(day === this.disabledDays[i]){
18095                     this.markInvalid(this.disabledDaysText);
18096                     return false;
18097                 }
18098             }
18099         }
18100         */
18101         var fvalue = this.formatDate(value);
18102         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18103             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18104             return false;
18105         }
18106         */
18107         return true;
18108     },
18109
18110     // private
18111     // Provides logic to override the default TriggerField.validateBlur which just returns true
18112     validateBlur : function(){
18113         return !this.menu || !this.menu.isVisible();
18114     },
18115
18116     /**
18117      * Returns the current date value of the date field.
18118      * @return {Date} The date value
18119      */
18120     getValue : function(){
18121         
18122         
18123         
18124         return  this.hiddenField ?
18125                 this.hiddenField.value :
18126                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18127     },
18128
18129     /**
18130      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18131      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18132      * (the default format used is "m/d/y").
18133      * <br />Usage:
18134      * <pre><code>
18135 //All of these calls set the same date value (May 4, 2006)
18136
18137 //Pass a date object:
18138 var dt = new Date('5/4/06');
18139 monthField.setValue(dt);
18140
18141 //Pass a date string (default format):
18142 monthField.setValue('5/4/06');
18143
18144 //Pass a date string (custom format):
18145 monthField.format = 'Y-m-d';
18146 monthField.setValue('2006-5-4');
18147 </code></pre>
18148      * @param {String/Date} date The date or valid date string
18149      */
18150     setValue : function(date){
18151         Roo.log('month setValue' + date);
18152         // can only be first of month..
18153         
18154         var val = this.parseDate(date);
18155         
18156         if (this.hiddenField) {
18157             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18158         }
18159         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18160         this.value = this.parseDate(date);
18161     },
18162
18163     // private
18164     parseDate : function(value){
18165         if(!value || value instanceof Date){
18166             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18167             return value;
18168         }
18169         var v = Date.parseDate(value, this.format);
18170         if (!v && this.useIso) {
18171             v = Date.parseDate(value, 'Y-m-d');
18172         }
18173         if (v) {
18174             // 
18175             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18176         }
18177         
18178         
18179         if(!v && this.altFormats){
18180             if(!this.altFormatsArray){
18181                 this.altFormatsArray = this.altFormats.split("|");
18182             }
18183             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18184                 v = Date.parseDate(value, this.altFormatsArray[i]);
18185             }
18186         }
18187         return v;
18188     },
18189
18190     // private
18191     formatDate : function(date, fmt){
18192         return (!date || !(date instanceof Date)) ?
18193                date : date.dateFormat(fmt || this.format);
18194     },
18195
18196     // private
18197     menuListeners : {
18198         select: function(m, d){
18199             this.setValue(d);
18200             this.fireEvent('select', this, d);
18201         },
18202         show : function(){ // retain focus styling
18203             this.onFocus();
18204         },
18205         hide : function(){
18206             this.focus.defer(10, this);
18207             var ml = this.menuListeners;
18208             this.menu.un("select", ml.select,  this);
18209             this.menu.un("show", ml.show,  this);
18210             this.menu.un("hide", ml.hide,  this);
18211         }
18212     },
18213     // private
18214     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18215     onTriggerClick : function(){
18216         if(this.disabled){
18217             return;
18218         }
18219         if(this.menu == null){
18220             this.menu = new Roo.menu.DateMenu();
18221            
18222         }
18223         
18224         Roo.apply(this.menu.picker,  {
18225             
18226             showClear: this.allowBlank,
18227             minDate : this.minValue,
18228             maxDate : this.maxValue,
18229             disabledDatesRE : this.ddMatch,
18230             disabledDatesText : this.disabledDatesText,
18231             
18232             format : this.useIso ? 'Y-m-d' : this.format,
18233             minText : String.format(this.minText, this.formatDate(this.minValue)),
18234             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18235             
18236         });
18237          this.menu.on(Roo.apply({}, this.menuListeners, {
18238             scope:this
18239         }));
18240        
18241         
18242         var m = this.menu;
18243         var p = m.picker;
18244         
18245         // hide month picker get's called when we called by 'before hide';
18246         
18247         var ignorehide = true;
18248         p.hideMonthPicker  = function(disableAnim){
18249             if (ignorehide) {
18250                 return;
18251             }
18252              if(this.monthPicker){
18253                 Roo.log("hideMonthPicker called");
18254                 if(disableAnim === true){
18255                     this.monthPicker.hide();
18256                 }else{
18257                     this.monthPicker.slideOut('t', {duration:.2});
18258                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18259                     p.fireEvent("select", this, this.value);
18260                     m.hide();
18261                 }
18262             }
18263         }
18264         
18265         Roo.log('picker set value');
18266         Roo.log(this.getValue());
18267         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18268         m.show(this.el, 'tl-bl?');
18269         ignorehide  = false;
18270         // this will trigger hideMonthPicker..
18271         
18272         
18273         // hidden the day picker
18274         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18275         
18276         
18277         
18278       
18279         
18280         p.showMonthPicker.defer(100, p);
18281     
18282         
18283        
18284     },
18285
18286     beforeBlur : function(){
18287         var v = this.parseDate(this.getRawValue());
18288         if(v){
18289             this.setValue(v);
18290         }
18291     }
18292
18293     /** @cfg {Boolean} grow @hide */
18294     /** @cfg {Number} growMin @hide */
18295     /** @cfg {Number} growMax @hide */
18296     /**
18297      * @hide
18298      * @method autoSize
18299      */
18300 });/*
18301  * Based on:
18302  * Ext JS Library 1.1.1
18303  * Copyright(c) 2006-2007, Ext JS, LLC.
18304  *
18305  * Originally Released Under LGPL - original licence link has changed is not relivant.
18306  *
18307  * Fork - LGPL
18308  * <script type="text/javascript">
18309  */
18310  
18311
18312 /**
18313  * @class Roo.form.ComboBox
18314  * @extends Roo.form.TriggerField
18315  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18316  * @constructor
18317  * Create a new ComboBox.
18318  * @param {Object} config Configuration options
18319  */
18320 Roo.form.ComboBox = function(config){
18321     Roo.form.ComboBox.superclass.constructor.call(this, config);
18322     this.addEvents({
18323         /**
18324          * @event expand
18325          * Fires when the dropdown list is expanded
18326              * @param {Roo.form.ComboBox} combo This combo box
18327              */
18328         'expand' : true,
18329         /**
18330          * @event collapse
18331          * Fires when the dropdown list is collapsed
18332              * @param {Roo.form.ComboBox} combo This combo box
18333              */
18334         'collapse' : true,
18335         /**
18336          * @event beforeselect
18337          * Fires before a list item is selected. Return false to cancel the selection.
18338              * @param {Roo.form.ComboBox} combo This combo box
18339              * @param {Roo.data.Record} record The data record returned from the underlying store
18340              * @param {Number} index The index of the selected item in the dropdown list
18341              */
18342         'beforeselect' : true,
18343         /**
18344          * @event select
18345          * Fires when a list item is selected
18346              * @param {Roo.form.ComboBox} combo This combo box
18347              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18348              * @param {Number} index The index of the selected item in the dropdown list
18349              */
18350         'select' : true,
18351         /**
18352          * @event beforequery
18353          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18354          * The event object passed has these properties:
18355              * @param {Roo.form.ComboBox} combo This combo box
18356              * @param {String} query The query
18357              * @param {Boolean} forceAll true to force "all" query
18358              * @param {Boolean} cancel true to cancel the query
18359              * @param {Object} e The query event object
18360              */
18361         'beforequery': true,
18362          /**
18363          * @event add
18364          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18365              * @param {Roo.form.ComboBox} combo This combo box
18366              */
18367         'add' : true,
18368         /**
18369          * @event edit
18370          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18371              * @param {Roo.form.ComboBox} combo This combo box
18372              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18373              */
18374         'edit' : true
18375         
18376         
18377     });
18378     if(this.transform){
18379         this.allowDomMove = false;
18380         var s = Roo.getDom(this.transform);
18381         if(!this.hiddenName){
18382             this.hiddenName = s.name;
18383         }
18384         if(!this.store){
18385             this.mode = 'local';
18386             var d = [], opts = s.options;
18387             for(var i = 0, len = opts.length;i < len; i++){
18388                 var o = opts[i];
18389                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18390                 if(o.selected) {
18391                     this.value = value;
18392                 }
18393                 d.push([value, o.text]);
18394             }
18395             this.store = new Roo.data.SimpleStore({
18396                 'id': 0,
18397                 fields: ['value', 'text'],
18398                 data : d
18399             });
18400             this.valueField = 'value';
18401             this.displayField = 'text';
18402         }
18403         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18404         if(!this.lazyRender){
18405             this.target = true;
18406             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18407             s.parentNode.removeChild(s); // remove it
18408             this.render(this.el.parentNode);
18409         }else{
18410             s.parentNode.removeChild(s); // remove it
18411         }
18412
18413     }
18414     if (this.store) {
18415         this.store = Roo.factory(this.store, Roo.data);
18416     }
18417     
18418     this.selectedIndex = -1;
18419     if(this.mode == 'local'){
18420         if(config.queryDelay === undefined){
18421             this.queryDelay = 10;
18422         }
18423         if(config.minChars === undefined){
18424             this.minChars = 0;
18425         }
18426     }
18427 };
18428
18429 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18430     /**
18431      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18432      */
18433     /**
18434      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18435      * rendering into an Roo.Editor, defaults to false)
18436      */
18437     /**
18438      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18439      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18440      */
18441     /**
18442      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18443      */
18444     /**
18445      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18446      * the dropdown list (defaults to undefined, with no header element)
18447      */
18448
18449      /**
18450      * @cfg {String/Roo.Template} tpl The template to use to render the output
18451      */
18452      
18453     // private
18454     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18455     /**
18456      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18457      */
18458     listWidth: undefined,
18459     /**
18460      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18461      * mode = 'remote' or 'text' if mode = 'local')
18462      */
18463     displayField: undefined,
18464     /**
18465      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18466      * mode = 'remote' or 'value' if mode = 'local'). 
18467      * Note: use of a valueField requires the user make a selection
18468      * in order for a value to be mapped.
18469      */
18470     valueField: undefined,
18471     
18472     
18473     /**
18474      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18475      * field's data value (defaults to the underlying DOM element's name)
18476      */
18477     hiddenName: undefined,
18478     /**
18479      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18480      */
18481     listClass: '',
18482     /**
18483      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18484      */
18485     selectedClass: 'x-combo-selected',
18486     /**
18487      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18488      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18489      * which displays a downward arrow icon).
18490      */
18491     triggerClass : 'x-form-arrow-trigger',
18492     /**
18493      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18494      */
18495     shadow:'sides',
18496     /**
18497      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18498      * anchor positions (defaults to 'tl-bl')
18499      */
18500     listAlign: 'tl-bl?',
18501     /**
18502      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18503      */
18504     maxHeight: 300,
18505     /**
18506      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18507      * query specified by the allQuery config option (defaults to 'query')
18508      */
18509     triggerAction: 'query',
18510     /**
18511      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18512      * (defaults to 4, does not apply if editable = false)
18513      */
18514     minChars : 4,
18515     /**
18516      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18517      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18518      */
18519     typeAhead: false,
18520     /**
18521      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18522      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18523      */
18524     queryDelay: 500,
18525     /**
18526      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18527      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18528      */
18529     pageSize: 0,
18530     /**
18531      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18532      * when editable = true (defaults to false)
18533      */
18534     selectOnFocus:false,
18535     /**
18536      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18537      */
18538     queryParam: 'query',
18539     /**
18540      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18541      * when mode = 'remote' (defaults to 'Loading...')
18542      */
18543     loadingText: 'Loading...',
18544     /**
18545      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18546      */
18547     resizable: false,
18548     /**
18549      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18550      */
18551     handleHeight : 8,
18552     /**
18553      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18554      * traditional select (defaults to true)
18555      */
18556     editable: true,
18557     /**
18558      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18559      */
18560     allQuery: '',
18561     /**
18562      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18563      */
18564     mode: 'remote',
18565     /**
18566      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18567      * listWidth has a higher value)
18568      */
18569     minListWidth : 70,
18570     /**
18571      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18572      * allow the user to set arbitrary text into the field (defaults to false)
18573      */
18574     forceSelection:false,
18575     /**
18576      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18577      * if typeAhead = true (defaults to 250)
18578      */
18579     typeAheadDelay : 250,
18580     /**
18581      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18582      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18583      */
18584     valueNotFoundText : undefined,
18585     /**
18586      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18587      */
18588     blockFocus : false,
18589     
18590     /**
18591      * @cfg {Boolean} disableClear Disable showing of clear button.
18592      */
18593     disableClear : false,
18594     /**
18595      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18596      */
18597     alwaysQuery : false,
18598     
18599     //private
18600     addicon : false,
18601     editicon: false,
18602     
18603     // element that contains real text value.. (when hidden is used..)
18604      
18605     // private
18606     onRender : function(ct, position)
18607     {
18608         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18609         
18610         if(this.hiddenName){
18611             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18612                     'before', true);
18613             this.hiddenField.value =
18614                 this.hiddenValue !== undefined ? this.hiddenValue :
18615                 this.value !== undefined ? this.value : '';
18616
18617             // prevent input submission
18618             this.el.dom.removeAttribute('name');
18619              
18620              
18621         }
18622         
18623         if(Roo.isGecko){
18624             this.el.dom.setAttribute('autocomplete', 'off');
18625         }
18626
18627         var cls = 'x-combo-list';
18628
18629         this.list = new Roo.Layer({
18630             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18631         });
18632
18633         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18634         this.list.setWidth(lw);
18635         this.list.swallowEvent('mousewheel');
18636         this.assetHeight = 0;
18637
18638         if(this.title){
18639             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18640             this.assetHeight += this.header.getHeight();
18641         }
18642
18643         this.innerList = this.list.createChild({cls:cls+'-inner'});
18644         this.innerList.on('mouseover', this.onViewOver, this);
18645         this.innerList.on('mousemove', this.onViewMove, this);
18646         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18647         
18648         if(this.allowBlank && !this.pageSize && !this.disableClear){
18649             this.footer = this.list.createChild({cls:cls+'-ft'});
18650             this.pageTb = new Roo.Toolbar(this.footer);
18651            
18652         }
18653         if(this.pageSize){
18654             this.footer = this.list.createChild({cls:cls+'-ft'});
18655             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18656                     {pageSize: this.pageSize});
18657             
18658         }
18659         
18660         if (this.pageTb && this.allowBlank && !this.disableClear) {
18661             var _this = this;
18662             this.pageTb.add(new Roo.Toolbar.Fill(), {
18663                 cls: 'x-btn-icon x-btn-clear',
18664                 text: '&#160;',
18665                 handler: function()
18666                 {
18667                     _this.collapse();
18668                     _this.clearValue();
18669                     _this.onSelect(false, -1);
18670                 }
18671             });
18672         }
18673         if (this.footer) {
18674             this.assetHeight += this.footer.getHeight();
18675         }
18676         
18677
18678         if(!this.tpl){
18679             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18680         }
18681
18682         this.view = new Roo.View(this.innerList, this.tpl, {
18683             singleSelect:true,
18684             store: this.store,
18685             selectedClass: this.selectedClass
18686         });
18687
18688         this.view.on('click', this.onViewClick, this);
18689
18690         this.store.on('beforeload', this.onBeforeLoad, this);
18691         this.store.on('load', this.onLoad, this);
18692         this.store.on('loadexception', this.onLoadException, this);
18693
18694         if(this.resizable){
18695             this.resizer = new Roo.Resizable(this.list,  {
18696                pinned:true, handles:'se'
18697             });
18698             this.resizer.on('resize', function(r, w, h){
18699                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18700                 this.listWidth = w;
18701                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18702                 this.restrictHeight();
18703             }, this);
18704             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18705         }
18706         if(!this.editable){
18707             this.editable = true;
18708             this.setEditable(false);
18709         }  
18710         
18711         
18712         if (typeof(this.events.add.listeners) != 'undefined') {
18713             
18714             this.addicon = this.wrap.createChild(
18715                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18716        
18717             this.addicon.on('click', function(e) {
18718                 this.fireEvent('add', this);
18719             }, this);
18720         }
18721         if (typeof(this.events.edit.listeners) != 'undefined') {
18722             
18723             this.editicon = this.wrap.createChild(
18724                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18725             if (this.addicon) {
18726                 this.editicon.setStyle('margin-left', '40px');
18727             }
18728             this.editicon.on('click', function(e) {
18729                 
18730                 // we fire even  if inothing is selected..
18731                 this.fireEvent('edit', this, this.lastData );
18732                 
18733             }, this);
18734         }
18735         
18736         
18737         
18738     },
18739
18740     // private
18741     initEvents : function(){
18742         Roo.form.ComboBox.superclass.initEvents.call(this);
18743
18744         this.keyNav = new Roo.KeyNav(this.el, {
18745             "up" : function(e){
18746                 this.inKeyMode = true;
18747                 this.selectPrev();
18748             },
18749
18750             "down" : function(e){
18751                 if(!this.isExpanded()){
18752                     this.onTriggerClick();
18753                 }else{
18754                     this.inKeyMode = true;
18755                     this.selectNext();
18756                 }
18757             },
18758
18759             "enter" : function(e){
18760                 this.onViewClick();
18761                 //return true;
18762             },
18763
18764             "esc" : function(e){
18765                 this.collapse();
18766             },
18767
18768             "tab" : function(e){
18769                 this.onViewClick(false);
18770                 this.fireEvent("specialkey", this, e);
18771                 return true;
18772             },
18773
18774             scope : this,
18775
18776             doRelay : function(foo, bar, hname){
18777                 if(hname == 'down' || this.scope.isExpanded()){
18778                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18779                 }
18780                 return true;
18781             },
18782
18783             forceKeyDown: true
18784         });
18785         this.queryDelay = Math.max(this.queryDelay || 10,
18786                 this.mode == 'local' ? 10 : 250);
18787         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18788         if(this.typeAhead){
18789             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18790         }
18791         if(this.editable !== false){
18792             this.el.on("keyup", this.onKeyUp, this);
18793         }
18794         if(this.forceSelection){
18795             this.on('blur', this.doForce, this);
18796         }
18797     },
18798
18799     onDestroy : function(){
18800         if(this.view){
18801             this.view.setStore(null);
18802             this.view.el.removeAllListeners();
18803             this.view.el.remove();
18804             this.view.purgeListeners();
18805         }
18806         if(this.list){
18807             this.list.destroy();
18808         }
18809         if(this.store){
18810             this.store.un('beforeload', this.onBeforeLoad, this);
18811             this.store.un('load', this.onLoad, this);
18812             this.store.un('loadexception', this.onLoadException, this);
18813         }
18814         Roo.form.ComboBox.superclass.onDestroy.call(this);
18815     },
18816
18817     // private
18818     fireKey : function(e){
18819         if(e.isNavKeyPress() && !this.list.isVisible()){
18820             this.fireEvent("specialkey", this, e);
18821         }
18822     },
18823
18824     // private
18825     onResize: function(w, h){
18826         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18827         
18828         if(typeof w != 'number'){
18829             // we do not handle it!?!?
18830             return;
18831         }
18832         var tw = this.trigger.getWidth();
18833         tw += this.addicon ? this.addicon.getWidth() : 0;
18834         tw += this.editicon ? this.editicon.getWidth() : 0;
18835         var x = w - tw;
18836         this.el.setWidth( this.adjustWidth('input', x));
18837             
18838         this.trigger.setStyle('left', x+'px');
18839         
18840         if(this.list && this.listWidth === undefined){
18841             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18842             this.list.setWidth(lw);
18843             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18844         }
18845         
18846     
18847         
18848     },
18849
18850     /**
18851      * Allow or prevent the user from directly editing the field text.  If false is passed,
18852      * the user will only be able to select from the items defined in the dropdown list.  This method
18853      * is the runtime equivalent of setting the 'editable' config option at config time.
18854      * @param {Boolean} value True to allow the user to directly edit the field text
18855      */
18856     setEditable : function(value){
18857         if(value == this.editable){
18858             return;
18859         }
18860         this.editable = value;
18861         if(!value){
18862             this.el.dom.setAttribute('readOnly', true);
18863             this.el.on('mousedown', this.onTriggerClick,  this);
18864             this.el.addClass('x-combo-noedit');
18865         }else{
18866             this.el.dom.setAttribute('readOnly', false);
18867             this.el.un('mousedown', this.onTriggerClick,  this);
18868             this.el.removeClass('x-combo-noedit');
18869         }
18870     },
18871
18872     // private
18873     onBeforeLoad : function(){
18874         if(!this.hasFocus){
18875             return;
18876         }
18877         this.innerList.update(this.loadingText ?
18878                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18879         this.restrictHeight();
18880         this.selectedIndex = -1;
18881     },
18882
18883     // private
18884     onLoad : function(){
18885         if(!this.hasFocus){
18886             return;
18887         }
18888         if(this.store.getCount() > 0){
18889             this.expand();
18890             this.restrictHeight();
18891             if(this.lastQuery == this.allQuery){
18892                 if(this.editable){
18893                     this.el.dom.select();
18894                 }
18895                 if(!this.selectByValue(this.value, true)){
18896                     this.select(0, true);
18897                 }
18898             }else{
18899                 this.selectNext();
18900                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18901                     this.taTask.delay(this.typeAheadDelay);
18902                 }
18903             }
18904         }else{
18905             this.onEmptyResults();
18906         }
18907         //this.el.focus();
18908     },
18909     // private
18910     onLoadException : function()
18911     {
18912         this.collapse();
18913         Roo.log(this.store.reader.jsonData);
18914         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18915             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18916         }
18917         
18918         
18919     },
18920     // private
18921     onTypeAhead : function(){
18922         if(this.store.getCount() > 0){
18923             var r = this.store.getAt(0);
18924             var newValue = r.data[this.displayField];
18925             var len = newValue.length;
18926             var selStart = this.getRawValue().length;
18927             if(selStart != len){
18928                 this.setRawValue(newValue);
18929                 this.selectText(selStart, newValue.length);
18930             }
18931         }
18932     },
18933
18934     // private
18935     onSelect : function(record, index){
18936         if(this.fireEvent('beforeselect', this, record, index) !== false){
18937             this.setFromData(index > -1 ? record.data : false);
18938             this.collapse();
18939             this.fireEvent('select', this, record, index);
18940         }
18941     },
18942
18943     /**
18944      * Returns the currently selected field value or empty string if no value is set.
18945      * @return {String} value The selected value
18946      */
18947     getValue : function(){
18948         if(this.valueField){
18949             return typeof this.value != 'undefined' ? this.value : '';
18950         }
18951         return Roo.form.ComboBox.superclass.getValue.call(this);
18952     },
18953
18954     /**
18955      * Clears any text/value currently set in the field
18956      */
18957     clearValue : function(){
18958         if(this.hiddenField){
18959             this.hiddenField.value = '';
18960         }
18961         this.value = '';
18962         this.setRawValue('');
18963         this.lastSelectionText = '';
18964         
18965     },
18966
18967     /**
18968      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18969      * will be displayed in the field.  If the value does not match the data value of an existing item,
18970      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18971      * Otherwise the field will be blank (although the value will still be set).
18972      * @param {String} value The value to match
18973      */
18974     setValue : function(v){
18975         var text = v;
18976         if(this.valueField){
18977             var r = this.findRecord(this.valueField, v);
18978             if(r){
18979                 text = r.data[this.displayField];
18980             }else if(this.valueNotFoundText !== undefined){
18981                 text = this.valueNotFoundText;
18982             }
18983         }
18984         this.lastSelectionText = text;
18985         if(this.hiddenField){
18986             this.hiddenField.value = v;
18987         }
18988         Roo.form.ComboBox.superclass.setValue.call(this, text);
18989         this.value = v;
18990     },
18991     /**
18992      * @property {Object} the last set data for the element
18993      */
18994     
18995     lastData : false,
18996     /**
18997      * Sets the value of the field based on a object which is related to the record format for the store.
18998      * @param {Object} value the value to set as. or false on reset?
18999      */
19000     setFromData : function(o){
19001         var dv = ''; // display value
19002         var vv = ''; // value value..
19003         this.lastData = o;
19004         if (this.displayField) {
19005             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19006         } else {
19007             // this is an error condition!!!
19008             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19009         }
19010         
19011         if(this.valueField){
19012             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19013         }
19014         if(this.hiddenField){
19015             this.hiddenField.value = vv;
19016             
19017             this.lastSelectionText = dv;
19018             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19019             this.value = vv;
19020             return;
19021         }
19022         // no hidden field.. - we store the value in 'value', but still display
19023         // display field!!!!
19024         this.lastSelectionText = dv;
19025         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19026         this.value = vv;
19027         
19028         
19029     },
19030     // private
19031     reset : function(){
19032         // overridden so that last data is reset..
19033         this.setValue(this.resetValue);
19034         this.originalValue = this.getValue();
19035         this.clearInvalid();
19036         this.lastData = false;
19037         if (this.view) {
19038             this.view.clearSelections();
19039         }
19040     },
19041     // private
19042     findRecord : function(prop, value){
19043         var record;
19044         if(this.store.getCount() > 0){
19045             this.store.each(function(r){
19046                 if(r.data[prop] == value){
19047                     record = r;
19048                     return false;
19049                 }
19050                 return true;
19051             });
19052         }
19053         return record;
19054     },
19055     
19056     getName: function()
19057     {
19058         // returns hidden if it's set..
19059         if (!this.rendered) {return ''};
19060         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19061         
19062     },
19063     // private
19064     onViewMove : function(e, t){
19065         this.inKeyMode = false;
19066     },
19067
19068     // private
19069     onViewOver : function(e, t){
19070         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19071             return;
19072         }
19073         var item = this.view.findItemFromChild(t);
19074         if(item){
19075             var index = this.view.indexOf(item);
19076             this.select(index, false);
19077         }
19078     },
19079
19080     // private
19081     onViewClick : function(doFocus)
19082     {
19083         var index = this.view.getSelectedIndexes()[0];
19084         var r = this.store.getAt(index);
19085         if(r){
19086             this.onSelect(r, index);
19087         }
19088         if(doFocus !== false && !this.blockFocus){
19089             this.el.focus();
19090         }
19091     },
19092
19093     // private
19094     restrictHeight : function(){
19095         this.innerList.dom.style.height = '';
19096         var inner = this.innerList.dom;
19097         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19098         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19099         this.list.beginUpdate();
19100         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19101         this.list.alignTo(this.el, this.listAlign);
19102         this.list.endUpdate();
19103     },
19104
19105     // private
19106     onEmptyResults : function(){
19107         this.collapse();
19108     },
19109
19110     /**
19111      * Returns true if the dropdown list is expanded, else false.
19112      */
19113     isExpanded : function(){
19114         return this.list.isVisible();
19115     },
19116
19117     /**
19118      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19119      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19120      * @param {String} value The data value of the item to select
19121      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19122      * selected item if it is not currently in view (defaults to true)
19123      * @return {Boolean} True if the value matched an item in the list, else false
19124      */
19125     selectByValue : function(v, scrollIntoView){
19126         if(v !== undefined && v !== null){
19127             var r = this.findRecord(this.valueField || this.displayField, v);
19128             if(r){
19129                 this.select(this.store.indexOf(r), scrollIntoView);
19130                 return true;
19131             }
19132         }
19133         return false;
19134     },
19135
19136     /**
19137      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19138      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19139      * @param {Number} index The zero-based index of the list item to select
19140      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19141      * selected item if it is not currently in view (defaults to true)
19142      */
19143     select : function(index, scrollIntoView){
19144         this.selectedIndex = index;
19145         this.view.select(index);
19146         if(scrollIntoView !== false){
19147             var el = this.view.getNode(index);
19148             if(el){
19149                 this.innerList.scrollChildIntoView(el, false);
19150             }
19151         }
19152     },
19153
19154     // private
19155     selectNext : function(){
19156         var ct = this.store.getCount();
19157         if(ct > 0){
19158             if(this.selectedIndex == -1){
19159                 this.select(0);
19160             }else if(this.selectedIndex < ct-1){
19161                 this.select(this.selectedIndex+1);
19162             }
19163         }
19164     },
19165
19166     // private
19167     selectPrev : function(){
19168         var ct = this.store.getCount();
19169         if(ct > 0){
19170             if(this.selectedIndex == -1){
19171                 this.select(0);
19172             }else if(this.selectedIndex != 0){
19173                 this.select(this.selectedIndex-1);
19174             }
19175         }
19176     },
19177
19178     // private
19179     onKeyUp : function(e){
19180         if(this.editable !== false && !e.isSpecialKey()){
19181             this.lastKey = e.getKey();
19182             this.dqTask.delay(this.queryDelay);
19183         }
19184     },
19185
19186     // private
19187     validateBlur : function(){
19188         return !this.list || !this.list.isVisible();   
19189     },
19190
19191     // private
19192     initQuery : function(){
19193         this.doQuery(this.getRawValue());
19194     },
19195
19196     // private
19197     doForce : function(){
19198         if(this.el.dom.value.length > 0){
19199             this.el.dom.value =
19200                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19201              
19202         }
19203     },
19204
19205     /**
19206      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19207      * query allowing the query action to be canceled if needed.
19208      * @param {String} query The SQL query to execute
19209      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19210      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19211      * saved in the current store (defaults to false)
19212      */
19213     doQuery : function(q, forceAll){
19214         if(q === undefined || q === null){
19215             q = '';
19216         }
19217         var qe = {
19218             query: q,
19219             forceAll: forceAll,
19220             combo: this,
19221             cancel:false
19222         };
19223         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19224             return false;
19225         }
19226         q = qe.query;
19227         forceAll = qe.forceAll;
19228         if(forceAll === true || (q.length >= this.minChars)){
19229             if(this.lastQuery != q || this.alwaysQuery){
19230                 this.lastQuery = q;
19231                 if(this.mode == 'local'){
19232                     this.selectedIndex = -1;
19233                     if(forceAll){
19234                         this.store.clearFilter();
19235                     }else{
19236                         this.store.filter(this.displayField, q);
19237                     }
19238                     this.onLoad();
19239                 }else{
19240                     this.store.baseParams[this.queryParam] = q;
19241                     this.store.load({
19242                         params: this.getParams(q)
19243                     });
19244                     this.expand();
19245                 }
19246             }else{
19247                 this.selectedIndex = -1;
19248                 this.onLoad();   
19249             }
19250         }
19251     },
19252
19253     // private
19254     getParams : function(q){
19255         var p = {};
19256         //p[this.queryParam] = q;
19257         if(this.pageSize){
19258             p.start = 0;
19259             p.limit = this.pageSize;
19260         }
19261         return p;
19262     },
19263
19264     /**
19265      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19266      */
19267     collapse : function(){
19268         if(!this.isExpanded()){
19269             return;
19270         }
19271         this.list.hide();
19272         Roo.get(document).un('mousedown', this.collapseIf, this);
19273         Roo.get(document).un('mousewheel', this.collapseIf, this);
19274         if (!this.editable) {
19275             Roo.get(document).un('keydown', this.listKeyPress, this);
19276         }
19277         this.fireEvent('collapse', this);
19278     },
19279
19280     // private
19281     collapseIf : function(e){
19282         if(!e.within(this.wrap) && !e.within(this.list)){
19283             this.collapse();
19284         }
19285     },
19286
19287     /**
19288      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19289      */
19290     expand : function(){
19291         if(this.isExpanded() || !this.hasFocus){
19292             return;
19293         }
19294         this.list.alignTo(this.el, this.listAlign);
19295         this.list.show();
19296         Roo.get(document).on('mousedown', this.collapseIf, this);
19297         Roo.get(document).on('mousewheel', this.collapseIf, this);
19298         if (!this.editable) {
19299             Roo.get(document).on('keydown', this.listKeyPress, this);
19300         }
19301         
19302         this.fireEvent('expand', this);
19303     },
19304
19305     // private
19306     // Implements the default empty TriggerField.onTriggerClick function
19307     onTriggerClick : function(){
19308         if(this.disabled){
19309             return;
19310         }
19311         if(this.isExpanded()){
19312             this.collapse();
19313             if (!this.blockFocus) {
19314                 this.el.focus();
19315             }
19316             
19317         }else {
19318             this.hasFocus = true;
19319             if(this.triggerAction == 'all') {
19320                 this.doQuery(this.allQuery, true);
19321             } else {
19322                 this.doQuery(this.getRawValue());
19323             }
19324             if (!this.blockFocus) {
19325                 this.el.focus();
19326             }
19327         }
19328     },
19329     listKeyPress : function(e)
19330     {
19331         //Roo.log('listkeypress');
19332         // scroll to first matching element based on key pres..
19333         if (e.isSpecialKey()) {
19334             return false;
19335         }
19336         var k = String.fromCharCode(e.getKey()).toUpperCase();
19337         //Roo.log(k);
19338         var match  = false;
19339         var csel = this.view.getSelectedNodes();
19340         var cselitem = false;
19341         if (csel.length) {
19342             var ix = this.view.indexOf(csel[0]);
19343             cselitem  = this.store.getAt(ix);
19344             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19345                 cselitem = false;
19346             }
19347             
19348         }
19349         
19350         this.store.each(function(v) { 
19351             if (cselitem) {
19352                 // start at existing selection.
19353                 if (cselitem.id == v.id) {
19354                     cselitem = false;
19355                 }
19356                 return;
19357             }
19358                 
19359             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19360                 match = this.store.indexOf(v);
19361                 return false;
19362             }
19363         }, this);
19364         
19365         if (match === false) {
19366             return true; // no more action?
19367         }
19368         // scroll to?
19369         this.view.select(match);
19370         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19371         sn.scrollIntoView(sn.dom.parentNode, false);
19372     } 
19373
19374     /** 
19375     * @cfg {Boolean} grow 
19376     * @hide 
19377     */
19378     /** 
19379     * @cfg {Number} growMin 
19380     * @hide 
19381     */
19382     /** 
19383     * @cfg {Number} growMax 
19384     * @hide 
19385     */
19386     /**
19387      * @hide
19388      * @method autoSize
19389      */
19390 });/*
19391  * Copyright(c) 2010-2012, Roo J Solutions Limited
19392  *
19393  * Licence LGPL
19394  *
19395  */
19396
19397 /**
19398  * @class Roo.form.ComboBoxArray
19399  * @extends Roo.form.TextField
19400  * A facebook style adder... for lists of email / people / countries  etc...
19401  * pick multiple items from a combo box, and shows each one.
19402  *
19403  *  Fred [x]  Brian [x]  [Pick another |v]
19404  *
19405  *
19406  *  For this to work: it needs various extra information
19407  *    - normal combo problay has
19408  *      name, hiddenName
19409  *    + displayField, valueField
19410  *
19411  *    For our purpose...
19412  *
19413  *
19414  *   If we change from 'extends' to wrapping...
19415  *   
19416  *  
19417  *
19418  
19419  
19420  * @constructor
19421  * Create a new ComboBoxArray.
19422  * @param {Object} config Configuration options
19423  */
19424  
19425
19426 Roo.form.ComboBoxArray = function(config)
19427 {
19428     this.addEvents({
19429         /**
19430          * @event beforeremove
19431          * Fires before remove the value from the list
19432              * @param {Roo.form.ComboBoxArray} _self This combo box array
19433              * @param {Roo.form.ComboBoxArray.Item} item removed item
19434              */
19435         'beforeremove' : true,
19436         /**
19437          * @event remove
19438          * Fires when remove the value from the list
19439              * @param {Roo.form.ComboBoxArray} _self This combo box array
19440              * @param {Roo.form.ComboBoxArray.Item} item removed item
19441              */
19442         'remove' : true
19443         
19444         
19445     });
19446     
19447     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19448     
19449     this.items = new Roo.util.MixedCollection(false);
19450     
19451     // construct the child combo...
19452     
19453     
19454     
19455     
19456    
19457     
19458 }
19459
19460  
19461 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19462
19463     /**
19464      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19465      */
19466     
19467     lastData : false,
19468     
19469     // behavies liek a hiddne field
19470     inputType:      'hidden',
19471     /**
19472      * @cfg {Number} width The width of the box that displays the selected element
19473      */ 
19474     width:          300,
19475
19476     
19477     
19478     /**
19479      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19480      */
19481     name : false,
19482     /**
19483      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19484      */
19485     hiddenName : false,
19486       /**
19487      * @cfg {String} seperator    The value seperator normally ',' 
19488      */
19489     seperator : ',',
19490     
19491     // private the array of items that are displayed..
19492     items  : false,
19493     // private - the hidden field el.
19494     hiddenEl : false,
19495     // private - the filed el..
19496     el : false,
19497     
19498     //validateValue : function() { return true; }, // all values are ok!
19499     //onAddClick: function() { },
19500     
19501     onRender : function(ct, position) 
19502     {
19503         
19504         // create the standard hidden element
19505         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19506         
19507         
19508         // give fake names to child combo;
19509         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19510         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19511         
19512         this.combo = Roo.factory(this.combo, Roo.form);
19513         this.combo.onRender(ct, position);
19514         if (typeof(this.combo.width) != 'undefined') {
19515             this.combo.onResize(this.combo.width,0);
19516         }
19517         
19518         this.combo.initEvents();
19519         
19520         // assigned so form know we need to do this..
19521         this.store          = this.combo.store;
19522         this.valueField     = this.combo.valueField;
19523         this.displayField   = this.combo.displayField ;
19524         
19525         
19526         this.combo.wrap.addClass('x-cbarray-grp');
19527         
19528         var cbwrap = this.combo.wrap.createChild(
19529             {tag: 'div', cls: 'x-cbarray-cb'},
19530             this.combo.el.dom
19531         );
19532         
19533              
19534         this.hiddenEl = this.combo.wrap.createChild({
19535             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19536         });
19537         this.el = this.combo.wrap.createChild({
19538             tag: 'input',  type:'hidden' , name: this.name, value : ''
19539         });
19540          //   this.el.dom.removeAttribute("name");
19541         
19542         
19543         this.outerWrap = this.combo.wrap;
19544         this.wrap = cbwrap;
19545         
19546         this.outerWrap.setWidth(this.width);
19547         this.outerWrap.dom.removeChild(this.el.dom);
19548         
19549         this.wrap.dom.appendChild(this.el.dom);
19550         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19551         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19552         
19553         this.combo.trigger.setStyle('position','relative');
19554         this.combo.trigger.setStyle('left', '0px');
19555         this.combo.trigger.setStyle('top', '2px');
19556         
19557         this.combo.el.setStyle('vertical-align', 'text-bottom');
19558         
19559         //this.trigger.setStyle('vertical-align', 'top');
19560         
19561         // this should use the code from combo really... on('add' ....)
19562         if (this.adder) {
19563             
19564         
19565             this.adder = this.outerWrap.createChild(
19566                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19567             var _t = this;
19568             this.adder.on('click', function(e) {
19569                 _t.fireEvent('adderclick', this, e);
19570             }, _t);
19571         }
19572         //var _t = this;
19573         //this.adder.on('click', this.onAddClick, _t);
19574         
19575         
19576         this.combo.on('select', function(cb, rec, ix) {
19577             this.addItem(rec.data);
19578             
19579             cb.setValue('');
19580             cb.el.dom.value = '';
19581             //cb.lastData = rec.data;
19582             // add to list
19583             
19584         }, this);
19585         
19586         
19587     },
19588     
19589     
19590     getName: function()
19591     {
19592         // returns hidden if it's set..
19593         if (!this.rendered) {return ''};
19594         return  this.hiddenName ? this.hiddenName : this.name;
19595         
19596     },
19597     
19598     
19599     onResize: function(w, h){
19600         
19601         return;
19602         // not sure if this is needed..
19603         //this.combo.onResize(w,h);
19604         
19605         if(typeof w != 'number'){
19606             // we do not handle it!?!?
19607             return;
19608         }
19609         var tw = this.combo.trigger.getWidth();
19610         tw += this.addicon ? this.addicon.getWidth() : 0;
19611         tw += this.editicon ? this.editicon.getWidth() : 0;
19612         var x = w - tw;
19613         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19614             
19615         this.combo.trigger.setStyle('left', '0px');
19616         
19617         if(this.list && this.listWidth === undefined){
19618             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19619             this.list.setWidth(lw);
19620             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19621         }
19622         
19623     
19624         
19625     },
19626     
19627     addItem: function(rec)
19628     {
19629         var valueField = this.combo.valueField;
19630         var displayField = this.combo.displayField;
19631         
19632         if (this.items.indexOfKey(rec[valueField]) > -1) {
19633             //console.log("GOT " + rec.data.id);
19634             return;
19635         }
19636         
19637         var x = new Roo.form.ComboBoxArray.Item({
19638             //id : rec[this.idField],
19639             data : rec,
19640             displayField : displayField ,
19641             tipField : displayField ,
19642             cb : this
19643         });
19644         // use the 
19645         this.items.add(rec[valueField],x);
19646         // add it before the element..
19647         this.updateHiddenEl();
19648         x.render(this.outerWrap, this.wrap.dom);
19649         // add the image handler..
19650     },
19651     
19652     updateHiddenEl : function()
19653     {
19654         this.validate();
19655         if (!this.hiddenEl) {
19656             return;
19657         }
19658         var ar = [];
19659         var idField = this.combo.valueField;
19660         
19661         this.items.each(function(f) {
19662             ar.push(f.data[idField]);
19663         });
19664         this.hiddenEl.dom.value = ar.join(this.seperator);
19665         this.validate();
19666     },
19667     
19668     reset : function()
19669     {
19670         this.items.clear();
19671         
19672         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19673            el.remove();
19674         });
19675         
19676         this.el.dom.value = '';
19677         if (this.hiddenEl) {
19678             this.hiddenEl.dom.value = '';
19679         }
19680         
19681     },
19682     getValue: function()
19683     {
19684         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19685     },
19686     setValue: function(v) // not a valid action - must use addItems..
19687     {
19688         
19689         this.reset();
19690          
19691         if (this.store.isLocal && (typeof(v) == 'string')) {
19692             // then we can use the store to find the values..
19693             // comma seperated at present.. this needs to allow JSON based encoding..
19694             this.hiddenEl.value  = v;
19695             var v_ar = [];
19696             Roo.each(v.split(this.seperator), function(k) {
19697                 Roo.log("CHECK " + this.valueField + ',' + k);
19698                 var li = this.store.query(this.valueField, k);
19699                 if (!li.length) {
19700                     return;
19701                 }
19702                 var add = {};
19703                 add[this.valueField] = k;
19704                 add[this.displayField] = li.item(0).data[this.displayField];
19705                 
19706                 this.addItem(add);
19707             }, this) 
19708              
19709         }
19710         if (typeof(v) == 'object' ) {
19711             // then let's assume it's an array of objects..
19712             Roo.each(v, function(l) {
19713                 var add = l;
19714                 if (typeof(l) == 'string') {
19715                     add = {};
19716                     add[this.valueField] = l;
19717                     add[this.displayField] = l
19718                 }
19719                 this.addItem(add);
19720             }, this);
19721              
19722         }
19723         
19724         
19725     },
19726     setFromData: function(v)
19727     {
19728         // this recieves an object, if setValues is called.
19729         this.reset();
19730         this.el.dom.value = v[this.displayField];
19731         this.hiddenEl.dom.value = v[this.valueField];
19732         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19733             return;
19734         }
19735         var kv = v[this.valueField];
19736         var dv = v[this.displayField];
19737         kv = typeof(kv) != 'string' ? '' : kv;
19738         dv = typeof(dv) != 'string' ? '' : dv;
19739         
19740         
19741         var keys = kv.split(this.seperator);
19742         var display = dv.split(this.seperator);
19743         for (var i = 0 ; i < keys.length; i++) {
19744             add = {};
19745             add[this.valueField] = keys[i];
19746             add[this.displayField] = display[i];
19747             this.addItem(add);
19748         }
19749       
19750         
19751     },
19752     
19753     /**
19754      * Validates the combox array value
19755      * @return {Boolean} True if the value is valid, else false
19756      */
19757     validate : function(){
19758         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19759             this.clearInvalid();
19760             return true;
19761         }
19762         return false;
19763     },
19764     
19765     validateValue : function(value){
19766         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19767         
19768     },
19769     
19770     /*@
19771      * overide
19772      * 
19773      */
19774     isDirty : function() {
19775         if(this.disabled) {
19776             return false;
19777         }
19778         
19779         try {
19780             var d = Roo.decode(String(this.originalValue));
19781         } catch (e) {
19782             return String(this.getValue()) !== String(this.originalValue);
19783         }
19784         
19785         var originalValue = [];
19786         
19787         for (var i = 0; i < d.length; i++){
19788             originalValue.push(d[i][this.valueField]);
19789         }
19790         
19791         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19792         
19793     }
19794     
19795 });
19796
19797
19798
19799 /**
19800  * @class Roo.form.ComboBoxArray.Item
19801  * @extends Roo.BoxComponent
19802  * A selected item in the list
19803  *  Fred [x]  Brian [x]  [Pick another |v]
19804  * 
19805  * @constructor
19806  * Create a new item.
19807  * @param {Object} config Configuration options
19808  */
19809  
19810 Roo.form.ComboBoxArray.Item = function(config) {
19811     config.id = Roo.id();
19812     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19813 }
19814
19815 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19816     data : {},
19817     cb: false,
19818     displayField : false,
19819     tipField : false,
19820     
19821     
19822     defaultAutoCreate : {
19823         tag: 'div',
19824         cls: 'x-cbarray-item',
19825         cn : [ 
19826             { tag: 'div' },
19827             {
19828                 tag: 'img',
19829                 width:16,
19830                 height : 16,
19831                 src : Roo.BLANK_IMAGE_URL ,
19832                 align: 'center'
19833             }
19834         ]
19835         
19836     },
19837     
19838  
19839     onRender : function(ct, position)
19840     {
19841         Roo.form.Field.superclass.onRender.call(this, ct, position);
19842         
19843         if(!this.el){
19844             var cfg = this.getAutoCreate();
19845             this.el = ct.createChild(cfg, position);
19846         }
19847         
19848         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19849         
19850         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19851             this.cb.renderer(this.data) :
19852             String.format('{0}',this.data[this.displayField]);
19853         
19854             
19855         this.el.child('div').dom.setAttribute('qtip',
19856                         String.format('{0}',this.data[this.tipField])
19857         );
19858         
19859         this.el.child('img').on('click', this.remove, this);
19860         
19861     },
19862    
19863     remove : function()
19864     {
19865         if(this.cb.disabled){
19866             return;
19867         }
19868         
19869         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19870             this.cb.items.remove(this);
19871             this.el.child('img').un('click', this.remove, this);
19872             this.el.remove();
19873             this.cb.updateHiddenEl();
19874
19875             this.cb.fireEvent('remove', this.cb, this);
19876         }
19877         
19878     }
19879 });/*
19880  * RooJS Library 1.1.1
19881  * Copyright(c) 2008-2011  Alan Knowles
19882  *
19883  * License - LGPL
19884  */
19885  
19886
19887 /**
19888  * @class Roo.form.ComboNested
19889  * @extends Roo.form.ComboBox
19890  * A combobox for that allows selection of nested items in a list,
19891  * eg.
19892  *
19893  *  Book
19894  *    -> red
19895  *    -> green
19896  *  Table
19897  *    -> square
19898  *      ->red
19899  *      ->green
19900  *    -> rectangle
19901  *      ->green
19902  *      
19903  * 
19904  * @constructor
19905  * Create a new ComboNested
19906  * @param {Object} config Configuration options
19907  */
19908 Roo.form.ComboNested = function(config){
19909     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19910     // should verify some data...
19911     // like
19912     // hiddenName = required..
19913     // displayField = required
19914     // valudField == required
19915     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19916     var _t = this;
19917     Roo.each(req, function(e) {
19918         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19919             throw "Roo.form.ComboNested : missing value for: " + e;
19920         }
19921     });
19922      
19923     
19924 };
19925
19926 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19927    
19928     /*
19929      * @config {Number} max Number of columns to show
19930      */
19931     
19932     maxColumns : 3,
19933    
19934     list : null, // the outermost div..
19935     innerLists : null, // the
19936     views : null,
19937     stores : null,
19938     // private
19939     loadingChildren : false,
19940     
19941     onRender : function(ct, position)
19942     {
19943         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19944         
19945         if(this.hiddenName){
19946             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19947                     'before', true);
19948             this.hiddenField.value =
19949                 this.hiddenValue !== undefined ? this.hiddenValue :
19950                 this.value !== undefined ? this.value : '';
19951
19952             // prevent input submission
19953             this.el.dom.removeAttribute('name');
19954              
19955              
19956         }
19957         
19958         if(Roo.isGecko){
19959             this.el.dom.setAttribute('autocomplete', 'off');
19960         }
19961
19962         var cls = 'x-combo-list';
19963
19964         this.list = new Roo.Layer({
19965             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19966         });
19967
19968         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19969         this.list.setWidth(lw);
19970         this.list.swallowEvent('mousewheel');
19971         this.assetHeight = 0;
19972
19973         if(this.title){
19974             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19975             this.assetHeight += this.header.getHeight();
19976         }
19977         this.innerLists = [];
19978         this.views = [];
19979         this.stores = [];
19980         for (var i =0 ; i < this.maxColumns; i++) {
19981             this.onRenderList( cls, i);
19982         }
19983         
19984         // always needs footer, as we are going to have an 'OK' button.
19985         this.footer = this.list.createChild({cls:cls+'-ft'});
19986         this.pageTb = new Roo.Toolbar(this.footer);  
19987         var _this = this;
19988         this.pageTb.add(  {
19989             
19990             text: 'Done',
19991             handler: function()
19992             {
19993                 _this.collapse();
19994             }
19995         });
19996         
19997         if ( this.allowBlank && !this.disableClear) {
19998             
19999             this.pageTb.add(new Roo.Toolbar.Fill(), {
20000                 cls: 'x-btn-icon x-btn-clear',
20001                 text: '&#160;',
20002                 handler: function()
20003                 {
20004                     _this.collapse();
20005                     _this.clearValue();
20006                     _this.onSelect(false, -1);
20007                 }
20008             });
20009         }
20010         if (this.footer) {
20011             this.assetHeight += this.footer.getHeight();
20012         }
20013         
20014     },
20015     onRenderList : function (  cls, i)
20016     {
20017         
20018         var lw = Math.floor(
20019                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20020         );
20021         
20022         this.list.setWidth(lw); // default to '1'
20023
20024         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20025         //il.on('mouseover', this.onViewOver, this, { list:  i });
20026         //il.on('mousemove', this.onViewMove, this, { list:  i });
20027         il.setWidth(lw);
20028         il.setStyle({ 'overflow-x' : 'hidden'});
20029
20030         if(!this.tpl){
20031             this.tpl = new Roo.Template({
20032                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20033                 isEmpty: function (value, allValues) {
20034                     //Roo.log(value);
20035                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20036                     return dl ? 'has-children' : 'no-children'
20037                 }
20038             });
20039         }
20040         
20041         var store  = this.store;
20042         if (i > 0) {
20043             store  = new Roo.data.SimpleStore({
20044                 //fields : this.store.reader.meta.fields,
20045                 reader : this.store.reader,
20046                 data : [ ]
20047             });
20048         }
20049         this.stores[i]  = store;
20050                   
20051         var view = this.views[i] = new Roo.View(
20052             il,
20053             this.tpl,
20054             {
20055                 singleSelect:true,
20056                 store: store,
20057                 selectedClass: this.selectedClass
20058             }
20059         );
20060         view.getEl().setWidth(lw);
20061         view.getEl().setStyle({
20062             position: i < 1 ? 'relative' : 'absolute',
20063             top: 0,
20064             left: (i * lw ) + 'px',
20065             display : i > 0 ? 'none' : 'block'
20066         });
20067         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20068         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20069         //view.on('click', this.onViewClick, this, { list : i });
20070
20071         store.on('beforeload', this.onBeforeLoad, this);
20072         store.on('load',  this.onLoad, this, { list  : i});
20073         store.on('loadexception', this.onLoadException, this);
20074
20075         // hide the other vies..
20076         
20077         
20078         
20079     },
20080       
20081     restrictHeight : function()
20082     {
20083         var mh = 0;
20084         Roo.each(this.innerLists, function(il,i) {
20085             var el = this.views[i].getEl();
20086             el.dom.style.height = '';
20087             var inner = el.dom;
20088             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20089             // only adjust heights on other ones..
20090             mh = Math.max(h, mh);
20091             if (i < 1) {
20092                 
20093                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20094                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20095                
20096             }
20097             
20098             
20099         }, this);
20100         
20101         this.list.beginUpdate();
20102         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20103         this.list.alignTo(this.el, this.listAlign);
20104         this.list.endUpdate();
20105         
20106     },
20107      
20108     
20109     // -- store handlers..
20110     // private
20111     onBeforeLoad : function()
20112     {
20113         if(!this.hasFocus){
20114             return;
20115         }
20116         this.innerLists[0].update(this.loadingText ?
20117                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20118         this.restrictHeight();
20119         this.selectedIndex = -1;
20120     },
20121     // private
20122     onLoad : function(a,b,c,d)
20123     {
20124         if (!this.loadingChildren) {
20125             // then we are loading the top level. - hide the children
20126             for (var i = 1;i < this.views.length; i++) {
20127                 this.views[i].getEl().setStyle({ display : 'none' });
20128             }
20129             var lw = Math.floor(
20130                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20131             );
20132         
20133              this.list.setWidth(lw); // default to '1'
20134
20135             
20136         }
20137         if(!this.hasFocus){
20138             return;
20139         }
20140         
20141         if(this.store.getCount() > 0) {
20142             this.expand();
20143             this.restrictHeight();   
20144         } else {
20145             this.onEmptyResults();
20146         }
20147         
20148         if (!this.loadingChildren) {
20149             this.selectActive();
20150         }
20151         /*
20152         this.stores[1].loadData([]);
20153         this.stores[2].loadData([]);
20154         this.views
20155         */    
20156     
20157         //this.el.focus();
20158     },
20159     
20160     
20161     // private
20162     onLoadException : function()
20163     {
20164         this.collapse();
20165         Roo.log(this.store.reader.jsonData);
20166         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20167             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20168         }
20169         
20170         
20171     },
20172     // no cleaning of leading spaces on blur here.
20173     cleanLeadingSpace : function(e) { },
20174     
20175
20176     onSelectChange : function (view, sels, opts )
20177     {
20178         var ix = view.getSelectedIndexes();
20179          
20180         if (opts.list > this.maxColumns - 2) {
20181             if (view.store.getCount()<  1) {
20182                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20183
20184             } else  {
20185                 if (ix.length) {
20186                     // used to clear ?? but if we are loading unselected 
20187                     this.setFromData(view.store.getAt(ix[0]).data);
20188                 }
20189                 
20190             }
20191             
20192             return;
20193         }
20194         
20195         if (!ix.length) {
20196             // this get's fired when trigger opens..
20197            // this.setFromData({});
20198             var str = this.stores[opts.list+1];
20199             str.data.clear(); // removeall wihtout the fire events..
20200             return;
20201         }
20202         
20203         var rec = view.store.getAt(ix[0]);
20204          
20205         this.setFromData(rec.data);
20206         this.fireEvent('select', this, rec, ix[0]);
20207         
20208         var lw = Math.floor(
20209              (
20210                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20211              ) / this.maxColumns
20212         );
20213         this.loadingChildren = true;
20214         this.stores[opts.list+1].loadDataFromChildren( rec );
20215         this.loadingChildren = false;
20216         var dl = this.stores[opts.list+1]. getTotalCount();
20217         
20218         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20219         
20220         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20221         for (var i = opts.list+2; i < this.views.length;i++) {
20222             this.views[i].getEl().setStyle({ display : 'none' });
20223         }
20224         
20225         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20226         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20227         
20228         if (this.isLoading) {
20229            // this.selectActive(opts.list);
20230         }
20231          
20232     },
20233     
20234     
20235     
20236     
20237     onDoubleClick : function()
20238     {
20239         this.collapse(); //??
20240     },
20241     
20242      
20243     
20244     
20245     
20246     // private
20247     recordToStack : function(store, prop, value, stack)
20248     {
20249         var cstore = new Roo.data.SimpleStore({
20250             //fields : this.store.reader.meta.fields, // we need array reader.. for
20251             reader : this.store.reader,
20252             data : [ ]
20253         });
20254         var _this = this;
20255         var record  = false;
20256         var srec = false;
20257         if(store.getCount() < 1){
20258             return false;
20259         }
20260         store.each(function(r){
20261             if(r.data[prop] == value){
20262                 record = r;
20263             srec = r;
20264                 return false;
20265             }
20266             if (r.data.cn && r.data.cn.length) {
20267                 cstore.loadDataFromChildren( r);
20268                 var cret = _this.recordToStack(cstore, prop, value, stack);
20269                 if (cret !== false) {
20270                     record = cret;
20271                     srec = r;
20272                     return false;
20273                 }
20274             }
20275              
20276             return true;
20277         });
20278         if (record == false) {
20279             return false
20280         }
20281         stack.unshift(srec);
20282         return record;
20283     },
20284     
20285     /*
20286      * find the stack of stores that match our value.
20287      *
20288      * 
20289      */
20290     
20291     selectActive : function ()
20292     {
20293         // if store is not loaded, then we will need to wait for that to happen first.
20294         var stack = [];
20295         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20296         for (var i = 0; i < stack.length; i++ ) {
20297             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20298         }
20299         
20300     }
20301         
20302          
20303     
20304     
20305     
20306     
20307 });/*
20308  * Based on:
20309  * Ext JS Library 1.1.1
20310  * Copyright(c) 2006-2007, Ext JS, LLC.
20311  *
20312  * Originally Released Under LGPL - original licence link has changed is not relivant.
20313  *
20314  * Fork - LGPL
20315  * <script type="text/javascript">
20316  */
20317 /**
20318  * @class Roo.form.Checkbox
20319  * @extends Roo.form.Field
20320  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20321  * @constructor
20322  * Creates a new Checkbox
20323  * @param {Object} config Configuration options
20324  */
20325 Roo.form.Checkbox = function(config){
20326     Roo.form.Checkbox.superclass.constructor.call(this, config);
20327     this.addEvents({
20328         /**
20329          * @event check
20330          * Fires when the checkbox is checked or unchecked.
20331              * @param {Roo.form.Checkbox} this This checkbox
20332              * @param {Boolean} checked The new checked value
20333              */
20334         check : true
20335     });
20336 };
20337
20338 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20339     /**
20340      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20341      */
20342     focusClass : undefined,
20343     /**
20344      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20345      */
20346     fieldClass: "x-form-field",
20347     /**
20348      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20349      */
20350     checked: false,
20351     /**
20352      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20353      * {tag: "input", type: "checkbox", autocomplete: "off"})
20354      */
20355     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20356     /**
20357      * @cfg {String} boxLabel The text that appears beside the checkbox
20358      */
20359     boxLabel : "",
20360     /**
20361      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20362      */  
20363     inputValue : '1',
20364     /**
20365      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20366      */
20367      valueOff: '0', // value when not checked..
20368
20369     actionMode : 'viewEl', 
20370     //
20371     // private
20372     itemCls : 'x-menu-check-item x-form-item',
20373     groupClass : 'x-menu-group-item',
20374     inputType : 'hidden',
20375     
20376     
20377     inSetChecked: false, // check that we are not calling self...
20378     
20379     inputElement: false, // real input element?
20380     basedOn: false, // ????
20381     
20382     isFormField: true, // not sure where this is needed!!!!
20383
20384     onResize : function(){
20385         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20386         if(!this.boxLabel){
20387             this.el.alignTo(this.wrap, 'c-c');
20388         }
20389     },
20390
20391     initEvents : function(){
20392         Roo.form.Checkbox.superclass.initEvents.call(this);
20393         this.el.on("click", this.onClick,  this);
20394         this.el.on("change", this.onClick,  this);
20395     },
20396
20397
20398     getResizeEl : function(){
20399         return this.wrap;
20400     },
20401
20402     getPositionEl : function(){
20403         return this.wrap;
20404     },
20405
20406     // private
20407     onRender : function(ct, position){
20408         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20409         /*
20410         if(this.inputValue !== undefined){
20411             this.el.dom.value = this.inputValue;
20412         }
20413         */
20414         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20415         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20416         var viewEl = this.wrap.createChild({ 
20417             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20418         this.viewEl = viewEl;   
20419         this.wrap.on('click', this.onClick,  this); 
20420         
20421         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20422         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20423         
20424         
20425         
20426         if(this.boxLabel){
20427             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20428         //    viewEl.on('click', this.onClick,  this); 
20429         }
20430         //if(this.checked){
20431             this.setChecked(this.checked);
20432         //}else{
20433             //this.checked = this.el.dom;
20434         //}
20435
20436     },
20437
20438     // private
20439     initValue : Roo.emptyFn,
20440
20441     /**
20442      * Returns the checked state of the checkbox.
20443      * @return {Boolean} True if checked, else false
20444      */
20445     getValue : function(){
20446         if(this.el){
20447             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20448         }
20449         return this.valueOff;
20450         
20451     },
20452
20453         // private
20454     onClick : function(){ 
20455         if (this.disabled) {
20456             return;
20457         }
20458         this.setChecked(!this.checked);
20459
20460         //if(this.el.dom.checked != this.checked){
20461         //    this.setValue(this.el.dom.checked);
20462        // }
20463     },
20464
20465     /**
20466      * Sets the checked state of the checkbox.
20467      * On is always based on a string comparison between inputValue and the param.
20468      * @param {Boolean/String} value - the value to set 
20469      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20470      */
20471     setValue : function(v,suppressEvent){
20472         
20473         
20474         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20475         //if(this.el && this.el.dom){
20476         //    this.el.dom.checked = this.checked;
20477         //    this.el.dom.defaultChecked = this.checked;
20478         //}
20479         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20480         //this.fireEvent("check", this, this.checked);
20481     },
20482     // private..
20483     setChecked : function(state,suppressEvent)
20484     {
20485         if (this.inSetChecked) {
20486             this.checked = state;
20487             return;
20488         }
20489         
20490     
20491         if(this.wrap){
20492             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20493         }
20494         this.checked = state;
20495         if(suppressEvent !== true){
20496             this.fireEvent('check', this, state);
20497         }
20498         this.inSetChecked = true;
20499         this.el.dom.value = state ? this.inputValue : this.valueOff;
20500         this.inSetChecked = false;
20501         
20502     },
20503     // handle setting of hidden value by some other method!!?!?
20504     setFromHidden: function()
20505     {
20506         if(!this.el){
20507             return;
20508         }
20509         //console.log("SET FROM HIDDEN");
20510         //alert('setFrom hidden');
20511         this.setValue(this.el.dom.value);
20512     },
20513     
20514     onDestroy : function()
20515     {
20516         if(this.viewEl){
20517             Roo.get(this.viewEl).remove();
20518         }
20519          
20520         Roo.form.Checkbox.superclass.onDestroy.call(this);
20521     },
20522     
20523     setBoxLabel : function(str)
20524     {
20525         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20526     }
20527
20528 });/*
20529  * Based on:
20530  * Ext JS Library 1.1.1
20531  * Copyright(c) 2006-2007, Ext JS, LLC.
20532  *
20533  * Originally Released Under LGPL - original licence link has changed is not relivant.
20534  *
20535  * Fork - LGPL
20536  * <script type="text/javascript">
20537  */
20538  
20539 /**
20540  * @class Roo.form.Radio
20541  * @extends Roo.form.Checkbox
20542  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20543  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20544  * @constructor
20545  * Creates a new Radio
20546  * @param {Object} config Configuration options
20547  */
20548 Roo.form.Radio = function(){
20549     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20550 };
20551 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20552     inputType: 'radio',
20553
20554     /**
20555      * If this radio is part of a group, it will return the selected value
20556      * @return {String}
20557      */
20558     getGroupValue : function(){
20559         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20560     },
20561     
20562     
20563     onRender : function(ct, position){
20564         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20565         
20566         if(this.inputValue !== undefined){
20567             this.el.dom.value = this.inputValue;
20568         }
20569          
20570         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20571         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20572         //var viewEl = this.wrap.createChild({ 
20573         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20574         //this.viewEl = viewEl;   
20575         //this.wrap.on('click', this.onClick,  this); 
20576         
20577         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20578         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20579         
20580         
20581         
20582         if(this.boxLabel){
20583             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20584         //    viewEl.on('click', this.onClick,  this); 
20585         }
20586          if(this.checked){
20587             this.el.dom.checked =   'checked' ;
20588         }
20589          
20590     } 
20591     
20592     
20593 });Roo.rtf = {}; // namespace
20594 Roo.rtf.Hex = function(hex)
20595 {
20596     this.hexstr = hex;
20597 };
20598 Roo.rtf.Paragraph = function(opts)
20599 {
20600     this.content = []; ///??? is that used?
20601 };Roo.rtf.Span = function(opts)
20602 {
20603     this.value = opts.value;
20604 };
20605
20606 Roo.rtf.Group = function(parent)
20607 {
20608     // we dont want to acutally store parent - it will make debug a nightmare..
20609     this.content = [];
20610     this.cn  = [];
20611      
20612        
20613     
20614 };
20615
20616 Roo.rtf.Group.prototype = {
20617     ignorable : false,
20618     content: false,
20619     cn: false,
20620     addContent : function(node) {
20621         // could set styles...
20622         this.content.push(node);
20623     },
20624     addChild : function(cn)
20625     {
20626         this.cn.push(cn);
20627     },
20628     // only for images really...
20629     toDataURL : function()
20630     {
20631         var mimetype = false;
20632         switch(true) {
20633             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20634                 mimetype = "image/png";
20635                 break;
20636              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20637                 mimetype = "image/jpeg";
20638                 break;
20639             default :
20640                 return 'about:blank'; // ?? error?
20641         }
20642         
20643         
20644         var hexstring = this.content[this.content.length-1].value;
20645         
20646         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20647             return String.fromCharCode(parseInt(a, 16));
20648         }).join(""));
20649     }
20650     
20651 };
20652 // this looks like it's normally the {rtf{ .... }}
20653 Roo.rtf.Document = function()
20654 {
20655     // we dont want to acutally store parent - it will make debug a nightmare..
20656     this.rtlch  = [];
20657     this.content = [];
20658     this.cn = [];
20659     
20660 };
20661 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20662     addChild : function(cn)
20663     {
20664         this.cn.push(cn);
20665         switch(cn.type) {
20666             case 'rtlch': // most content seems to be inside this??
20667             case 'listtext':
20668             case 'shpinst':
20669                 this.rtlch.push(cn);
20670                 return;
20671             default:
20672                 this[cn.type] = cn;
20673         }
20674         
20675     },
20676     
20677     getElementsByType : function(type)
20678     {
20679         var ret =  [];
20680         this._getElementsByType(type, ret, this.cn, 'rtf');
20681         return ret;
20682     },
20683     _getElementsByType : function (type, ret, search_array, path)
20684     {
20685         search_array.forEach(function(n,i) {
20686             if (n.type == type) {
20687                 n.path = path + '/' + n.type + ':' + i;
20688                 ret.push(n);
20689             }
20690             if (n.cn.length > 0) {
20691                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20692             }
20693         },this);
20694     }
20695     
20696 });
20697  
20698 Roo.rtf.Ctrl = function(opts)
20699 {
20700     this.value = opts.value;
20701     this.param = opts.param;
20702 };
20703 /**
20704  *
20705  *
20706  * based on this https://github.com/iarna/rtf-parser
20707  * it's really only designed to extract pict from pasted RTF 
20708  *
20709  * usage:
20710  *
20711  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20712  *  
20713  *
20714  */
20715
20716  
20717
20718
20719
20720 Roo.rtf.Parser = function(text) {
20721     //super({objectMode: true})
20722     this.text = '';
20723     this.parserState = this.parseText;
20724     
20725     // these are for interpeter...
20726     this.doc = {};
20727     ///this.parserState = this.parseTop
20728     this.groupStack = [];
20729     this.hexStore = [];
20730     this.doc = false;
20731     
20732     this.groups = []; // where we put the return.
20733     
20734     for (var ii = 0; ii < text.length; ++ii) {
20735         ++this.cpos;
20736         
20737         if (text[ii] === '\n') {
20738             ++this.row;
20739             this.col = 1;
20740         } else {
20741             ++this.col;
20742         }
20743         this.parserState(text[ii]);
20744     }
20745     
20746     
20747     
20748 };
20749 Roo.rtf.Parser.prototype = {
20750     text : '', // string being parsed..
20751     controlWord : '',
20752     controlWordParam :  '',
20753     hexChar : '',
20754     doc : false,
20755     group: false,
20756     groupStack : false,
20757     hexStore : false,
20758     
20759     
20760     cpos : 0, 
20761     row : 1, // reportin?
20762     col : 1, //
20763
20764      
20765     push : function (el)
20766     {
20767         var m = 'cmd'+ el.type;
20768         if (typeof(this[m]) == 'undefined') {
20769             Roo.log('invalid cmd:' + el.type);
20770             return;
20771         }
20772         this[m](el);
20773         //Roo.log(el);
20774     },
20775     flushHexStore : function()
20776     {
20777         if (this.hexStore.length < 1) {
20778             return;
20779         }
20780         var hexstr = this.hexStore.map(
20781             function(cmd) {
20782                 return cmd.value;
20783         }).join('');
20784         
20785         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20786               
20787             
20788         this.hexStore.splice(0)
20789         
20790     },
20791     
20792     cmdgroupstart : function()
20793     {
20794         this.flushHexStore();
20795         if (this.group) {
20796             this.groupStack.push(this.group);
20797         }
20798          // parent..
20799         if (this.doc === false) {
20800             this.group = this.doc = new Roo.rtf.Document();
20801             return;
20802             
20803         }
20804         this.group = new Roo.rtf.Group(this.group);
20805     },
20806     cmdignorable : function()
20807     {
20808         this.flushHexStore();
20809         this.group.ignorable = true;
20810     },
20811     cmdendparagraph : function()
20812     {
20813         this.flushHexStore();
20814         this.group.addContent(new Roo.rtf.Paragraph());
20815     },
20816     cmdgroupend : function ()
20817     {
20818         this.flushHexStore();
20819         var endingGroup = this.group;
20820         
20821         
20822         this.group = this.groupStack.pop();
20823         if (this.group) {
20824             this.group.addChild(endingGroup);
20825         }
20826         
20827         
20828         
20829         var doc = this.group || this.doc;
20830         //if (endingGroup instanceof FontTable) {
20831         //  doc.fonts = endingGroup.table
20832         //} else if (endingGroup instanceof ColorTable) {
20833         //  doc.colors = endingGroup.table
20834         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20835         if (endingGroup.ignorable === false) {
20836             //code
20837             this.groups.push(endingGroup);
20838            // Roo.log( endingGroup );
20839         }
20840             //Roo.each(endingGroup.content, function(item)) {
20841             //    doc.addContent(item);
20842             //}
20843             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20844         //}
20845     },
20846     cmdtext : function (cmd)
20847     {
20848         this.flushHexStore();
20849         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20850             //this.group = this.doc
20851         }
20852         this.group.addContent(new Roo.rtf.Span(cmd));
20853     },
20854     cmdcontrolword : function (cmd)
20855     {
20856         this.flushHexStore();
20857         if (!this.group.type) {
20858             this.group.type = cmd.value;
20859             return;
20860         }
20861         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20862         // we actually don't care about ctrl words...
20863         return ;
20864         /*
20865         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20866         if (this[method]) {
20867             this[method](cmd.param)
20868         } else {
20869             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20870         }
20871         */
20872     },
20873     cmdhexchar : function(cmd) {
20874         this.hexStore.push(cmd);
20875     },
20876     cmderror : function(cmd) {
20877         throw new Exception (cmd.value);
20878     },
20879     
20880     /*
20881       _flush (done) {
20882         if (this.text !== '\u0000') this.emitText()
20883         done()
20884       }
20885       */
20886       
20887       
20888     parseText : function(c)
20889     {
20890         if (c === '\\') {
20891             this.parserState = this.parseEscapes;
20892         } else if (c === '{') {
20893             this.emitStartGroup();
20894         } else if (c === '}') {
20895             this.emitEndGroup();
20896         } else if (c === '\x0A' || c === '\x0D') {
20897             // cr/lf are noise chars
20898         } else {
20899             this.text += c;
20900         }
20901     },
20902     
20903     parseEscapes: function (c)
20904     {
20905         if (c === '\\' || c === '{' || c === '}') {
20906             this.text += c;
20907             this.parserState = this.parseText;
20908         } else {
20909             this.parserState = this.parseControlSymbol;
20910             this.parseControlSymbol(c);
20911         }
20912     },
20913     parseControlSymbol: function(c)
20914     {
20915         if (c === '~') {
20916             this.text += '\u00a0'; // nbsp
20917             this.parserState = this.parseText
20918         } else if (c === '-') {
20919              this.text += '\u00ad'; // soft hyphen
20920         } else if (c === '_') {
20921             this.text += '\u2011'; // non-breaking hyphen
20922         } else if (c === '*') {
20923             this.emitIgnorable();
20924             this.parserState = this.parseText;
20925         } else if (c === "'") {
20926             this.parserState = this.parseHexChar;
20927         } else if (c === '|') { // formula cacter
20928             this.emitFormula();
20929             this.parserState = this.parseText;
20930         } else if (c === ':') { // subentry in an index entry
20931             this.emitIndexSubEntry();
20932             this.parserState = this.parseText;
20933         } else if (c === '\x0a') {
20934             this.emitEndParagraph();
20935             this.parserState = this.parseText;
20936         } else if (c === '\x0d') {
20937             this.emitEndParagraph();
20938             this.parserState = this.parseText;
20939         } else {
20940             this.parserState = this.parseControlWord;
20941             this.parseControlWord(c);
20942         }
20943     },
20944     parseHexChar: function (c)
20945     {
20946         if (/^[A-Fa-f0-9]$/.test(c)) {
20947             this.hexChar += c;
20948             if (this.hexChar.length >= 2) {
20949               this.emitHexChar();
20950               this.parserState = this.parseText;
20951             }
20952             return;
20953         }
20954         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20955         this.parserState = this.parseText;
20956         
20957     },
20958     parseControlWord : function(c)
20959     {
20960         if (c === ' ') {
20961             this.emitControlWord();
20962             this.parserState = this.parseText;
20963         } else if (/^[-\d]$/.test(c)) {
20964             this.parserState = this.parseControlWordParam;
20965             this.controlWordParam += c;
20966         } else if (/^[A-Za-z]$/.test(c)) {
20967           this.controlWord += c;
20968         } else {
20969           this.emitControlWord();
20970           this.parserState = this.parseText;
20971           this.parseText(c);
20972         }
20973     },
20974     parseControlWordParam : function (c) {
20975         if (/^\d$/.test(c)) {
20976           this.controlWordParam += c;
20977         } else if (c === ' ') {
20978           this.emitControlWord();
20979           this.parserState = this.parseText;
20980         } else {
20981           this.emitControlWord();
20982           this.parserState = this.parseText;
20983           this.parseText(c);
20984         }
20985     },
20986     
20987     
20988     
20989     
20990     emitText : function () {
20991         if (this.text === '') {
20992             return;
20993         }
20994         this.push({
20995             type: 'text',
20996             value: this.text,
20997             pos: this.cpos,
20998             row: this.row,
20999             col: this.col
21000         });
21001         this.text = ''
21002     },
21003     emitControlWord : function ()
21004     {
21005         this.emitText();
21006         if (this.controlWord === '') {
21007             this.emitError('empty control word');
21008         } else {
21009             this.push({
21010                   type: 'controlword',
21011                   value: this.controlWord,
21012                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21013                   pos: this.cpos,
21014                   row: this.row,
21015                   col: this.col
21016             });
21017         }
21018         this.controlWord = '';
21019         this.controlWordParam = '';
21020     },
21021     emitStartGroup : function ()
21022     {
21023         this.emitText();
21024         this.push({
21025             type: 'groupstart',
21026             pos: this.cpos,
21027             row: this.row,
21028             col: this.col
21029         });
21030     },
21031     emitEndGroup : function ()
21032     {
21033         this.emitText();
21034         this.push({
21035             type: 'groupend',
21036             pos: this.cpos,
21037             row: this.row,
21038             col: this.col
21039         });
21040     },
21041     emitIgnorable : function ()
21042     {
21043         this.emitText();
21044         this.push({
21045             type: 'ignorable',
21046             pos: this.cpos,
21047             row: this.row,
21048             col: this.col
21049         });
21050     },
21051     emitHexChar : function ()
21052     {
21053         this.emitText();
21054         this.push({
21055             type: 'hexchar',
21056             value: this.hexChar,
21057             pos: this.cpos,
21058             row: this.row,
21059             col: this.col
21060         });
21061         this.hexChar = ''
21062     },
21063     emitError : function (message)
21064     {
21065       this.emitText();
21066       this.push({
21067             type: 'error',
21068             value: message,
21069             row: this.row,
21070             col: this.col,
21071             char: this.cpos //,
21072             //stack: new Error().stack
21073         });
21074     },
21075     emitEndParagraph : function () {
21076         this.emitText();
21077         this.push({
21078             type: 'endparagraph',
21079             pos: this.cpos,
21080             row: this.row,
21081             col: this.col
21082         });
21083     }
21084      
21085 } ;
21086 Roo.htmleditor = {};
21087  
21088 /**
21089  * @class Roo.htmleditor.Filter
21090  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21091  * @cfg {DomElement} node The node to iterate and filter
21092  * @cfg {boolean|String|Array} tag Tags to replace 
21093  * @constructor
21094  * Create a new Filter.
21095  * @param {Object} config Configuration options
21096  */
21097
21098
21099
21100 Roo.htmleditor.Filter = function(cfg) {
21101     Roo.apply(this.cfg);
21102     // this does not actually call walk as it's really just a abstract class
21103 }
21104
21105
21106 Roo.htmleditor.Filter.prototype = {
21107     
21108     node: false,
21109     
21110     tag: false,
21111
21112     // overrride to do replace comments.
21113     replaceComment : false,
21114     
21115     // overrride to do replace or do stuff with tags..
21116     replaceTag : false,
21117     
21118     walk : function(dom)
21119     {
21120         Roo.each( Array.from(dom.childNodes), function( e ) {
21121             switch(true) {
21122                 
21123                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
21124                     this.replaceComment(e);
21125                     return;
21126                 
21127                 case e.nodeType != 1: //not a node.
21128                     return;
21129                 
21130                 case this.tag === true: // everything
21131                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21132                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21133                     if (this.replaceTag && false === this.replaceTag(e)) {
21134                         return;
21135                     }
21136                     if (e.hasChildNodes()) {
21137                         this.walk(e);
21138                     }
21139                     return;
21140                 
21141                 default:    // tags .. that do not match.
21142                     if (e.hasChildNodes()) {
21143                         this.walk(e);
21144                     }
21145             }
21146             
21147         }, this);
21148         
21149     }
21150 }; 
21151
21152 /**
21153  * @class Roo.htmleditor.FilterAttributes
21154  * clean attributes and  styles including http:// etc.. in attribute
21155  * @constructor
21156 * Run a new Attribute Filter
21157 * @param {Object} config Configuration options
21158  */
21159 Roo.htmleditor.FilterAttributes = function(cfg)
21160 {
21161     Roo.apply(this, cfg);
21162     this.attrib_black = this.attrib_black || [];
21163     this.attrib_white = this.attrib_white || [];
21164
21165     this.attrib_clean = this.attrib_clean || [];
21166     this.style_white = this.style_white || [];
21167     this.style_black = this.style_black || [];
21168     this.walk(cfg.node);
21169 }
21170
21171 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21172 {
21173     tag: true, // all tags
21174     
21175     attrib_black : false, // array
21176     attrib_clean : false,
21177     attrib_white : false,
21178
21179     style_white : false,
21180     style_black : false,
21181      
21182      
21183     replaceTag : function(node)
21184     {
21185         if (!node.attributes || !node.attributes.length) {
21186             return true;
21187         }
21188         
21189         for (var i = node.attributes.length-1; i > -1 ; i--) {
21190             var a = node.attributes[i];
21191             //console.log(a);
21192             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21193                 node.removeAttribute(a.name);
21194                 continue;
21195             }
21196             
21197             
21198             
21199             if (a.name.toLowerCase().substr(0,2)=='on')  {
21200                 node.removeAttribute(a.name);
21201                 continue;
21202             }
21203             
21204             
21205             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21206                 node.removeAttribute(a.name);
21207                 continue;
21208             }
21209             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21210                 this.cleanAttr(node,a.name,a.value); // fixme..
21211                 continue;
21212             }
21213             if (a.name == 'style') {
21214                 this.cleanStyle(node,a.name,a.value);
21215                 continue;
21216             }
21217             /// clean up MS crap..
21218             // tecnically this should be a list of valid class'es..
21219             
21220             
21221             if (a.name == 'class') {
21222                 if (a.value.match(/^Mso/)) {
21223                     node.removeAttribute('class');
21224                 }
21225                 
21226                 if (a.value.match(/^body$/)) {
21227                     node.removeAttribute('class');
21228                 }
21229                 continue;
21230             }
21231             
21232             
21233             // style cleanup!?
21234             // class cleanup?
21235             
21236         }
21237         return true; // clean children
21238     },
21239         
21240     cleanAttr: function(node, n,v)
21241     {
21242         
21243         if (v.match(/^\./) || v.match(/^\//)) {
21244             return;
21245         }
21246         if (v.match(/^(http|https):\/\//)
21247             || v.match(/^mailto:/) 
21248             || v.match(/^ftp:/)
21249             || v.match(/^data:/)
21250             ) {
21251             return;
21252         }
21253         if (v.match(/^#/)) {
21254             return;
21255         }
21256         if (v.match(/^\{/)) { // allow template editing.
21257             return;
21258         }
21259 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21260         node.removeAttribute(n);
21261         
21262     },
21263     cleanStyle : function(node,  n,v)
21264     {
21265         if (v.match(/expression/)) { //XSS?? should we even bother..
21266             node.removeAttribute(n);
21267             return;
21268         }
21269         
21270         var parts = v.split(/;/);
21271         var clean = [];
21272         
21273         Roo.each(parts, function(p) {
21274             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21275             if (!p.length) {
21276                 return true;
21277             }
21278             var l = p.split(':').shift().replace(/\s+/g,'');
21279             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21280             
21281             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21282                 return true;
21283             }
21284             //Roo.log()
21285             // only allow 'c whitelisted system attributes'
21286             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21287                 return true;
21288             }
21289             
21290             
21291             clean.push(p);
21292             return true;
21293         },this);
21294         if (clean.length) { 
21295             node.setAttribute(n, clean.join(';'));
21296         } else {
21297             node.removeAttribute(n);
21298         }
21299         
21300     }
21301         
21302         
21303         
21304     
21305 });/**
21306  * @class Roo.htmleditor.FilterBlack
21307  * remove blacklisted elements.
21308  * @constructor
21309  * Run a new Blacklisted Filter
21310  * @param {Object} config Configuration options
21311  */
21312
21313 Roo.htmleditor.FilterBlack = function(cfg)
21314 {
21315     Roo.apply(this, cfg);
21316     this.walk(cfg.node);
21317 }
21318
21319 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21320 {
21321     tag : true, // all elements.
21322    
21323     replace : function(n)
21324     {
21325         n.parentNode.removeChild(n);
21326     }
21327 });
21328 /**
21329  * @class Roo.htmleditor.FilterComment
21330  * remove comments.
21331  * @constructor
21332 * Run a new Comments Filter
21333 * @param {Object} config Configuration options
21334  */
21335 Roo.htmleditor.FilterComment = function(cfg)
21336 {
21337     this.walk(cfg.node);
21338 }
21339
21340 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21341 {
21342   
21343     replaceComment : function(n)
21344     {
21345         n.parentNode.removeChild(n);
21346     }
21347 });/**
21348  * @class Roo.htmleditor.FilterKeepChildren
21349  * remove tags but keep children
21350  * @constructor
21351  * Run a new Keep Children Filter
21352  * @param {Object} config Configuration options
21353  */
21354
21355 Roo.htmleditor.FilterKeepChildren = function(cfg)
21356 {
21357     Roo.apply(this, cfg);
21358     if (this.tag === false) {
21359         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21360     }
21361     this.walk(cfg.node);
21362 }
21363
21364 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21365 {
21366     
21367   
21368     replaceTag : function(node)
21369     {
21370         // walk children...
21371         //Roo.log(node);
21372         var ar = Array.from(node.childNodes);
21373         //remove first..
21374         for (var i = 0; i < ar.length; i++) {
21375             if (ar[i].nodeType == 1) {
21376                 if (
21377                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21378                     || // array and it matches
21379                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21380                 ) {
21381                     this.replaceTag(ar[i]); // child is blacklisted as well...
21382                     continue;
21383                 }
21384             }
21385         }  
21386         ar = Array.from(node.childNodes);
21387         for (var i = 0; i < ar.length; i++) {
21388          
21389             node.removeChild(ar[i]);
21390             // what if we need to walk these???
21391             node.parentNode.insertBefore(ar[i], node);
21392             if (this.tag !== false) {
21393                 this.walk(ar[i]);
21394                 
21395             }
21396         }
21397         node.parentNode.removeChild(node);
21398         return false; // don't walk children
21399         
21400         
21401     }
21402 });/**
21403  * @class Roo.htmleditor.FilterParagraph
21404  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21405  * like on 'push' to remove the <p> tags and replace them with line breaks.
21406  * @constructor
21407  * Run a new Paragraph Filter
21408  * @param {Object} config Configuration options
21409  */
21410
21411 Roo.htmleditor.FilterParagraph = function(cfg)
21412 {
21413     // no need to apply config.
21414     this.walk(cfg.node);
21415 }
21416
21417 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21418 {
21419     
21420      
21421     tag : 'P',
21422     
21423      
21424     replaceTag : function(node)
21425     {
21426         
21427         if (node.childNodes.length == 1 &&
21428             node.childNodes[0].nodeType == 3 &&
21429             node.childNodes[0].textContent.trim().length < 1
21430             ) {
21431             // remove and replace with '<BR>';
21432             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21433             return false; // no need to walk..
21434         }
21435         var ar = Array.from(node.childNodes);
21436         for (var i = 0; i < ar.length; i++) {
21437             node.removeChild(ar[i]);
21438             // what if we need to walk these???
21439             node.parentNode.insertBefore(ar[i], node);
21440         }
21441         // now what about this?
21442         // <p> &nbsp; </p>
21443         
21444         // double BR.
21445         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21446         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21447         node.parentNode.removeChild(node);
21448         
21449         return false;
21450
21451     }
21452     
21453 });/**
21454  * @class Roo.htmleditor.FilterSpan
21455  * filter span's with no attributes out..
21456  * @constructor
21457  * Run a new Span Filter
21458  * @param {Object} config Configuration options
21459  */
21460
21461 Roo.htmleditor.FilterSpan = function(cfg)
21462 {
21463     // no need to apply config.
21464     this.walk(cfg.node);
21465 }
21466
21467 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21468 {
21469      
21470     tag : 'SPAN',
21471      
21472  
21473     replaceTag : function(node)
21474     {
21475         if (node.attributes && node.attributes.length > 0) {
21476             return true; // walk if there are any.
21477         }
21478         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21479         return false;
21480      
21481     }
21482     
21483 });/**
21484  * @class Roo.htmleditor.FilterTableWidth
21485   try and remove table width data - as that frequently messes up other stuff.
21486  * 
21487  *      was cleanTableWidths.
21488  *
21489  * Quite often pasting from word etc.. results in tables with column and widths.
21490  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21491  *
21492  * @constructor
21493  * Run a new Table Filter
21494  * @param {Object} config Configuration options
21495  */
21496
21497 Roo.htmleditor.FilterTableWidth = function(cfg)
21498 {
21499     // no need to apply config.
21500     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21501     this.walk(cfg.node);
21502 }
21503
21504 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21505 {
21506      
21507      
21508     
21509     replaceTag: function(node) {
21510         
21511         
21512       
21513         if (node.hasAttribute('width')) {
21514             node.removeAttribute('width');
21515         }
21516         
21517          
21518         if (node.hasAttribute("style")) {
21519             // pretty basic...
21520             
21521             var styles = node.getAttribute("style").split(";");
21522             var nstyle = [];
21523             Roo.each(styles, function(s) {
21524                 if (!s.match(/:/)) {
21525                     return;
21526                 }
21527                 var kv = s.split(":");
21528                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21529                     return;
21530                 }
21531                 // what ever is left... we allow.
21532                 nstyle.push(s);
21533             });
21534             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21535             if (!nstyle.length) {
21536                 node.removeAttribute('style');
21537             }
21538         }
21539         
21540         return true; // continue doing children..
21541     }
21542 });/**
21543  * @class Roo.htmleditor.FilterWord
21544  * try and clean up all the mess that Word generates.
21545  * 
21546  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21547  
21548  * @constructor
21549  * Run a new Span Filter
21550  * @param {Object} config Configuration options
21551  */
21552
21553 Roo.htmleditor.FilterWord = function(cfg)
21554 {
21555     // no need to apply config.
21556     this.walk(cfg.node);
21557 }
21558
21559 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21560 {
21561     tag: true,
21562      
21563     
21564     /**
21565      * Clean up MS wordisms...
21566      */
21567     replaceTag : function(node)
21568     {
21569          
21570         // no idea what this does - span with text, replaceds with just text.
21571         if(
21572                 node.nodeName == 'SPAN' &&
21573                 !node.hasAttributes() &&
21574                 node.childNodes.length == 1 &&
21575                 node.firstChild.nodeName == "#text"  
21576         ) {
21577             var textNode = node.firstChild;
21578             node.removeChild(textNode);
21579             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21580                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21581             }
21582             node.parentNode.insertBefore(textNode, node);
21583             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21584                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21585             }
21586             
21587             node.parentNode.removeChild(node);
21588             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21589         }
21590         
21591    
21592         
21593         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21594             node.parentNode.removeChild(node);
21595             return false; // dont do chidlren
21596         }
21597         //Roo.log(node.tagName);
21598         // remove - but keep children..
21599         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21600             //Roo.log('-- removed');
21601             while (node.childNodes.length) {
21602                 var cn = node.childNodes[0];
21603                 node.removeChild(cn);
21604                 node.parentNode.insertBefore(cn, node);
21605                 // move node to parent - and clean it..
21606                 this.replaceTag(cn);
21607             }
21608             node.parentNode.removeChild(node);
21609             /// no need to iterate chidlren = it's got none..
21610             //this.iterateChildren(node, this.cleanWord);
21611             return false; // no need to iterate children.
21612         }
21613         // clean styles
21614         if (node.className.length) {
21615             
21616             var cn = node.className.split(/\W+/);
21617             var cna = [];
21618             Roo.each(cn, function(cls) {
21619                 if (cls.match(/Mso[a-zA-Z]+/)) {
21620                     return;
21621                 }
21622                 cna.push(cls);
21623             });
21624             node.className = cna.length ? cna.join(' ') : '';
21625             if (!cna.length) {
21626                 node.removeAttribute("class");
21627             }
21628         }
21629         
21630         if (node.hasAttribute("lang")) {
21631             node.removeAttribute("lang");
21632         }
21633         
21634         if (node.hasAttribute("style")) {
21635             
21636             var styles = node.getAttribute("style").split(";");
21637             var nstyle = [];
21638             Roo.each(styles, function(s) {
21639                 if (!s.match(/:/)) {
21640                     return;
21641                 }
21642                 var kv = s.split(":");
21643                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21644                     return;
21645                 }
21646                 // what ever is left... we allow.
21647                 nstyle.push(s);
21648             });
21649             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21650             if (!nstyle.length) {
21651                 node.removeAttribute('style');
21652             }
21653         }
21654         return true; // do children
21655         
21656         
21657         
21658     }
21659 });
21660 /**
21661  * @class Roo.htmleditor.FilterStyleToTag
21662  * part of the word stuff... - certain 'styles' should be converted to tags.
21663  * eg.
21664  *   font-weight: bold -> bold
21665  *   ?? super / subscrit etc..
21666  * 
21667  * @constructor
21668 * Run a new style to tag filter.
21669 * @param {Object} config Configuration options
21670  */
21671 Roo.htmleditor.FilterStyleToTag = function(cfg)
21672 {
21673     
21674     this.tags = {
21675         B  : [ 'fontWeight' , 'bold'],
21676         I :  [ 'fontStyle' , 'italic'],
21677         //pre :  [ 'font-style' , 'italic'],
21678         // h1.. h6 ?? font-size?
21679         SUP : [ 'verticalAlign' , 'super' ],
21680         SUB : [ 'verticalAlign' , 'sub' ]
21681         
21682         
21683     };
21684     
21685     Roo.apply(this, cfg);
21686      
21687     
21688     this.walk(cfg.node);
21689     
21690     
21691     
21692 }
21693
21694
21695 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21696 {
21697     tag: true, // all tags
21698     
21699     tags : false,
21700     
21701     
21702     replaceTag : function(node)
21703     {
21704         
21705         
21706         if (node.getAttribute("style") === null) {
21707             return true;
21708         }
21709         var inject = [];
21710         for (var k in this.tags) {
21711             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21712                 inject.push(k);
21713                 node.style.removeProperty(this.tags[k][0]);
21714             }
21715         }
21716         if (!inject.length) {
21717             return true; 
21718         }
21719         var cn = Array.from(node.childNodes);
21720         var nn = node;
21721         Roo.each(inject, function(t) {
21722             var nc = node.ownerDocument.createElement(t);
21723             nn.appendChild(nc);
21724             nn = nc;
21725         });
21726         for(var i = 0;i < cn.length;cn++) {
21727             node.removeChild(cn[i]);
21728             nn.appendChild(cn[i]);
21729         }
21730         return true /// iterate thru
21731     }
21732     
21733 })/**
21734  * @class Roo.htmleditor.FilterLongBr
21735  * BR/BR/BR - keep a maximum of 2...
21736  * @constructor
21737  * Run a new Long BR Filter
21738  * @param {Object} config Configuration options
21739  */
21740
21741 Roo.htmleditor.FilterLongBr = function(cfg)
21742 {
21743     // no need to apply config.
21744     this.walk(cfg.node);
21745 }
21746
21747 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21748 {
21749     
21750      
21751     tag : 'BR',
21752     
21753      
21754     replaceTag : function(node)
21755     {
21756         
21757         var ps = node.nextSibling;
21758         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21759             ps = ps.nextSibling;
21760         }
21761         
21762         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21763             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21764             return false;
21765         }
21766         
21767         if (!ps || ps.nodeType != 1) {
21768             return false;
21769         }
21770         
21771         if (!ps || ps.tagName != 'BR') {
21772            
21773             return false;
21774         }
21775         
21776         
21777         
21778         
21779         
21780         if (!node.previousSibling) {
21781             return false;
21782         }
21783         var ps = node.previousSibling;
21784         
21785         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21786             ps = ps.previousSibling;
21787         }
21788         if (!ps || ps.nodeType != 1) {
21789             return false;
21790         }
21791         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21792         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21793             return false;
21794         }
21795         
21796         node.parentNode.removeChild(node); // remove me...
21797         
21798         return false; // no need to do children
21799
21800     }
21801     
21802 }); 
21803
21804 /**
21805  * @class Roo.htmleditor.FilterBlock
21806  * removes id / data-block and contenteditable that are associated with blocks
21807  * usage should be done on a cloned copy of the dom
21808  * @constructor
21809 * Run a new Attribute Filter { node : xxxx }}
21810 * @param {Object} config Configuration options
21811  */
21812 Roo.htmleditor.FilterBlock = function(cfg)
21813 {
21814     Roo.apply(this, cfg);
21815     var qa = cfg.node.querySelectorAll;
21816     this.removeAttributes('data-block');
21817     this.removeAttributes('contenteditable');
21818     this.removeAttributes('id');
21819     
21820 }
21821
21822 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21823 {
21824     node: true, // all tags
21825      
21826      
21827     removeAttributes : function(attr)
21828     {
21829         var ar = this.node.querySelectorAll('*[' + attr + ']');
21830         for (var i =0;i<ar.length;i++) {
21831             ar[i].removeAttribute(attr);
21832         }
21833     }
21834         
21835         
21836         
21837     
21838 });
21839 /**
21840  * @class Roo.htmleditor.Tidy
21841  * Tidy HTML 
21842  * @cfg {Roo.HtmlEditorCore} core the editor.
21843  * @constructor
21844  * Create a new Filter.
21845  * @param {Object} config Configuration options
21846  */
21847
21848
21849 Roo.htmleditor.Tidy = function(cfg) {
21850     Roo.apply(this, cfg);
21851     
21852     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
21853      
21854 }
21855
21856 Roo.htmleditor.Tidy.toString = function(node)
21857 {
21858     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
21859 }
21860
21861 Roo.htmleditor.Tidy.prototype = {
21862     
21863     
21864     wrap : function(s) {
21865         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
21866     },
21867
21868     
21869     tidy : function(node, indent) {
21870      
21871         if  (node.nodeType == 3) {
21872             // text.
21873             
21874             
21875             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
21876                 
21877             
21878         }
21879         
21880         if  (node.nodeType != 1) {
21881             return '';
21882         }
21883         
21884         
21885         
21886         if (node.tagName == 'BODY') {
21887             
21888             return this.cn(node, '');
21889         }
21890              
21891              // Prints the node tagName, such as <A>, <IMG>, etc
21892         var ret = "<" + node.tagName +  this.attr(node) ;
21893         
21894         // elements with no children..
21895         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
21896                 return ret + '/>';
21897         }
21898         ret += '>';
21899         
21900         
21901         var cindent = indent === false ? '' : (indent + '  ');
21902         // tags where we will not pad the children.. (inline text tags etc..)
21903         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
21904             cindent = false;
21905             
21906             
21907         }
21908         
21909         var cn = this.cn(node, cindent );
21910         
21911         return ret + cn  + '</' + node.tagName + '>';
21912         
21913     },
21914     cn: function(node, indent)
21915     {
21916         var ret = [];
21917         
21918         var ar = Array.from(node.childNodes);
21919         for (var i = 0 ; i < ar.length ; i++) {
21920             
21921             
21922             
21923             if (indent !== false   // indent==false preservies everything
21924                 && i > 0
21925                 && ar[i].nodeType == 3 
21926                 && ar[i].nodeValue.length > 0
21927                 && ar[i].nodeValue.match(/^\s+/)
21928             ) {
21929                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
21930                     ret.pop(); // remove line break from last?
21931                 }
21932                 
21933                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
21934             }
21935             if (indent !== false
21936                 && ar[i].nodeType == 1 // element - and indent is not set... 
21937             ) {
21938                 ret.push("\n" + indent); 
21939             }
21940             
21941             ret.push(this.tidy(ar[i], indent));
21942             // text + trailing indent 
21943             if (indent !== false
21944                 && ar[i].nodeType == 3
21945                 && ar[i].nodeValue.length > 0
21946                 && ar[i].nodeValue.match(/\s+$/)
21947             ){
21948                 ret.push("\n" + indent); 
21949             }
21950             
21951             
21952             
21953             
21954         }
21955         // what if all text?
21956         
21957         
21958         return ret.join('');
21959     },
21960     
21961          
21962         
21963     attr : function(node)
21964     {
21965         var attr = [];
21966         for(i = 0; i < node.attributes.length;i++) {
21967             
21968             // skip empty values?
21969             if (!node.attributes.item(i).value.length) {
21970                 continue;
21971             }
21972             attr.push(  node.attributes.item(i).name + '="' +
21973                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
21974             );
21975         }
21976         return attr.length ? (' ' + attr.join(' ') ) : '';
21977         
21978     }
21979     
21980     
21981     
21982 }
21983 /**
21984  * @class Roo.htmleditor.KeyEnter
21985  * Handle Enter press..
21986  * @cfg {Roo.HtmlEditorCore} core the editor.
21987  * @constructor
21988  * Create a new Filter.
21989  * @param {Object} config Configuration options
21990  */
21991
21992
21993
21994
21995
21996 Roo.htmleditor.KeyEnter = function(cfg) {
21997     Roo.apply(this, cfg);
21998     // this does not actually call walk as it's really just a abstract class
21999  
22000     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
22001 }
22002
22003 //Roo.htmleditor.KeyEnter.i = 0;
22004
22005
22006 Roo.htmleditor.KeyEnter.prototype = {
22007     
22008     core : false,
22009     
22010     keypress : function(e)
22011     {
22012         if (e.charCode != 13 && e.charCode != 10) {
22013             Roo.log([e.charCode,e]);
22014             return true;
22015         }
22016         e.preventDefault();
22017         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
22018         var doc = this.core.doc;
22019           //add a new line
22020        
22021     
22022         var sel = this.core.getSelection();
22023         var range = sel.getRangeAt(0);
22024         var n = range.commonAncestorContainer;
22025         var pc = range.closest([ 'ol', 'ul']);
22026         var pli = range.closest('li');
22027         if (!pc || e.ctrlKey) {
22028             sel.insertNode('br', 'after'); 
22029          
22030             this.core.undoManager.addEvent();
22031             this.core.fireEditorEvent(e);
22032             return false;
22033         }
22034         
22035         // deal with <li> insetion
22036         if (pli.innerText.trim() == '' &&
22037             pli.previousSibling &&
22038             pli.previousSibling.nodeName == 'LI' &&
22039             pli.previousSibling.innerText.trim() ==  '') {
22040             pli.parentNode.removeChild(pli.previousSibling);
22041             sel.cursorAfter(pc);
22042             this.core.undoManager.addEvent();
22043             this.core.fireEditorEvent(e);
22044             return false;
22045         }
22046     
22047         var li = doc.createElement('LI');
22048         li.innerHTML = '&nbsp;';
22049         if (!pli || !pli.firstSibling) {
22050             pc.appendChild(li);
22051         } else {
22052             pli.parentNode.insertBefore(li, pli.firstSibling);
22053         }
22054         sel.cursorText (li.firstChild);
22055       
22056         this.core.undoManager.addEvent();
22057         this.core.fireEditorEvent(e);
22058
22059         return false;
22060         
22061     
22062         
22063         
22064          
22065     }
22066 };
22067      
22068 /**
22069  * @class Roo.htmleditor.Block
22070  * Base class for html editor blocks - do not use it directly .. extend it..
22071  * @cfg {DomElement} node The node to apply stuff to.
22072  * @cfg {String} friendly_name the name that appears in the context bar about this block
22073  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
22074  
22075  * @constructor
22076  * Create a new Filter.
22077  * @param {Object} config Configuration options
22078  */
22079
22080 Roo.htmleditor.Block  = function(cfg)
22081 {
22082     // do nothing .. should not be called really.
22083 }
22084 /**
22085  * factory method to get the block from an element (using cache if necessary)
22086  * @static
22087  * @param {HtmlElement} the dom element
22088  */
22089 Roo.htmleditor.Block.factory = function(node)
22090 {
22091     var cc = Roo.htmleditor.Block.cache;
22092     var id = Roo.get(node).id;
22093     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
22094         Roo.htmleditor.Block.cache[id].readElement(node);
22095         return Roo.htmleditor.Block.cache[id];
22096     }
22097     var db  = node.getAttribute('data-block');
22098     if (!db) {
22099         db = node.nodeName.toLowerCase().toUpperCaseFirst();
22100     }
22101     var cls = Roo.htmleditor['Block' + db];
22102     if (typeof(cls) == 'undefined') {
22103         //Roo.log(node.getAttribute('data-block'));
22104         Roo.log("OOps missing block : " + 'Block' + db);
22105         return false;
22106     }
22107     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
22108     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
22109 };
22110
22111 /**
22112  * initalize all Elements from content that are 'blockable'
22113  * @static
22114  * @param the body element
22115  */
22116 Roo.htmleditor.Block.initAll = function(body, type)
22117 {
22118     if (typeof(type) == 'undefined') {
22119         var ia = Roo.htmleditor.Block.initAll;
22120         ia(body,'table');
22121         ia(body,'td');
22122         ia(body,'figure');
22123         return;
22124     }
22125     Roo.each(Roo.get(body).query(type), function(e) {
22126         Roo.htmleditor.Block.factory(e);    
22127     },this);
22128 };
22129 // question goes here... do we need to clear out this cache sometimes?
22130 // or show we make it relivant to the htmleditor.
22131 Roo.htmleditor.Block.cache = {};
22132
22133 Roo.htmleditor.Block.prototype = {
22134     
22135     node : false,
22136     
22137      // used by context menu
22138     friendly_name : 'Based Block',
22139     
22140     // text for button to delete this element
22141     deleteTitle : false,
22142     
22143     context : false,
22144     /**
22145      * Update a node with values from this object
22146      * @param {DomElement} node
22147      */
22148     updateElement : function(node)
22149     {
22150         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
22151     },
22152      /**
22153      * convert to plain HTML for calling insertAtCursor..
22154      */
22155     toHTML : function()
22156     {
22157         return Roo.DomHelper.markup(this.toObject());
22158     },
22159     /**
22160      * used by readEleemnt to extract data from a node
22161      * may need improving as it's pretty basic
22162      
22163      * @param {DomElement} node
22164      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
22165      * @param {String} attribute (use html - for contents, or style for using next param as style)
22166      * @param {String} style the style property - eg. text-align
22167      */
22168     getVal : function(node, tag, attr, style)
22169     {
22170         var n = node;
22171         if (tag !== true && n.tagName != tag.toUpperCase()) {
22172             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
22173             // but kiss for now.
22174             n = node.getElementsByTagName(tag).item(0);
22175         }
22176         if (!n) {
22177             return '';
22178         }
22179         if (attr == 'html') {
22180             return n.innerHTML;
22181         }
22182         if (attr == 'style') {
22183             return n.style[style]; 
22184         }
22185         
22186         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
22187             
22188     },
22189     /**
22190      * create a DomHelper friendly object - for use with 
22191      * Roo.DomHelper.markup / overwrite / etc..
22192      * (override this)
22193      */
22194     toObject : function()
22195     {
22196         return {};
22197     },
22198       /**
22199      * Read a node that has a 'data-block' property - and extract the values from it.
22200      * @param {DomElement} node - the node
22201      */
22202     readElement : function(node)
22203     {
22204         
22205     } 
22206     
22207     
22208 };
22209
22210  
22211
22212 /**
22213  * @class Roo.htmleditor.BlockFigure
22214  * Block that has an image and a figcaption
22215  * @cfg {String} image_src the url for the image
22216  * @cfg {String} align (left|right) alignment for the block default left
22217  * @cfg {String} caption the text to appear below  (and in the alt tag)
22218  * @cfg {String} caption_display (block|none) display or not the caption
22219  * @cfg {String|number} image_width the width of the image number or %?
22220  * @cfg {String|number} image_height the height of the image number or %?
22221  * 
22222  * @constructor
22223  * Create a new Filter.
22224  * @param {Object} config Configuration options
22225  */
22226
22227 Roo.htmleditor.BlockFigure = function(cfg)
22228 {
22229     if (cfg.node) {
22230         this.readElement(cfg.node);
22231         this.updateElement(cfg.node);
22232     }
22233     Roo.apply(this, cfg);
22234 }
22235 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
22236  
22237     
22238     // setable values.
22239     image_src: '',
22240     align: 'center',
22241     caption : '',
22242     caption_display : 'block',
22243     width : '100%',
22244     
22245     // margin: '2%', not used
22246     
22247     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
22248
22249     
22250     // used by context menu
22251     friendly_name : 'Image with caption',
22252     deleteTitle : "Delete Image and Caption",
22253     
22254     contextMenu : function(toolbar)
22255     {
22256         
22257         var block = function() {
22258             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
22259         };
22260         
22261         
22262         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
22263         
22264         var syncValue = toolbar.editorcore.syncValue;
22265         
22266         var fields = {};
22267         
22268         return [
22269              {
22270                 xtype : 'TextItem',
22271                 text : "Source: ",
22272                 xns : rooui.Toolbar  //Boostrap?
22273             },
22274             {
22275                 xtype : 'TextField',
22276                 allowBlank : false,
22277                 width : 150,
22278                 name : 'image_src',
22279                 listeners : {
22280                     keyup : function (combo, e)
22281                     { 
22282                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
22283                         var b = block();
22284                         b.image_src = this.getValue();
22285                         b.updateElement();
22286                         syncValue();
22287                         toolbar.editorcore.onEditorEvent();
22288                     }
22289                 },
22290                 xns : rooui.form
22291                 
22292             },
22293             {
22294                 xtype : 'TextItem',
22295                 text : "Width: ",
22296                 xns : rooui.Toolbar  //Boostrap?
22297             },
22298             {
22299                 xtype : 'ComboBox',
22300                 allowBlank : false,
22301                 displayField : 'val',
22302                 editable : true,
22303                 listWidth : 100,
22304                 triggerAction : 'all',
22305                 typeAhead : true,
22306                 valueField : 'val',
22307                 width : 70,
22308                 name : 'width',
22309                 listeners : {
22310                     select : function (combo, r, index)
22311                     {
22312                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
22313                         var b = block();
22314                         b.width = r.get('val');
22315                         b.updateElement();
22316                         syncValue();
22317                         toolbar.editorcore.onEditorEvent();
22318                     }
22319                 },
22320                 xns : rooui.form,
22321                 store : {
22322                     xtype : 'SimpleStore',
22323                     data : [
22324                         ['auto'],
22325                         ['50%'],
22326                         ['100%']
22327                     ],
22328                     fields : [ 'val'],
22329                     xns : Roo.data
22330                 }
22331             },
22332             {
22333                 xtype : 'TextItem',
22334                 text : "Align: ",
22335                 xns : rooui.Toolbar  //Boostrap?
22336             },
22337             {
22338                 xtype : 'ComboBox',
22339                 allowBlank : false,
22340                 displayField : 'val',
22341                 editable : true,
22342                 listWidth : 100,
22343                 triggerAction : 'all',
22344                 typeAhead : true,
22345                 valueField : 'val',
22346                 width : 70,
22347                 name : 'align',
22348                 listeners : {
22349                     select : function (combo, r, index)
22350                     {
22351                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
22352                         var b = block();
22353                         b.align = r.get('val');
22354                         b.updateElement();
22355                         syncValue();
22356                         toolbar.editorcore.onEditorEvent();
22357                     }
22358                 },
22359                 xns : rooui.form,
22360                 store : {
22361                     xtype : 'SimpleStore',
22362                     data : [
22363                         ['left'],
22364                         ['right'],
22365                         ['center']
22366                     ],
22367                     fields : [ 'val'],
22368                     xns : Roo.data
22369                 }
22370             },
22371             
22372             
22373             {
22374                 xtype : 'Button',
22375                 text: 'Hide Caption',
22376                 name : 'caption_display',
22377                 pressed : false,
22378                 enableToggle : true,
22379                 setValue : function(v) {
22380                     this.toggle(v == 'block' ? false : true);
22381                 },
22382                 listeners : {
22383                     toggle: function (btn, state)
22384                     {
22385                         var b  = block();
22386                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
22387                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
22388                         b.updateElement();
22389                         syncValue();
22390                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
22391                         toolbar.editorcore.onEditorEvent();
22392                     }
22393                 },
22394                 xns : rooui.Toolbar
22395             }
22396         ];
22397         
22398     },
22399     /**
22400      * create a DomHelper friendly object - for use with
22401      * Roo.DomHelper.markup / overwrite / etc..
22402      */
22403     toObject : function()
22404     {
22405         var d = document.createElement('div');
22406         d.innerHTML = this.caption;
22407         
22408         return  {
22409             tag: 'figure',
22410             'data-block' : 'Figure',
22411             contenteditable : 'false',
22412             style : {
22413                 display: 'block',
22414                 float :  this.align ,
22415                 'max-width':  this.width,
22416                 width : 'auto',
22417                 margin:  0,
22418                 padding: '10px'
22419                 
22420             },
22421             align : this.align,
22422             cn : [
22423                 {
22424                     tag : 'img',
22425                     src : this.image_src,
22426                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
22427                     style: {
22428                         width : 'auto',
22429                         'max-width': '100%',
22430                         margin : '0px' 
22431                         
22432                         
22433                     }
22434                 },
22435                 {
22436                     tag: 'figcaption',
22437                     contenteditable : true,
22438                     style : {
22439                         'text-align': 'left',
22440                         'margin-top' : '16px',
22441                         'font-size' : '16px',
22442                         'line-height' : '24px',
22443                         'font-style': 'italic',
22444                         display : this.caption_display
22445                     },
22446                     html : this.caption
22447                     
22448                 }
22449             ]
22450         };
22451          
22452     },
22453     
22454     readElement : function(node)
22455     {
22456         this.image_src = this.getVal(node, 'img', 'src');
22457         this.align = this.getVal(node, 'figure', 'align');
22458         this.caption = this.getVal(node, 'figcaption', 'html');
22459         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
22460         this.width = this.getVal(node, 'figure', 'style', 'max-width');
22461         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
22462         
22463     },
22464     removeNode : function()
22465     {
22466         return this.node;
22467     }
22468     
22469   
22470    
22471      
22472     
22473     
22474     
22475     
22476 })
22477
22478  
22479
22480 /**
22481  * @class Roo.htmleditor.BlockTable
22482  * Block that manages a table
22483  * 
22484  * @constructor
22485  * Create a new Filter.
22486  * @param {Object} config Configuration options
22487  */
22488
22489 Roo.htmleditor.BlockTable = function(cfg)
22490 {
22491     if (cfg.node) {
22492         this.readElement(cfg.node);
22493         this.updateElement(cfg.node);
22494     }
22495     Roo.apply(this, cfg);
22496     if (!cfg.node) {
22497         this.rows = [];
22498         for(var r = 0; r < this.no_row; r++) {
22499             this.rows[r] = [];
22500             for(var c = 0; c < this.no_col; c++) {
22501                 this.rows[r][c] = this.emptyCell();
22502             }
22503         }
22504     }
22505     
22506     
22507 }
22508 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
22509  
22510     rows : false,
22511     no_col : 1,
22512     no_row : 1,
22513     
22514     
22515     width: '100%',
22516     
22517     // used by context menu
22518     friendly_name : 'Table',
22519     deleteTitle : 'Delete Table',
22520     // context menu is drawn once..
22521     
22522     contextMenu : function(toolbar)
22523     {
22524         
22525         var block = function() {
22526             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
22527         };
22528         
22529         
22530         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
22531         
22532         var syncValue = toolbar.editorcore.syncValue;
22533         
22534         var fields = {};
22535         
22536         return [
22537             {
22538                 xtype : 'TextItem',
22539                 text : "Width: ",
22540                 xns : rooui.Toolbar  //Boostrap?
22541             },
22542             {
22543                 xtype : 'ComboBox',
22544                 allowBlank : false,
22545                 displayField : 'val',
22546                 editable : true,
22547                 listWidth : 100,
22548                 triggerAction : 'all',
22549                 typeAhead : true,
22550                 valueField : 'val',
22551                 width : 100,
22552                 name : 'width',
22553                 listeners : {
22554                     select : function (combo, r, index)
22555                     {
22556                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
22557                         var b = block();
22558                         b.width = r.get('val');
22559                         b.updateElement();
22560                         syncValue();
22561                         toolbar.editorcore.onEditorEvent();
22562                     }
22563                 },
22564                 xns : rooui.form,
22565                 store : {
22566                     xtype : 'SimpleStore',
22567                     data : [
22568                         ['100%'],
22569                         ['auto']
22570                     ],
22571                     fields : [ 'val'],
22572                     xns : Roo.data
22573                 }
22574             },
22575             // -------- Cols
22576             
22577             {
22578                 xtype : 'TextItem',
22579                 text : "Columns: ",
22580                 xns : rooui.Toolbar  //Boostrap?
22581             },
22582          
22583             {
22584                 xtype : 'Button',
22585                 text: '-',
22586                 listeners : {
22587                     click : function (_self, e)
22588                     {
22589                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
22590                         block().removeColumn();
22591                         syncValue();
22592                         toolbar.editorcore.onEditorEvent();
22593                     }
22594                 },
22595                 xns : rooui.Toolbar
22596             },
22597             {
22598                 xtype : 'Button',
22599                 text: '+',
22600                 listeners : {
22601                     click : function (_self, e)
22602                     {
22603                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
22604                         block().addColumn();
22605                         syncValue();
22606                         toolbar.editorcore.onEditorEvent();
22607                     }
22608                 },
22609                 xns : rooui.Toolbar
22610             },
22611             // -------- ROWS
22612             {
22613                 xtype : 'TextItem',
22614                 text : "Rows: ",
22615                 xns : rooui.Toolbar  //Boostrap?
22616             },
22617          
22618             {
22619                 xtype : 'Button',
22620                 text: '-',
22621                 listeners : {
22622                     click : function (_self, e)
22623                     {
22624                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
22625                         block().removeRow();
22626                         syncValue();
22627                         toolbar.editorcore.onEditorEvent();
22628                     }
22629                 },
22630                 xns : rooui.Toolbar
22631             },
22632             {
22633                 xtype : 'Button',
22634                 text: '+',
22635                 listeners : {
22636                     click : function (_self, e)
22637                     {
22638                         block().addRow();
22639                         syncValue();
22640                         toolbar.editorcore.onEditorEvent();
22641                     }
22642                 },
22643                 xns : rooui.Toolbar
22644             },
22645             // -------- ROWS
22646             {
22647                 xtype : 'Button',
22648                 text: 'Reset Column Widths',
22649                 listeners : {
22650                     
22651                     click : function (_self, e)
22652                     {
22653                         block().resetWidths();
22654                         syncValue();
22655                         toolbar.editorcore.onEditorEvent();
22656                     }
22657                 },
22658                 xns : rooui.Toolbar
22659             } 
22660             
22661             
22662             
22663         ];
22664         
22665     },
22666     
22667     
22668   /**
22669      * create a DomHelper friendly object - for use with
22670      * Roo.DomHelper.markup / overwrite / etc..
22671      * ?? should it be called with option to hide all editing features?
22672      */
22673     toObject : function()
22674     {
22675         
22676         var ret = {
22677             tag : 'table',
22678             contenteditable : 'false', // this stops cell selection from picking the table.
22679             'data-block' : 'Table',
22680             style : {
22681                 width:  this.width,
22682                 border : 'solid 1px #000', // ??? hard coded?
22683                 'border-collapse' : 'collapse' 
22684             },
22685             cn : [
22686                 { tag : 'tbody' , cn : [] }
22687             ]
22688         };
22689         
22690         // do we have a head = not really 
22691         var ncols = 0;
22692         Roo.each(this.rows, function( row ) {
22693             var tr = {
22694                 tag: 'tr',
22695                 style : {
22696                     margin: '6px',
22697                     border : 'solid 1px #000',
22698                     textAlign : 'left' 
22699                 },
22700                 cn : [ ]
22701             };
22702             
22703             ret.cn[0].cn.push(tr);
22704             // does the row have any properties? ?? height?
22705             var nc = 0;
22706             Roo.each(row, function( cell ) {
22707                 
22708                 var td = {
22709                     tag : 'td',
22710                     contenteditable :  'true',
22711                     'data-block' : 'Td',
22712                     html : cell.html,
22713                     style : cell.style
22714                 };
22715                 if (cell.colspan > 1) {
22716                     td.colspan = cell.colspan ;
22717                     nc += cell.colspan;
22718                 } else {
22719                     nc++;
22720                 }
22721                 if (cell.rowspan > 1) {
22722                     td.rowspan = cell.rowspan ;
22723                 }
22724                 
22725                 
22726                 // widths ?
22727                 tr.cn.push(td);
22728                     
22729                 
22730             }, this);
22731             ncols = Math.max(nc, ncols);
22732             
22733             
22734         }, this);
22735         // add the header row..
22736         
22737         ncols++;
22738          
22739         
22740         return ret;
22741          
22742     },
22743     
22744     readElement : function(node)
22745     {
22746         node  = node ? node : this.node ;
22747         this.width = this.getVal(node, true, 'style', 'width') || '100%';
22748         
22749         this.rows = [];
22750         this.no_row = 0;
22751         var trs = Array.from(node.rows);
22752         trs.forEach(function(tr) {
22753             var row =  [];
22754             this.rows.push(row);
22755             
22756             this.no_row++;
22757             var no_column = 0;
22758             Array.from(tr.cells).forEach(function(td) {
22759                 
22760                 var add = {
22761                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
22762                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
22763                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
22764                     html : td.innerHTML
22765                 };
22766                 no_column += add.colspan;
22767                      
22768                 
22769                 row.push(add);
22770                 
22771                 
22772             },this);
22773             this.no_col = Math.max(this.no_col, no_column);
22774             
22775             
22776         },this);
22777         
22778         
22779     },
22780     normalizeRows: function()
22781     {
22782         var ret= [];
22783         var rid = -1;
22784         this.rows.forEach(function(row) {
22785             rid++;
22786             ret[rid] = [];
22787             row = this.normalizeRow(row);
22788             var cid = 0;
22789             row.forEach(function(c) {
22790                 while (typeof(ret[rid][cid]) != 'undefined') {
22791                     cid++;
22792                 }
22793                 if (typeof(ret[rid]) == 'undefined') {
22794                     ret[rid] = [];
22795                 }
22796                 ret[rid][cid] = c;
22797                 c.row = rid;
22798                 c.col = cid;
22799                 if (c.rowspan < 2) {
22800                     return;
22801                 }
22802                 
22803                 for(var i = 1 ;i < c.rowspan; i++) {
22804                     if (typeof(ret[rid+i]) == 'undefined') {
22805                         ret[rid+i] = [];
22806                     }
22807                     ret[rid+i][cid] = c;
22808                 }
22809             });
22810         }, this);
22811         return ret;
22812     
22813     },
22814     
22815     normalizeRow: function(row)
22816     {
22817         var ret= [];
22818         row.forEach(function(c) {
22819             if (c.colspan < 2) {
22820                 ret.push(c);
22821                 return;
22822             }
22823             for(var i =0 ;i < c.colspan; i++) {
22824                 ret.push(c);
22825             }
22826         });
22827         return ret;
22828     
22829     },
22830     
22831     deleteColumn : function(sel)
22832     {
22833         if (!sel || sel.type != 'col') {
22834             return;
22835         }
22836         if (this.no_col < 2) {
22837             return;
22838         }
22839         
22840         this.rows.forEach(function(row) {
22841             var cols = this.normalizeRow(row);
22842             var col = cols[sel.col];
22843             if (col.colspan > 1) {
22844                 col.colspan --;
22845             } else {
22846                 row.remove(col);
22847             }
22848             
22849         }, this);
22850         this.no_col--;
22851         
22852     },
22853     removeColumn : function()
22854     {
22855         this.deleteColumn({
22856             type: 'col',
22857             col : this.no_col-1
22858         });
22859         this.updateElement();
22860     },
22861     
22862      
22863     addColumn : function()
22864     {
22865         
22866         this.rows.forEach(function(row) {
22867             row.push(this.emptyCell());
22868            
22869         }, this);
22870         this.updateElement();
22871     },
22872     
22873     deleteRow : function(sel)
22874     {
22875         if (!sel || sel.type != 'row') {
22876             return;
22877         }
22878         
22879         if (this.no_row < 2) {
22880             return;
22881         }
22882         
22883         var rows = this.normalizeRows();
22884         
22885         
22886         rows[sel.row].forEach(function(col) {
22887             if (col.rowspan > 1) {
22888                 col.rowspan--;
22889             } else {
22890                 col.remove = 1; // flage it as removed.
22891             }
22892             
22893         }, this);
22894         var newrows = [];
22895         this.rows.forEach(function(row) {
22896             newrow = [];
22897             row.forEach(function(c) {
22898                 if (typeof(c.remove) == 'undefined') {
22899                     newrow.push(c);
22900                 }
22901                 
22902             });
22903             if (newrow.length > 0) {
22904                 newrows.push(row);
22905             }
22906         });
22907         this.rows =  newrows;
22908         
22909         
22910         
22911         this.no_row--;
22912         this.updateElement();
22913         
22914     },
22915     removeRow : function()
22916     {
22917         this.deleteRow({
22918             type: 'row',
22919             row : this.no_row-1
22920         });
22921         
22922     },
22923     
22924      
22925     addRow : function()
22926     {
22927         
22928         var row = [];
22929         for (var i = 0; i < this.no_col; i++ ) {
22930             
22931             row.push(this.emptyCell());
22932            
22933         }
22934         this.rows.push(row);
22935         this.updateElement();
22936         
22937     },
22938      
22939     // the default cell object... at present...
22940     emptyCell : function() {
22941         return (new Roo.htmleditor.BlockTd({})).toObject();
22942         
22943      
22944     },
22945     
22946     removeNode : function()
22947     {
22948         return this.node;
22949     },
22950     
22951     
22952     
22953     resetWidths : function()
22954     {
22955         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
22956             var nn = Roo.htmleditor.Block.factory(n);
22957             nn.width = '';
22958             nn.updateElement(n);
22959         });
22960     }
22961     
22962     
22963     
22964     
22965 })
22966
22967 /**
22968  *
22969  * editing a TD?
22970  *
22971  * since selections really work on the table cell, then editing really should work from there
22972  *
22973  * The original plan was to support merging etc... - but that may not be needed yet..
22974  *
22975  * So this simple version will support:
22976  *   add/remove cols
22977  *   adjust the width +/-
22978  *   reset the width...
22979  *   
22980  *
22981  */
22982
22983
22984  
22985
22986 /**
22987  * @class Roo.htmleditor.BlockTable
22988  * Block that manages a table
22989  * 
22990  * @constructor
22991  * Create a new Filter.
22992  * @param {Object} config Configuration options
22993  */
22994
22995 Roo.htmleditor.BlockTd = function(cfg)
22996 {
22997     if (cfg.node) {
22998         this.readElement(cfg.node);
22999         this.updateElement(cfg.node);
23000     }
23001     Roo.apply(this, cfg);
23002      
23003     
23004     
23005 }
23006 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
23007  
23008     node : false,
23009     
23010     width: '',
23011     textAlign : 'left',
23012     valign : 'top',
23013     
23014     colspan : 1,
23015     rowspan : 1,
23016     
23017     
23018     // used by context menu
23019     friendly_name : 'Table Cell',
23020     deleteTitle : false, // use our customer delete
23021     
23022     // context menu is drawn once..
23023     
23024     contextMenu : function(toolbar)
23025     {
23026         
23027         var cell = function() {
23028             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23029         };
23030         
23031         var table = function() {
23032             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
23033         };
23034         
23035         var lr = false;
23036         var saveSel = function()
23037         {
23038             lr = toolbar.editorcore.getSelection().getRangeAt(0);
23039         }
23040         var restoreSel = function()
23041         {
23042             if (lr) {
23043                 (function() {
23044                     toolbar.editorcore.focus();
23045                     var cr = toolbar.editorcore.getSelection();
23046                     cr.removeAllRanges();
23047                     cr.addRange(lr);
23048                     toolbar.editorcore.onEditorEvent();
23049                 }).defer(10, this);
23050                 
23051                 
23052             }
23053         }
23054         
23055         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23056         
23057         var syncValue = toolbar.editorcore.syncValue;
23058         
23059         var fields = {};
23060         
23061         return [
23062             {
23063                 xtype : 'Button',
23064                 text : 'Edit Table',
23065                 listeners : {
23066                     click : function() {
23067                         var t = toolbar.tb.selectedNode.closest('table');
23068                         toolbar.editorcore.selectNode(t);
23069                         toolbar.editorcore.onEditorEvent();                        
23070                     }
23071                 }
23072                 
23073             },
23074               
23075            
23076              
23077             {
23078                 xtype : 'TextItem',
23079                 text : "Column Width: ",
23080                  xns : rooui.Toolbar 
23081                
23082             },
23083             {
23084                 xtype : 'Button',
23085                 text: '-',
23086                 listeners : {
23087                     click : function (_self, e)
23088                     {
23089                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23090                         cell().shrinkColumn();
23091                         syncValue();
23092                          toolbar.editorcore.onEditorEvent();
23093                     }
23094                 },
23095                 xns : rooui.Toolbar
23096             },
23097             {
23098                 xtype : 'Button',
23099                 text: '+',
23100                 listeners : {
23101                     click : function (_self, e)
23102                     {
23103                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23104                         cell().growColumn();
23105                         syncValue();
23106                         toolbar.editorcore.onEditorEvent();
23107                     }
23108                 },
23109                 xns : rooui.Toolbar
23110             },
23111             
23112             {
23113                 xtype : 'TextItem',
23114                 text : "Vertical Align: ",
23115                 xns : rooui.Toolbar  //Boostrap?
23116             },
23117             {
23118                 xtype : 'ComboBox',
23119                 allowBlank : false,
23120                 displayField : 'val',
23121                 editable : true,
23122                 listWidth : 100,
23123                 triggerAction : 'all',
23124                 typeAhead : true,
23125                 valueField : 'val',
23126                 width : 100,
23127                 name : 'valign',
23128                 listeners : {
23129                     select : function (combo, r, index)
23130                     {
23131                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23132                         var b = cell();
23133                         b.valign = r.get('val');
23134                         b.updateElement();
23135                         syncValue();
23136                         toolbar.editorcore.onEditorEvent();
23137                     }
23138                 },
23139                 xns : rooui.form,
23140                 store : {
23141                     xtype : 'SimpleStore',
23142                     data : [
23143                         ['top'],
23144                         ['middle'],
23145                         ['bottom'] // there are afew more... 
23146                     ],
23147                     fields : [ 'val'],
23148                     xns : Roo.data
23149                 }
23150             },
23151             
23152             {
23153                 xtype : 'TextItem',
23154                 text : "Merge Cells: ",
23155                  xns : rooui.Toolbar 
23156                
23157             },
23158             
23159             
23160             {
23161                 xtype : 'Button',
23162                 text: 'Right',
23163                 listeners : {
23164                     click : function (_self, e)
23165                     {
23166                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23167                         cell().mergeRight();
23168                         //block().growColumn();
23169                         syncValue();
23170                         toolbar.editorcore.onEditorEvent();
23171                     }
23172                 },
23173                 xns : rooui.Toolbar
23174             },
23175              
23176             {
23177                 xtype : 'Button',
23178                 text: 'Below',
23179                 listeners : {
23180                     click : function (_self, e)
23181                     {
23182                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23183                         cell().mergeBelow();
23184                         //block().growColumn();
23185                         syncValue();
23186                         toolbar.editorcore.onEditorEvent();
23187                     }
23188                 },
23189                 xns : rooui.Toolbar
23190             },
23191             {
23192                 xtype : 'TextItem',
23193                 text : "| ",
23194                  xns : rooui.Toolbar 
23195                
23196             },
23197             
23198             {
23199                 xtype : 'Button',
23200                 text: 'Split',
23201                 listeners : {
23202                     click : function (_self, e)
23203                     {
23204                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23205                         cell().split();
23206                         syncValue();
23207                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23208                         toolbar.editorcore.onEditorEvent();
23209                                              
23210                     }
23211                 },
23212                 xns : rooui.Toolbar
23213             },
23214             {
23215                 xtype : 'Fill',
23216                 xns : rooui.Toolbar 
23217                
23218             },
23219         
23220           
23221             {
23222                 xtype : 'Button',
23223                 text: 'Delete',
23224                  
23225                 xns : rooui.Toolbar,
23226                 menu : {
23227                     xtype : 'Menu',
23228                     xns : rooui.menu,
23229                     items : [
23230                         {
23231                             xtype : 'Item',
23232                             html: 'Column',
23233                             listeners : {
23234                                 click : function (_self, e)
23235                                 {
23236                                     var t = table();
23237                                     
23238                                     cell().deleteColumn();
23239                                     syncValue();
23240                                     toolbar.editorcore.selectNode(t.node);
23241                                     toolbar.editorcore.onEditorEvent();   
23242                                 }
23243                             },
23244                             xns : rooui.menu
23245                         },
23246                         {
23247                             xtype : 'Item',
23248                             html: 'Row',
23249                             listeners : {
23250                                 click : function (_self, e)
23251                                 {
23252                                     var t = table();
23253                                     cell().deleteRow();
23254                                     syncValue();
23255                                     
23256                                     toolbar.editorcore.selectNode(t.node);
23257                                     toolbar.editorcore.onEditorEvent();   
23258                                                          
23259                                 }
23260                             },
23261                             xns : rooui.menu
23262                         },
23263                        {
23264                             xtype : 'Separator',
23265                             xns : rooui.menu
23266                         },
23267                         {
23268                             xtype : 'Item',
23269                             html: 'Table',
23270                             listeners : {
23271                                 click : function (_self, e)
23272                                 {
23273                                     var t = table();
23274                                     var nn = t.node.nextSibling || t.node.previousSibling;
23275                                     t.node.parentNode.removeChild(t.node);
23276                                     if (nn) { 
23277                                         toolbar.editorcore.selectNode(nn, true);
23278                                     }
23279                                     toolbar.editorcore.onEditorEvent();   
23280                                                          
23281                                 }
23282                             },
23283                             xns : rooui.menu
23284                         }
23285                     ]
23286                 }
23287             }
23288             
23289             // align... << fixme
23290             
23291         ];
23292         
23293     },
23294     
23295     
23296   /**
23297      * create a DomHelper friendly object - for use with
23298      * Roo.DomHelper.markup / overwrite / etc..
23299      * ?? should it be called with option to hide all editing features?
23300      */
23301  /**
23302      * create a DomHelper friendly object - for use with
23303      * Roo.DomHelper.markup / overwrite / etc..
23304      * ?? should it be called with option to hide all editing features?
23305      */
23306     toObject : function()
23307     {
23308         
23309         var ret = {
23310             tag : 'td',
23311             contenteditable : 'true', // this stops cell selection from picking the table.
23312             'data-block' : 'Td',
23313             valign : this.valign,
23314             style : {  
23315                 'text-align' :  this.textAlign,
23316                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
23317                 'border-collapse' : 'collapse',
23318                 padding : '6px', // 8 for desktop / 4 for mobile
23319                 'vertical-align': this.valign
23320             },
23321             html : this.html
23322         };
23323         if (this.width != '') {
23324             ret.width = this.width;
23325             ret.style.width = this.width;
23326         }
23327         
23328         
23329         if (this.colspan > 1) {
23330             ret.colspan = this.colspan ;
23331         } 
23332         if (this.rowspan > 1) {
23333             ret.rowspan = this.rowspan ;
23334         }
23335         
23336            
23337         
23338         return ret;
23339          
23340     },
23341     
23342     readElement : function(node)
23343     {
23344         node  = node ? node : this.node ;
23345         this.width = node.style.width;
23346         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
23347         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
23348         this.html = node.innerHTML;
23349         
23350         
23351     },
23352      
23353     // the default cell object... at present...
23354     emptyCell : function() {
23355         return {
23356             colspan :  1,
23357             rowspan :  1,
23358             textAlign : 'left',
23359             html : "&nbsp;" // is this going to be editable now?
23360         };
23361      
23362     },
23363     
23364     removeNode : function()
23365     {
23366         return this.node.closest('table');
23367          
23368     },
23369     
23370     cellData : false,
23371     
23372     colWidths : false,
23373     
23374     toTableArray  : function()
23375     {
23376         var ret = [];
23377         var tab = this.node.closest('tr').closest('table');
23378         Array.from(tab.rows).forEach(function(r, ri){
23379             ret[ri] = [];
23380         });
23381         var rn = 0;
23382         this.colWidths = [];
23383         var all_auto = true;
23384         Array.from(tab.rows).forEach(function(r, ri){
23385             
23386             var cn = 0;
23387             Array.from(r.cells).forEach(function(ce, ci){
23388                 var c =  {
23389                     cell : ce,
23390                     row : rn,
23391                     col: cn,
23392                     colspan : ce.colSpan,
23393                     rowspan : ce.rowSpan
23394                 };
23395                 if (ce.isEqualNode(this.node)) {
23396                     this.cellData = c;
23397                 }
23398                 // if we have been filled up by a row?
23399                 if (typeof(ret[rn][cn]) != 'undefined') {
23400                     while(typeof(ret[rn][cn]) != 'undefined') {
23401                         cn++;
23402                     }
23403                     c.col = cn;
23404                 }
23405                 
23406                 if (typeof(this.colWidths[cn]) == 'undefined') {
23407                     this.colWidths[cn] =   ce.style.width;
23408                     if (this.colWidths[cn] != '') {
23409                         all_auto = false;
23410                     }
23411                 }
23412                 
23413                 
23414                 if (c.colspan < 2 && c.rowspan < 2 ) {
23415                     ret[rn][cn] = c;
23416                     cn++;
23417                     return;
23418                 }
23419                 for(var j = 0; j < c.rowspan; j++) {
23420                     if (typeof(ret[rn+j]) == 'undefined') {
23421                         continue; // we have a problem..
23422                     }
23423                     ret[rn+j][cn] = c;
23424                     for(var i = 0; i < c.colspan; i++) {
23425                         ret[rn+j][cn+i] = c;
23426                     }
23427                 }
23428                 
23429                 cn += c.colspan;
23430             }, this);
23431             rn++;
23432         }, this);
23433         
23434         // initalize widths.?
23435         // either all widths or no widths..
23436         if (all_auto) {
23437             this.colWidths[0] = false; // no widths flag.
23438         }
23439         
23440         
23441         return ret;
23442         
23443     },
23444     
23445     
23446     
23447     
23448     mergeRight: function()
23449     {
23450          
23451         // get the contents of the next cell along..
23452         var tr = this.node.closest('tr');
23453         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
23454         if (i >= tr.childNodes.length - 1) {
23455             return; // no cells on right to merge with.
23456         }
23457         var table = this.toTableArray();
23458         
23459         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
23460             return; // nothing right?
23461         }
23462         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
23463         // right cell - must be same rowspan and on the same row.
23464         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
23465             return; // right hand side is not same rowspan.
23466         }
23467         
23468         
23469         
23470         this.node.innerHTML += ' ' + rc.cell.innerHTML;
23471         tr.removeChild(rc.cell);
23472         this.colspan += rc.colspan;
23473         this.node.setAttribute('colspan', this.colspan);
23474
23475     },
23476     
23477     
23478     mergeBelow : function()
23479     {
23480         var table = this.toTableArray();
23481         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
23482             return; // no row below
23483         }
23484         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
23485             return; // nothing right?
23486         }
23487         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
23488         
23489         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
23490             return; // right hand side is not same rowspan.
23491         }
23492         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
23493         rc.cell.parentNode.removeChild(rc.cell);
23494         this.rowspan += rc.rowspan;
23495         this.node.setAttribute('rowspan', this.rowspan);
23496     },
23497     
23498     split: function()
23499     {
23500         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
23501             return;
23502         }
23503         var table = this.toTableArray();
23504         var cd = this.cellData;
23505         this.rowspan = 1;
23506         this.colspan = 1;
23507         
23508         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
23509             
23510             
23511             
23512             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
23513                 if (r == cd.row && c == cd.col) {
23514                     this.node.removeAttribute('rowspan');
23515                     this.node.removeAttribute('colspan');
23516                     continue;
23517                 }
23518                  
23519                 var ntd = this.node.cloneNode(); // which col/row should be 0..
23520                 ntd.removeAttribute('id'); //
23521                 //ntd.style.width  = '';
23522                 ntd.innerHTML = '';
23523                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
23524             }
23525             
23526         }
23527         this.redrawAllCells(table);
23528         
23529          
23530         
23531     },
23532     
23533     
23534     
23535     redrawAllCells: function(table)
23536     {
23537         
23538          
23539         var tab = this.node.closest('tr').closest('table');
23540         var ctr = tab.rows[0].parentNode;
23541         Array.from(tab.rows).forEach(function(r, ri){
23542             
23543             Array.from(r.cells).forEach(function(ce, ci){
23544                 ce.parentNode.removeChild(ce);
23545             });
23546             r.parentNode.removeChild(r);
23547         });
23548         for(var r = 0 ; r < table.length; r++) {
23549             var re = tab.rows[r];
23550             
23551             var re = tab.ownerDocument.createElement('tr');
23552             ctr.appendChild(re);
23553             for(var c = 0 ; c < table[r].length; c++) {
23554                 if (table[r][c].cell === false) {
23555                     continue;
23556                 }
23557                 
23558                 re.appendChild(table[r][c].cell);
23559                  
23560                 table[r][c].cell = false;
23561             }
23562         }
23563         
23564     },
23565     updateWidths : function(table)
23566     {
23567         for(var r = 0 ; r < table.length; r++) {
23568            
23569             for(var c = 0 ; c < table[r].length; c++) {
23570                 if (table[r][c].cell === false) {
23571                     continue;
23572                 }
23573                 
23574                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
23575                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
23576                     el.width = Math.floor(this.colWidths[c])  +'%';
23577                     el.updateElement(el.node);
23578                 }
23579                 table[r][c].cell = false; // done
23580             }
23581         }
23582     },
23583     normalizeWidths : function(table)
23584     {
23585     
23586         if (this.colWidths[0] === false) {
23587             var nw = 100.0 / this.colWidths.length;
23588             this.colWidths.forEach(function(w,i) {
23589                 this.colWidths[i] = nw;
23590             },this);
23591             return;
23592         }
23593     
23594         var t = 0, missing = [];
23595         
23596         this.colWidths.forEach(function(w,i) {
23597             //if you mix % and
23598             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
23599             var add =  this.colWidths[i];
23600             if (add > 0) {
23601                 t+=add;
23602                 return;
23603             }
23604             missing.push(i);
23605             
23606             
23607         },this);
23608         var nc = this.colWidths.length;
23609         if (missing.length) {
23610             var mult = (nc - missing.length) / (1.0 * nc);
23611             var t = mult * t;
23612             var ew = (100 -t) / (1.0 * missing.length);
23613             this.colWidths.forEach(function(w,i) {
23614                 if (w > 0) {
23615                     this.colWidths[i] = w * mult;
23616                     return;
23617                 }
23618                 
23619                 this.colWidths[i] = ew;
23620             }, this);
23621             // have to make up numbers..
23622              
23623         }
23624         // now we should have all the widths..
23625         
23626     
23627     },
23628     
23629     shrinkColumn : function()
23630     {
23631         var table = this.toTableArray();
23632         this.normalizeWidths(table);
23633         var col = this.cellData.col;
23634         var nw = this.colWidths[col] * 0.8;
23635         if (nw < 5) {
23636             return;
23637         }
23638         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
23639         this.colWidths.forEach(function(w,i) {
23640             if (i == col) {
23641                  this.colWidths[i] = nw;
23642                 return;
23643             }
23644             this.colWidths[i] += otherAdd
23645         }, this);
23646         this.updateWidths(table);
23647          
23648     },
23649     growColumn : function()
23650     {
23651         var table = this.toTableArray();
23652         this.normalizeWidths(table);
23653         var col = this.cellData.col;
23654         var nw = this.colWidths[col] * 1.2;
23655         if (nw > 90) {
23656             return;
23657         }
23658         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
23659         this.colWidths.forEach(function(w,i) {
23660             if (i == col) {
23661                 this.colWidths[i] = nw;
23662                 return;
23663             }
23664             this.colWidths[i] -= otherSub
23665         }, this);
23666         this.updateWidths(table);
23667          
23668     },
23669     deleteRow : function()
23670     {
23671         // delete this rows 'tr'
23672         // if any of the cells in this row have a rowspan > 1 && row!= this row..
23673         // then reduce the rowspan.
23674         var table = this.toTableArray();
23675         // this.cellData.row;
23676         for (var i =0;i< table[this.cellData.row].length ; i++) {
23677             var c = table[this.cellData.row][i];
23678             if (c.row != this.cellData.row) {
23679                 
23680                 c.rowspan--;
23681                 c.cell.setAttribute('rowspan', c.rowspan);
23682                 continue;
23683             }
23684             if (c.rowspan > 1) {
23685                 c.rowspan--;
23686                 c.cell.setAttribute('rowspan', c.rowspan);
23687             }
23688         }
23689         table.splice(this.cellData.row,1);
23690         this.redrawAllCells(table);
23691         
23692     },
23693     deleteColumn : function()
23694     {
23695         var table = this.toTableArray();
23696         
23697         for (var i =0;i< table.length ; i++) {
23698             var c = table[i][this.cellData.col];
23699             if (c.col != this.cellData.col) {
23700                 table[i][this.cellData.col].colspan--;
23701             } else if (c.colspan > 1) {
23702                 c.colspan--;
23703                 c.cell.setAttribute('colspan', c.colspan);
23704             }
23705             table[i].splice(this.cellData.col,1);
23706         }
23707         
23708         this.redrawAllCells(table);
23709     }
23710     
23711     
23712     
23713     
23714 })
23715
23716 //<script type="text/javascript">
23717
23718 /*
23719  * Based  Ext JS Library 1.1.1
23720  * Copyright(c) 2006-2007, Ext JS, LLC.
23721  * LGPL
23722  *
23723  */
23724  
23725 /**
23726  * @class Roo.HtmlEditorCore
23727  * @extends Roo.Component
23728  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23729  *
23730  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23731  */
23732
23733 Roo.HtmlEditorCore = function(config){
23734     
23735     
23736     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23737     
23738     
23739     this.addEvents({
23740         /**
23741          * @event initialize
23742          * Fires when the editor is fully initialized (including the iframe)
23743          * @param {Roo.HtmlEditorCore} this
23744          */
23745         initialize: true,
23746         /**
23747          * @event activate
23748          * Fires when the editor is first receives the focus. Any insertion must wait
23749          * until after this event.
23750          * @param {Roo.HtmlEditorCore} this
23751          */
23752         activate: true,
23753          /**
23754          * @event beforesync
23755          * Fires before the textarea is updated with content from the editor iframe. Return false
23756          * to cancel the sync.
23757          * @param {Roo.HtmlEditorCore} this
23758          * @param {String} html
23759          */
23760         beforesync: true,
23761          /**
23762          * @event beforepush
23763          * Fires before the iframe editor is updated with content from the textarea. Return false
23764          * to cancel the push.
23765          * @param {Roo.HtmlEditorCore} this
23766          * @param {String} html
23767          */
23768         beforepush: true,
23769          /**
23770          * @event sync
23771          * Fires when the textarea is updated with content from the editor iframe.
23772          * @param {Roo.HtmlEditorCore} this
23773          * @param {String} html
23774          */
23775         sync: true,
23776          /**
23777          * @event push
23778          * Fires when the iframe editor is updated with content from the textarea.
23779          * @param {Roo.HtmlEditorCore} this
23780          * @param {String} html
23781          */
23782         push: true,
23783         
23784         /**
23785          * @event editorevent
23786          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23787          * @param {Roo.HtmlEditorCore} this
23788          */
23789         editorevent: true 
23790          
23791         
23792     });
23793     
23794     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23795     
23796     // defaults : white / black...
23797     this.applyBlacklists();
23798     
23799     
23800     
23801 };
23802
23803
23804 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23805
23806
23807      /**
23808      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23809      */
23810     
23811     owner : false,
23812     
23813      /**
23814      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23815      *                        Roo.resizable.
23816      */
23817     resizable : false,
23818      /**
23819      * @cfg {Number} height (in pixels)
23820      */   
23821     height: 300,
23822    /**
23823      * @cfg {Number} width (in pixels)
23824      */   
23825     width: 500,
23826      /**
23827      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
23828      *         if you are doing an email editor, this probably needs disabling, it's designed
23829      */
23830     autoClean: true,
23831     
23832     /**
23833      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
23834      */
23835     enableBlocks : true,
23836     /**
23837      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23838      * 
23839      */
23840     stylesheets: false,
23841      /**
23842      * @cfg {String} language default en - language of text (usefull for rtl languages)
23843      * 
23844      */
23845     language: 'en',
23846     
23847     /**
23848      * @cfg {boolean} allowComments - default false - allow comments in HTML source
23849      *          - by default they are stripped - if you are editing email you may need this.
23850      */
23851     allowComments: false,
23852     // id of frame..
23853     frameId: false,
23854     
23855     // private properties
23856     validationEvent : false,
23857     deferHeight: true,
23858     initialized : false,
23859     activated : false,
23860     sourceEditMode : false,
23861     onFocus : Roo.emptyFn,
23862     iframePad:3,
23863     hideMode:'offsets',
23864     
23865     clearUp: true,
23866     
23867     // blacklist + whitelisted elements..
23868     black: false,
23869     white: false,
23870      
23871     bodyCls : '',
23872
23873     
23874     undoManager : false,
23875     /**
23876      * Protected method that will not generally be called directly. It
23877      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23878      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23879      */
23880     getDocMarkup : function(){
23881         // body styles..
23882         var st = '';
23883         
23884         // inherit styels from page...?? 
23885         if (this.stylesheets === false) {
23886             
23887             Roo.get(document.head).select('style').each(function(node) {
23888                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23889             });
23890             
23891             Roo.get(document.head).select('link').each(function(node) { 
23892                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23893             });
23894             
23895         } else if (!this.stylesheets.length) {
23896                 // simple..
23897                 st = '<style type="text/css">' +
23898                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23899                    '</style>';
23900         } else {
23901             for (var i in this.stylesheets) {
23902                 if (typeof(this.stylesheets[i]) != 'string') {
23903                     continue;
23904                 }
23905                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23906             }
23907             
23908         }
23909         
23910         st +=  '<style type="text/css">' +
23911             'IMG { cursor: pointer } ' +
23912         '</style>';
23913
23914         var cls = 'roo-htmleditor-body';
23915         
23916         if(this.bodyCls.length){
23917             cls += ' ' + this.bodyCls;
23918         }
23919         
23920         return '<html><head>' + st  +
23921             //<style type="text/css">' +
23922             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23923             //'</style>' +
23924             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23925     },
23926
23927     // private
23928     onRender : function(ct, position)
23929     {
23930         var _t = this;
23931         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23932         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23933         
23934         
23935         this.el.dom.style.border = '0 none';
23936         this.el.dom.setAttribute('tabIndex', -1);
23937         this.el.addClass('x-hidden hide');
23938         
23939         
23940         
23941         if(Roo.isIE){ // fix IE 1px bogus margin
23942             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23943         }
23944        
23945         
23946         this.frameId = Roo.id();
23947         
23948          
23949         
23950         var iframe = this.owner.wrap.createChild({
23951             tag: 'iframe',
23952             cls: 'form-control', // bootstrap..
23953             id: this.frameId,
23954             name: this.frameId,
23955             frameBorder : 'no',
23956             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23957         }, this.el
23958         );
23959         
23960         
23961         this.iframe = iframe.dom;
23962
23963         this.assignDocWin();
23964         
23965         this.doc.designMode = 'on';
23966        
23967         this.doc.open();
23968         this.doc.write(this.getDocMarkup());
23969         this.doc.close();
23970
23971         
23972         var task = { // must defer to wait for browser to be ready
23973             run : function(){
23974                 //console.log("run task?" + this.doc.readyState);
23975                 this.assignDocWin();
23976                 if(this.doc.body || this.doc.readyState == 'complete'){
23977                     try {
23978                         this.doc.designMode="on";
23979                         
23980                     } catch (e) {
23981                         return;
23982                     }
23983                     Roo.TaskMgr.stop(task);
23984                     this.initEditor.defer(10, this);
23985                 }
23986             },
23987             interval : 10,
23988             duration: 10000,
23989             scope: this
23990         };
23991         Roo.TaskMgr.start(task);
23992
23993     },
23994
23995     // private
23996     onResize : function(w, h)
23997     {
23998          Roo.log('resize: ' +w + ',' + h );
23999         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24000         if(!this.iframe){
24001             return;
24002         }
24003         if(typeof w == 'number'){
24004             
24005             this.iframe.style.width = w + 'px';
24006         }
24007         if(typeof h == 'number'){
24008             
24009             this.iframe.style.height = h + 'px';
24010             if(this.doc){
24011                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24012             }
24013         }
24014         
24015     },
24016
24017     /**
24018      * Toggles the editor between standard and source edit mode.
24019      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24020      */
24021     toggleSourceEdit : function(sourceEditMode){
24022         
24023         this.sourceEditMode = sourceEditMode === true;
24024         
24025         if(this.sourceEditMode){
24026  
24027             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
24028             
24029         }else{
24030             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
24031             //this.iframe.className = '';
24032             this.deferFocus();
24033         }
24034         //this.setSize(this.owner.wrap.getSize());
24035         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24036     },
24037
24038     
24039   
24040
24041     /**
24042      * Protected method that will not generally be called directly. If you need/want
24043      * custom HTML cleanup, this is the method you should override.
24044      * @param {String} html The HTML to be cleaned
24045      * return {String} The cleaned HTML
24046      */
24047     cleanHtml : function(html){
24048         html = String(html);
24049         if(html.length > 5){
24050             if(Roo.isSafari){ // strip safari nonsense
24051                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24052             }
24053         }
24054         if(html == '&nbsp;'){
24055             html = '';
24056         }
24057         return html;
24058     },
24059
24060     /**
24061      * HTML Editor -> Textarea
24062      * Protected method that will not generally be called directly. Syncs the contents
24063      * of the editor iframe with the textarea.
24064      */
24065     syncValue : function()
24066     {
24067         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
24068         if(this.initialized){
24069             
24070             this.undoManager.addEvent();
24071
24072             
24073             var bd = (this.doc.body || this.doc.documentElement);
24074            
24075             
24076             
24077             var div = document.createElement('div');
24078             div.innerHTML = bd.innerHTML;
24079              
24080            
24081             if (this.enableBlocks) {
24082                 new Roo.htmleditor.FilterBlock({ node : div });
24083             }
24084             //?? tidy?
24085             
24086             
24087             var html = div.innerHTML;
24088             if(Roo.isSafari){
24089                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24090                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24091                 if(m && m[1]){
24092                     html = '<div style="'+m[0]+'">' + html + '</div>';
24093                 }
24094             }
24095             html = this.cleanHtml(html);
24096             // fix up the special chars.. normaly like back quotes in word...
24097             // however we do not want to do this with chinese..
24098             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24099                 
24100                 var cc = match.charCodeAt();
24101
24102                 // Get the character value, handling surrogate pairs
24103                 if (match.length == 2) {
24104                     // It's a surrogate pair, calculate the Unicode code point
24105                     var high = match.charCodeAt(0) - 0xD800;
24106                     var low  = match.charCodeAt(1) - 0xDC00;
24107                     cc = (high * 0x400) + low + 0x10000;
24108                 }  else if (
24109                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24110                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24111                     (cc >= 0xf900 && cc < 0xfb00 )
24112                 ) {
24113                         return match;
24114                 }  
24115          
24116                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24117                 return "&#" + cc + ";";
24118                 
24119                 
24120             });
24121             
24122             
24123              
24124             if(this.owner.fireEvent('beforesync', this, html) !== false){
24125                 this.el.dom.value = html;
24126                 this.owner.fireEvent('sync', this, html);
24127             }
24128         }
24129     },
24130
24131     /**
24132      * TEXTAREA -> EDITABLE
24133      * Protected method that will not generally be called directly. Pushes the value of the textarea
24134      * into the iframe editor.
24135      */
24136     pushValue : function()
24137     {
24138         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
24139         if(this.initialized){
24140             var v = this.el.dom.value.trim();
24141             
24142             
24143             if(this.owner.fireEvent('beforepush', this, v) !== false){
24144                 var d = (this.doc.body || this.doc.documentElement);
24145                 d.innerHTML = v;
24146                  
24147                 this.el.dom.value = d.innerHTML;
24148                 this.owner.fireEvent('push', this, v);
24149             }
24150             if (this.autoClean) {
24151                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
24152                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
24153             }
24154             
24155             Roo.htmleditor.Block.initAll(this.doc.body);
24156             this.updateLanguage();
24157             
24158             var lc = this.doc.body.lastChild;
24159             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
24160                 // add an extra line at the end.
24161                 this.doc.body.appendChild(this.doc.createElement('br'));
24162             }
24163             
24164             
24165         }
24166     },
24167
24168     // private
24169     deferFocus : function(){
24170         this.focus.defer(10, this);
24171     },
24172
24173     // doc'ed in Field
24174     focus : function(){
24175         if(this.win && !this.sourceEditMode){
24176             this.win.focus();
24177         }else{
24178             this.el.focus();
24179         }
24180     },
24181     
24182     assignDocWin: function()
24183     {
24184         var iframe = this.iframe;
24185         
24186          if(Roo.isIE){
24187             this.doc = iframe.contentWindow.document;
24188             this.win = iframe.contentWindow;
24189         } else {
24190 //            if (!Roo.get(this.frameId)) {
24191 //                return;
24192 //            }
24193 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24194 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24195             
24196             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24197                 return;
24198             }
24199             
24200             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24201             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24202         }
24203     },
24204     
24205     // private
24206     initEditor : function(){
24207         //console.log("INIT EDITOR");
24208         this.assignDocWin();
24209         
24210         
24211         
24212         this.doc.designMode="on";
24213         this.doc.open();
24214         this.doc.write(this.getDocMarkup());
24215         this.doc.close();
24216         
24217         var dbody = (this.doc.body || this.doc.documentElement);
24218         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24219         // this copies styles from the containing element into thsi one..
24220         // not sure why we need all of this..
24221         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24222         
24223         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24224         //ss['background-attachment'] = 'fixed'; // w3c
24225         dbody.bgProperties = 'fixed'; // ie
24226         //Roo.DomHelper.applyStyles(dbody, ss);
24227         Roo.EventManager.on(this.doc, {
24228             //'mousedown': this.onEditorEvent,
24229             'mouseup': this.onEditorEvent,
24230             'dblclick': this.onEditorEvent,
24231             'click': this.onEditorEvent,
24232             'keyup': this.onEditorEvent,
24233             
24234             buffer:100,
24235             scope: this
24236         });
24237         Roo.EventManager.on(this.doc, {
24238             'paste': this.onPasteEvent,
24239             scope : this
24240         });
24241         if(Roo.isGecko){
24242             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24243         }
24244         //??? needed???
24245         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24246             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24247         }
24248         this.initialized = true;
24249
24250         
24251         // initialize special key events - enter
24252         new Roo.htmleditor.KeyEnter({core : this});
24253         
24254          
24255         
24256         this.owner.fireEvent('initialize', this);
24257         this.pushValue();
24258     },
24259     
24260     onPasteEvent : function(e,v)
24261     {
24262         // I think we better assume paste is going to be a dirty load of rubish from word..
24263         
24264         // even pasting into a 'email version' of this widget will have to clean up that mess.
24265         var cd = (e.browserEvent.clipboardData || window.clipboardData);
24266         
24267         // check what type of paste - if it's an image, then handle it differently.
24268         if (cd.files.length > 0) {
24269             // pasting images?
24270             var urlAPI = (window.createObjectURL && window) || 
24271                 (window.URL && URL.revokeObjectURL && URL) || 
24272                 (window.webkitURL && webkitURL);
24273     
24274             var url = urlAPI.createObjectURL( cd.files[0]);
24275             this.insertAtCursor('<img src=" + url + ">');
24276             return false;
24277         }
24278         
24279         var html = cd.getData('text/html'); // clipboard event
24280         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
24281         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
24282         Roo.log(images);
24283         //Roo.log(imgs);
24284         // fixme..
24285         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
24286                        .map(function(g) { return g.toDataURL(); });
24287         
24288         
24289         html = this.cleanWordChars(html);
24290         
24291         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
24292         
24293         
24294         var sn = this.getParentElement();
24295         // check if d contains a table, and prevent nesting??
24296         //Roo.log(d.getElementsByTagName('table'));
24297         //Roo.log(sn);
24298         //Roo.log(sn.closest('table'));
24299         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
24300             e.preventDefault();
24301             this.insertAtCursor("You can not nest tables");
24302             //Roo.log("prevent?"); // fixme - 
24303             return false;
24304         }
24305         
24306         if (images.length > 0) {
24307             Roo.each(d.getElementsByTagName('img'), function(img, i) {
24308                 img.setAttribute('src', images[i]);
24309             });
24310         }
24311         if (this.autoClean) {
24312             new Roo.htmleditor.FilterStyleToTag({ node : d });
24313             new Roo.htmleditor.FilterAttributes({
24314                 node : d,
24315                 attrib_white : ['href', 'src', 'name', 'align'],
24316                 attrib_clean : ['href', 'src' ] 
24317             });
24318             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
24319             // should be fonts..
24320             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
24321             new Roo.htmleditor.FilterParagraph({ node : d });
24322             new Roo.htmleditor.FilterSpan({ node : d });
24323             new Roo.htmleditor.FilterLongBr({ node : d });
24324         }
24325         if (this.enableBlocks) {
24326                 
24327             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
24328                 if (img.closest('figure')) { // assume!! that it's aready
24329                     return;
24330                 }
24331                 var fig  = new Roo.htmleditor.BlockFigure({
24332                     image_src  : img.src
24333                 });
24334                 fig.updateElement(img); // replace it..
24335                 
24336             });
24337         }
24338         
24339         
24340         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
24341         if (this.enableBlocks) {
24342             Roo.htmleditor.Block.initAll(this.doc.body);
24343         }
24344         
24345         
24346         e.preventDefault();
24347         return false;
24348         // default behaveiour should be our local cleanup paste? (optional?)
24349         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
24350         //this.owner.fireEvent('paste', e, v);
24351     },
24352     // private
24353     onDestroy : function(){
24354         
24355         
24356         
24357         if(this.rendered){
24358             
24359             //for (var i =0; i < this.toolbars.length;i++) {
24360             //    // fixme - ask toolbars for heights?
24361             //    this.toolbars[i].onDestroy();
24362            // }
24363             
24364             //this.wrap.dom.innerHTML = '';
24365             //this.wrap.remove();
24366         }
24367     },
24368
24369     // private
24370     onFirstFocus : function(){
24371         
24372         this.assignDocWin();
24373         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
24374         
24375         this.activated = true;
24376          
24377     
24378         if(Roo.isGecko){ // prevent silly gecko errors
24379             this.win.focus();
24380             var s = this.win.getSelection();
24381             if(!s.focusNode || s.focusNode.nodeType != 3){
24382                 var r = s.getRangeAt(0);
24383                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24384                 r.collapse(true);
24385                 this.deferFocus();
24386             }
24387             try{
24388                 this.execCmd('useCSS', true);
24389                 this.execCmd('styleWithCSS', false);
24390             }catch(e){}
24391         }
24392         this.owner.fireEvent('activate', this);
24393     },
24394
24395     // private
24396     adjustFont: function(btn){
24397         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24398         //if(Roo.isSafari){ // safari
24399         //    adjust *= 2;
24400        // }
24401         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24402         if(Roo.isSafari){ // safari
24403             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24404             v =  (v < 10) ? 10 : v;
24405             v =  (v > 48) ? 48 : v;
24406             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24407             
24408         }
24409         
24410         
24411         v = Math.max(1, v+adjust);
24412         
24413         this.execCmd('FontSize', v  );
24414     },
24415
24416     onEditorEvent : function(e)
24417     {
24418         
24419         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
24420             return; // we do not handle this.. (undo manager does..)
24421         }
24422         // in theory this detects if the last element is not a br, then we try and do that.
24423         // its so clicking in space at bottom triggers adding a br and moving the cursor.
24424         if (e &&
24425             e.target.nodeName == 'BODY' &&
24426             e.type == "mouseup" &&
24427             this.doc.body.lastChild
24428            ) {
24429             var lc = this.doc.body.lastChild;
24430             // gtx-trans is google translate plugin adding crap.
24431             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
24432                 lc = lc.previousSibling;
24433             }
24434             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
24435             // if last element is <BR> - then dont do anything.
24436             
24437                 var ns = this.doc.createElement('br');
24438                 this.doc.body.appendChild(ns);
24439                 range = this.doc.createRange();
24440                 range.setStartAfter(ns);
24441                 range.collapse(true);
24442                 var sel = this.win.getSelection();
24443                 sel.removeAllRanges();
24444                 sel.addRange(range);
24445             }
24446         }
24447         
24448         
24449         
24450         this.fireEditorEvent(e);
24451       //  this.updateToolbar();
24452         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24453     },
24454     
24455     fireEditorEvent: function(e)
24456     {
24457         this.owner.fireEvent('editorevent', this, e);
24458     },
24459
24460     insertTag : function(tg)
24461     {
24462         // could be a bit smarter... -> wrap the current selected tRoo..
24463         if (tg.toLowerCase() == 'span' ||
24464             tg.toLowerCase() == 'code' ||
24465             tg.toLowerCase() == 'sup' ||
24466             tg.toLowerCase() == 'sub' 
24467             ) {
24468             
24469             range = this.createRange(this.getSelection());
24470             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24471             wrappingNode.appendChild(range.extractContents());
24472             range.insertNode(wrappingNode);
24473
24474             return;
24475             
24476             
24477             
24478         }
24479         this.execCmd("formatblock",   tg);
24480         this.undoManager.addEvent(); 
24481     },
24482     
24483     insertText : function(txt)
24484     {
24485         
24486         
24487         var range = this.createRange();
24488         range.deleteContents();
24489                //alert(Sender.getAttribute('label'));
24490                
24491         range.insertNode(this.doc.createTextNode(txt));
24492         this.undoManager.addEvent();
24493     } ,
24494     
24495      
24496
24497     /**
24498      * Executes a Midas editor command on the editor document and performs necessary focus and
24499      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24500      * @param {String} cmd The Midas command
24501      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24502      */
24503     relayCmd : function(cmd, value)
24504     {
24505         
24506         switch (cmd) {
24507             case 'justifyleft':
24508             case 'justifyright':
24509             case 'justifycenter':
24510                 // if we are in a cell, then we will adjust the
24511                 var n = this.getParentElement();
24512                 var td = n.closest('td');
24513                 if (td) {
24514                     var bl = Roo.htmleditor.Block.factory(td);
24515                     bl.textAlign = cmd.replace('justify','');
24516                     bl.updateElement();
24517                     this.owner.fireEvent('editorevent', this);
24518                     return;
24519                 }
24520                 this.execCmd('styleWithCSS', true); // 
24521                 break;
24522             case 'bold':
24523             case 'italic':
24524                 // if there is no selection, then we insert, and set the curson inside it..
24525                 this.execCmd('styleWithCSS', false); 
24526                 break;
24527                 
24528         
24529             default:
24530                 break;
24531         }
24532         
24533         
24534         this.win.focus();
24535         this.execCmd(cmd, value);
24536         this.owner.fireEvent('editorevent', this);
24537         //this.updateToolbar();
24538         this.owner.deferFocus();
24539     },
24540
24541     /**
24542      * Executes a Midas editor command directly on the editor document.
24543      * For visual commands, you should use {@link #relayCmd} instead.
24544      * <b>This should only be called after the editor is initialized.</b>
24545      * @param {String} cmd The Midas command
24546      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24547      */
24548     execCmd : function(cmd, value){
24549         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24550         this.syncValue();
24551     },
24552  
24553  
24554    
24555     /**
24556      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24557      * to insert tRoo.
24558      * @param {String} text | dom node.. 
24559      */
24560     insertAtCursor : function(text)
24561     {
24562         
24563         if(!this.activated){
24564             return;
24565         }
24566          
24567         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24568             this.win.focus();
24569             
24570             
24571             // from jquery ui (MIT licenced)
24572             var range, node;
24573             var win = this.win;
24574             
24575             if (win.getSelection && win.getSelection().getRangeAt) {
24576                 
24577                 // delete the existing?
24578                 
24579                 this.createRange(this.getSelection()).deleteContents();
24580                 range = win.getSelection().getRangeAt(0);
24581                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24582                 range.insertNode(node);
24583                 range = range.cloneRange();
24584                 range.collapse(false);
24585                  
24586                 win.getSelection().removeAllRanges();
24587                 win.getSelection().addRange(range);
24588                 
24589                 
24590                 
24591             } else if (win.document.selection && win.document.selection.createRange) {
24592                 // no firefox support
24593                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24594                 win.document.selection.createRange().pasteHTML(txt);
24595             
24596             } else {
24597                 // no firefox support
24598                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24599                 this.execCmd('InsertHTML', txt);
24600             } 
24601             this.syncValue();
24602             
24603             this.deferFocus();
24604         }
24605     },
24606  // private
24607     mozKeyPress : function(e){
24608         if(e.ctrlKey){
24609             var c = e.getCharCode(), cmd;
24610           
24611             if(c > 0){
24612                 c = String.fromCharCode(c).toLowerCase();
24613                 switch(c){
24614                     case 'b':
24615                         cmd = 'bold';
24616                         break;
24617                     case 'i':
24618                         cmd = 'italic';
24619                         break;
24620                     
24621                     case 'u':
24622                         cmd = 'underline';
24623                         break;
24624                     
24625                     //case 'v':
24626                       //  this.cleanUpPaste.defer(100, this);
24627                       //  return;
24628                         
24629                 }
24630                 if(cmd){
24631                     
24632                     this.relayCmd(cmd);
24633                     //this.win.focus();
24634                     //this.execCmd(cmd);
24635                     //this.deferFocus();
24636                     e.preventDefault();
24637                 }
24638                 
24639             }
24640         }
24641     },
24642
24643     // private
24644     fixKeys : function(){ // load time branching for fastest keydown performance
24645         
24646         
24647         if(Roo.isIE){
24648             return function(e){
24649                 var k = e.getKey(), r;
24650                 if(k == e.TAB){
24651                     e.stopEvent();
24652                     r = this.doc.selection.createRange();
24653                     if(r){
24654                         r.collapse(true);
24655                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24656                         this.deferFocus();
24657                     }
24658                     return;
24659                 }
24660                 /// this is handled by Roo.htmleditor.KeyEnter
24661                  /*
24662                 if(k == e.ENTER){
24663                     r = this.doc.selection.createRange();
24664                     if(r){
24665                         var target = r.parentElement();
24666                         if(!target || target.tagName.toLowerCase() != 'li'){
24667                             e.stopEvent();
24668                             r.pasteHTML('<br/>');
24669                             r.collapse(false);
24670                             r.select();
24671                         }
24672                     }
24673                 }
24674                 */
24675                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24676                 //    this.cleanUpPaste.defer(100, this);
24677                 //    return;
24678                 //}
24679                 
24680                 
24681             };
24682         }else if(Roo.isOpera){
24683             return function(e){
24684                 var k = e.getKey();
24685                 if(k == e.TAB){
24686                     e.stopEvent();
24687                     this.win.focus();
24688                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24689                     this.deferFocus();
24690                 }
24691                
24692                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24693                 //    this.cleanUpPaste.defer(100, this);
24694                  //   return;
24695                 //}
24696                 
24697             };
24698         }else if(Roo.isSafari){
24699             return function(e){
24700                 var k = e.getKey();
24701                 
24702                 if(k == e.TAB){
24703                     e.stopEvent();
24704                     this.execCmd('InsertText','\t');
24705                     this.deferFocus();
24706                     return;
24707                 }
24708                  this.mozKeyPress(e);
24709                 
24710                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24711                  //   this.cleanUpPaste.defer(100, this);
24712                  //   return;
24713                // }
24714                 
24715              };
24716         }
24717     }(),
24718     
24719     getAllAncestors: function()
24720     {
24721         var p = this.getSelectedNode();
24722         var a = [];
24723         if (!p) {
24724             a.push(p); // push blank onto stack..
24725             p = this.getParentElement();
24726         }
24727         
24728         
24729         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24730             a.push(p);
24731             p = p.parentNode;
24732         }
24733         a.push(this.doc.body);
24734         return a;
24735     },
24736     lastSel : false,
24737     lastSelNode : false,
24738     
24739     
24740     getSelection : function() 
24741     {
24742         this.assignDocWin();
24743         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
24744     },
24745     /**
24746      * Select a dom node
24747      * @param {DomElement} node the node to select
24748      */
24749     selectNode : function(node, collapse)
24750     {
24751         var nodeRange = node.ownerDocument.createRange();
24752         try {
24753             nodeRange.selectNode(node);
24754         } catch (e) {
24755             nodeRange.selectNodeContents(node);
24756         }
24757         if (collapse === true) {
24758             nodeRange.collapse(true);
24759         }
24760         //
24761         var s = this.win.getSelection();
24762         s.removeAllRanges();
24763         s.addRange(nodeRange);
24764     },
24765     
24766     getSelectedNode: function() 
24767     {
24768         // this may only work on Gecko!!!
24769         
24770         // should we cache this!!!!
24771         
24772          
24773          
24774         var range = this.createRange(this.getSelection()).cloneRange();
24775         
24776         if (Roo.isIE) {
24777             var parent = range.parentElement();
24778             while (true) {
24779                 var testRange = range.duplicate();
24780                 testRange.moveToElementText(parent);
24781                 if (testRange.inRange(range)) {
24782                     break;
24783                 }
24784                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24785                     break;
24786                 }
24787                 parent = parent.parentElement;
24788             }
24789             return parent;
24790         }
24791         
24792         // is ancestor a text element.
24793         var ac =  range.commonAncestorContainer;
24794         if (ac.nodeType == 3) {
24795             ac = ac.parentNode;
24796         }
24797         
24798         var ar = ac.childNodes;
24799          
24800         var nodes = [];
24801         var other_nodes = [];
24802         var has_other_nodes = false;
24803         for (var i=0;i<ar.length;i++) {
24804             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24805                 continue;
24806             }
24807             // fullly contained node.
24808             
24809             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24810                 nodes.push(ar[i]);
24811                 continue;
24812             }
24813             
24814             // probably selected..
24815             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24816                 other_nodes.push(ar[i]);
24817                 continue;
24818             }
24819             // outer..
24820             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24821                 continue;
24822             }
24823             
24824             
24825             has_other_nodes = true;
24826         }
24827         if (!nodes.length && other_nodes.length) {
24828             nodes= other_nodes;
24829         }
24830         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24831             return false;
24832         }
24833         
24834         return nodes[0];
24835     },
24836     
24837     
24838     createRange: function(sel)
24839     {
24840         // this has strange effects when using with 
24841         // top toolbar - not sure if it's a great idea.
24842         //this.editor.contentWindow.focus();
24843         if (typeof sel != "undefined") {
24844             try {
24845                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24846             } catch(e) {
24847                 return this.doc.createRange();
24848             }
24849         } else {
24850             return this.doc.createRange();
24851         }
24852     },
24853     getParentElement: function()
24854     {
24855         
24856         this.assignDocWin();
24857         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24858         
24859         var range = this.createRange(sel);
24860          
24861         try {
24862             var p = range.commonAncestorContainer;
24863             while (p.nodeType == 3) { // text node
24864                 p = p.parentNode;
24865             }
24866             return p;
24867         } catch (e) {
24868             return null;
24869         }
24870     
24871     },
24872     /***
24873      *
24874      * Range intersection.. the hard stuff...
24875      *  '-1' = before
24876      *  '0' = hits..
24877      *  '1' = after.
24878      *         [ -- selected range --- ]
24879      *   [fail]                        [fail]
24880      *
24881      *    basically..
24882      *      if end is before start or  hits it. fail.
24883      *      if start is after end or hits it fail.
24884      *
24885      *   if either hits (but other is outside. - then it's not 
24886      *   
24887      *    
24888      **/
24889     
24890     
24891     // @see http://www.thismuchiknow.co.uk/?p=64.
24892     rangeIntersectsNode : function(range, node)
24893     {
24894         var nodeRange = node.ownerDocument.createRange();
24895         try {
24896             nodeRange.selectNode(node);
24897         } catch (e) {
24898             nodeRange.selectNodeContents(node);
24899         }
24900     
24901         var rangeStartRange = range.cloneRange();
24902         rangeStartRange.collapse(true);
24903     
24904         var rangeEndRange = range.cloneRange();
24905         rangeEndRange.collapse(false);
24906     
24907         var nodeStartRange = nodeRange.cloneRange();
24908         nodeStartRange.collapse(true);
24909     
24910         var nodeEndRange = nodeRange.cloneRange();
24911         nodeEndRange.collapse(false);
24912     
24913         return rangeStartRange.compareBoundaryPoints(
24914                  Range.START_TO_START, nodeEndRange) == -1 &&
24915                rangeEndRange.compareBoundaryPoints(
24916                  Range.START_TO_START, nodeStartRange) == 1;
24917         
24918          
24919     },
24920     rangeCompareNode : function(range, node)
24921     {
24922         var nodeRange = node.ownerDocument.createRange();
24923         try {
24924             nodeRange.selectNode(node);
24925         } catch (e) {
24926             nodeRange.selectNodeContents(node);
24927         }
24928         
24929         
24930         range.collapse(true);
24931     
24932         nodeRange.collapse(true);
24933      
24934         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24935         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24936          
24937         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24938         
24939         var nodeIsBefore   =  ss == 1;
24940         var nodeIsAfter    = ee == -1;
24941         
24942         if (nodeIsBefore && nodeIsAfter) {
24943             return 0; // outer
24944         }
24945         if (!nodeIsBefore && nodeIsAfter) {
24946             return 1; //right trailed.
24947         }
24948         
24949         if (nodeIsBefore && !nodeIsAfter) {
24950             return 2;  // left trailed.
24951         }
24952         // fully contined.
24953         return 3;
24954     },
24955  
24956     cleanWordChars : function(input) {// change the chars to hex code
24957         
24958        var swapCodes  = [ 
24959             [    8211, "&#8211;" ], 
24960             [    8212, "&#8212;" ], 
24961             [    8216,  "'" ],  
24962             [    8217, "'" ],  
24963             [    8220, '"' ],  
24964             [    8221, '"' ],  
24965             [    8226, "*" ],  
24966             [    8230, "..." ]
24967         ]; 
24968         var output = input;
24969         Roo.each(swapCodes, function(sw) { 
24970             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24971             
24972             output = output.replace(swapper, sw[1]);
24973         });
24974         
24975         return output;
24976     },
24977     
24978      
24979     
24980         
24981     
24982     cleanUpChild : function (node)
24983     {
24984         
24985         new Roo.htmleditor.FilterComment({node : node});
24986         new Roo.htmleditor.FilterAttributes({
24987                 node : node,
24988                 attrib_black : this.ablack,
24989                 attrib_clean : this.aclean,
24990                 style_white : this.cwhite,
24991                 style_black : this.cblack
24992         });
24993         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
24994         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
24995          
24996         
24997     },
24998     
24999     /**
25000      * Clean up MS wordisms...
25001      * @deprecated - use filter directly
25002      */
25003     cleanWord : function(node)
25004     {
25005         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
25006         
25007     },
25008    
25009     
25010     /**
25011
25012      * @deprecated - use filters
25013      */
25014     cleanTableWidths : function(node)
25015     {
25016         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
25017         
25018  
25019     },
25020     
25021      
25022         
25023     applyBlacklists : function()
25024     {
25025         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25026         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25027         
25028         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
25029         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
25030         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
25031         
25032         this.white = [];
25033         this.black = [];
25034         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25035             if (b.indexOf(tag) > -1) {
25036                 return;
25037             }
25038             this.white.push(tag);
25039             
25040         }, this);
25041         
25042         Roo.each(w, function(tag) {
25043             if (b.indexOf(tag) > -1) {
25044                 return;
25045             }
25046             if (this.white.indexOf(tag) > -1) {
25047                 return;
25048             }
25049             this.white.push(tag);
25050             
25051         }, this);
25052         
25053         
25054         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25055             if (w.indexOf(tag) > -1) {
25056                 return;
25057             }
25058             this.black.push(tag);
25059             
25060         }, this);
25061         
25062         Roo.each(b, function(tag) {
25063             if (w.indexOf(tag) > -1) {
25064                 return;
25065             }
25066             if (this.black.indexOf(tag) > -1) {
25067                 return;
25068             }
25069             this.black.push(tag);
25070             
25071         }, this);
25072         
25073         
25074         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25075         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25076         
25077         this.cwhite = [];
25078         this.cblack = [];
25079         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25080             if (b.indexOf(tag) > -1) {
25081                 return;
25082             }
25083             this.cwhite.push(tag);
25084             
25085         }, this);
25086         
25087         Roo.each(w, function(tag) {
25088             if (b.indexOf(tag) > -1) {
25089                 return;
25090             }
25091             if (this.cwhite.indexOf(tag) > -1) {
25092                 return;
25093             }
25094             this.cwhite.push(tag);
25095             
25096         }, this);
25097         
25098         
25099         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25100             if (w.indexOf(tag) > -1) {
25101                 return;
25102             }
25103             this.cblack.push(tag);
25104             
25105         }, this);
25106         
25107         Roo.each(b, function(tag) {
25108             if (w.indexOf(tag) > -1) {
25109                 return;
25110             }
25111             if (this.cblack.indexOf(tag) > -1) {
25112                 return;
25113             }
25114             this.cblack.push(tag);
25115             
25116         }, this);
25117     },
25118     
25119     setStylesheets : function(stylesheets)
25120     {
25121         if(typeof(stylesheets) == 'string'){
25122             Roo.get(this.iframe.contentDocument.head).createChild({
25123                 tag : 'link',
25124                 rel : 'stylesheet',
25125                 type : 'text/css',
25126                 href : stylesheets
25127             });
25128             
25129             return;
25130         }
25131         var _this = this;
25132      
25133         Roo.each(stylesheets, function(s) {
25134             if(!s.length){
25135                 return;
25136             }
25137             
25138             Roo.get(_this.iframe.contentDocument.head).createChild({
25139                 tag : 'link',
25140                 rel : 'stylesheet',
25141                 type : 'text/css',
25142                 href : s
25143             });
25144         });
25145
25146         
25147     },
25148     
25149     
25150     updateLanguage : function()
25151     {
25152         if (!this.iframe || !this.iframe.content) {
25153             return;
25154         }
25155         Roo.get(this.iframe.content.body).attr("lang", this.language);
25156     },
25157     
25158     
25159     removeStylesheets : function()
25160     {
25161         var _this = this;
25162         
25163         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25164             s.remove();
25165         });
25166     },
25167     
25168     setStyle : function(style)
25169     {
25170         Roo.get(this.iframe.contentDocument.head).createChild({
25171             tag : 'style',
25172             type : 'text/css',
25173             html : style
25174         });
25175
25176         return;
25177     }
25178     
25179     // hide stuff that is not compatible
25180     /**
25181      * @event blur
25182      * @hide
25183      */
25184     /**
25185      * @event change
25186      * @hide
25187      */
25188     /**
25189      * @event focus
25190      * @hide
25191      */
25192     /**
25193      * @event specialkey
25194      * @hide
25195      */
25196     /**
25197      * @cfg {String} fieldClass @hide
25198      */
25199     /**
25200      * @cfg {String} focusClass @hide
25201      */
25202     /**
25203      * @cfg {String} autoCreate @hide
25204      */
25205     /**
25206      * @cfg {String} inputType @hide
25207      */
25208     /**
25209      * @cfg {String} invalidClass @hide
25210      */
25211     /**
25212      * @cfg {String} invalidText @hide
25213      */
25214     /**
25215      * @cfg {String} msgFx @hide
25216      */
25217     /**
25218      * @cfg {String} validateOnBlur @hide
25219      */
25220 });
25221
25222 Roo.HtmlEditorCore.white = [
25223         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
25224         
25225        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
25226        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
25227        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
25228        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
25229        'TABLE',   'UL',         'XMP', 
25230        
25231        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
25232       'THEAD',   'TR', 
25233      
25234       'DIR', 'MENU', 'OL', 'UL', 'DL',
25235        
25236       'EMBED',  'OBJECT'
25237 ];
25238
25239
25240 Roo.HtmlEditorCore.black = [
25241     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25242         'APPLET', // 
25243         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
25244         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
25245         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
25246         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
25247         //'FONT' // CLEAN LATER..
25248         'COLGROUP', 'COL'  // messy tables.
25249         
25250 ];
25251 Roo.HtmlEditorCore.clean = [ // ?? needed???
25252      'SCRIPT', 'STYLE', 'TITLE', 'XML'
25253 ];
25254 Roo.HtmlEditorCore.tag_remove = [
25255     'FONT', 'TBODY'  
25256 ];
25257 // attributes..
25258
25259 Roo.HtmlEditorCore.ablack = [
25260     'on'
25261 ];
25262     
25263 Roo.HtmlEditorCore.aclean = [ 
25264     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25265 ];
25266
25267 // protocols..
25268 Roo.HtmlEditorCore.pwhite= [
25269         'http',  'https',  'mailto'
25270 ];
25271
25272 // white listed style attributes.
25273 Roo.HtmlEditorCore.cwhite= [
25274       //  'text-align', /// default is to allow most things..
25275       
25276          
25277 //        'font-size'//??
25278 ];
25279
25280 // black listed style attributes.
25281 Roo.HtmlEditorCore.cblack= [
25282       //  'font-size' -- this can be set by the project 
25283 ];
25284
25285
25286
25287
25288     //<script type="text/javascript">
25289
25290 /*
25291  * Ext JS Library 1.1.1
25292  * Copyright(c) 2006-2007, Ext JS, LLC.
25293  * Licence LGPL
25294  * 
25295  */
25296  
25297  
25298 Roo.form.HtmlEditor = function(config){
25299     
25300     
25301     
25302     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
25303     
25304     if (!this.toolbars) {
25305         this.toolbars = [];
25306     }
25307     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25308     
25309     
25310 };
25311
25312 /**
25313  * @class Roo.form.HtmlEditor
25314  * @extends Roo.form.Field
25315  * Provides a lightweight HTML Editor component.
25316  *
25317  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25318  * 
25319  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25320  * supported by this editor.</b><br/><br/>
25321  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25322  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25323  */
25324 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
25325     /**
25326      * @cfg {Boolean} clearUp
25327      */
25328     clearUp : true,
25329       /**
25330      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25331      */
25332     toolbars : false,
25333    
25334      /**
25335      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25336      *                        Roo.resizable.
25337      */
25338     resizable : false,
25339      /**
25340      * @cfg {Number} height (in pixels)
25341      */   
25342     height: 300,
25343    /**
25344      * @cfg {Number} width (in pixels)
25345      */   
25346     width: 500,
25347     
25348     /**
25349      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
25350      * 
25351      */
25352     stylesheets: false,
25353     
25354     
25355      /**
25356      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
25357      * 
25358      */
25359     cblack: false,
25360     /**
25361      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
25362      * 
25363      */
25364     cwhite: false,
25365     
25366      /**
25367      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
25368      * 
25369      */
25370     black: false,
25371     /**
25372      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
25373      * 
25374      */
25375     white: false,
25376     /**
25377      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25378      */
25379     allowComments: false,
25380     /**
25381      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25382      */
25383     enableBlocks : true,
25384     
25385     /**
25386      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25387      *         if you are doing an email editor, this probably needs disabling, it's designed
25388      */
25389     autoClean: true,
25390     /**
25391      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
25392      */
25393     bodyCls : '',
25394     /**
25395      * @cfg {String} language default en - language of text (usefull for rtl languages)
25396      * 
25397      */
25398     language: 'en',
25399     
25400      
25401     // id of frame..
25402     frameId: false,
25403     
25404     // private properties
25405     validationEvent : false,
25406     deferHeight: true,
25407     initialized : false,
25408     activated : false,
25409     
25410     onFocus : Roo.emptyFn,
25411     iframePad:3,
25412     hideMode:'offsets',
25413     
25414     actionMode : 'container', // defaults to hiding it...
25415     
25416     defaultAutoCreate : { // modified by initCompnoent..
25417         tag: "textarea",
25418         style:"width:500px;height:300px;",
25419         autocomplete: "new-password"
25420     },
25421
25422     // private
25423     initComponent : function(){
25424         this.addEvents({
25425             /**
25426              * @event initialize
25427              * Fires when the editor is fully initialized (including the iframe)
25428              * @param {HtmlEditor} this
25429              */
25430             initialize: true,
25431             /**
25432              * @event activate
25433              * Fires when the editor is first receives the focus. Any insertion must wait
25434              * until after this event.
25435              * @param {HtmlEditor} this
25436              */
25437             activate: true,
25438              /**
25439              * @event beforesync
25440              * Fires before the textarea is updated with content from the editor iframe. Return false
25441              * to cancel the sync.
25442              * @param {HtmlEditor} this
25443              * @param {String} html
25444              */
25445             beforesync: true,
25446              /**
25447              * @event beforepush
25448              * Fires before the iframe editor is updated with content from the textarea. Return false
25449              * to cancel the push.
25450              * @param {HtmlEditor} this
25451              * @param {String} html
25452              */
25453             beforepush: true,
25454              /**
25455              * @event sync
25456              * Fires when the textarea is updated with content from the editor iframe.
25457              * @param {HtmlEditor} this
25458              * @param {String} html
25459              */
25460             sync: true,
25461              /**
25462              * @event push
25463              * Fires when the iframe editor is updated with content from the textarea.
25464              * @param {HtmlEditor} this
25465              * @param {String} html
25466              */
25467             push: true,
25468              /**
25469              * @event editmodechange
25470              * Fires when the editor switches edit modes
25471              * @param {HtmlEditor} this
25472              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25473              */
25474             editmodechange: true,
25475             /**
25476              * @event editorevent
25477              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25478              * @param {HtmlEditor} this
25479              */
25480             editorevent: true,
25481             /**
25482              * @event firstfocus
25483              * Fires when on first focus - needed by toolbars..
25484              * @param {HtmlEditor} this
25485              */
25486             firstfocus: true,
25487             /**
25488              * @event autosave
25489              * Auto save the htmlEditor value as a file into Events
25490              * @param {HtmlEditor} this
25491              */
25492             autosave: true,
25493             /**
25494              * @event savedpreview
25495              * preview the saved version of htmlEditor
25496              * @param {HtmlEditor} this
25497              */
25498             savedpreview: true,
25499             
25500             /**
25501             * @event stylesheetsclick
25502             * Fires when press the Sytlesheets button
25503             * @param {Roo.HtmlEditorCore} this
25504             */
25505             stylesheetsclick: true,
25506             /**
25507             * @event paste
25508             * Fires when press user pastes into the editor
25509             * @param {Roo.HtmlEditorCore} this
25510             */
25511             paste: true 
25512         });
25513         this.defaultAutoCreate =  {
25514             tag: "textarea",
25515             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25516             autocomplete: "new-password"
25517         };
25518     },
25519
25520     /**
25521      * Protected method that will not generally be called directly. It
25522      * is called when the editor creates its toolbar. Override this method if you need to
25523      * add custom toolbar buttons.
25524      * @param {HtmlEditor} editor
25525      */
25526     createToolbar : function(editor){
25527         Roo.log("create toolbars");
25528         if (!editor.toolbars || !editor.toolbars.length) {
25529             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25530         }
25531         
25532         for (var i =0 ; i < editor.toolbars.length;i++) {
25533             editor.toolbars[i] = Roo.factory(
25534                     typeof(editor.toolbars[i]) == 'string' ?
25535                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25536                 Roo.form.HtmlEditor);
25537             editor.toolbars[i].init(editor);
25538         }
25539          
25540         
25541     },
25542     /**
25543      * get the Context selected node
25544      * @returns {DomElement|boolean} selected node if active or false if none
25545      * 
25546      */
25547     getSelectedNode : function()
25548     {
25549         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
25550             return false;
25551         }
25552         return this.toolbars[1].tb.selectedNode;
25553     
25554     },
25555     // private
25556     onRender : function(ct, position)
25557     {
25558         var _t = this;
25559         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25560         
25561         this.wrap = this.el.wrap({
25562             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25563         });
25564         
25565         this.editorcore.onRender(ct, position);
25566          
25567         if (this.resizable) {
25568             this.resizeEl = new Roo.Resizable(this.wrap, {
25569                 pinned : true,
25570                 wrap: true,
25571                 dynamic : true,
25572                 minHeight : this.height,
25573                 height: this.height,
25574                 handles : this.resizable,
25575                 width: this.width,
25576                 listeners : {
25577                     resize : function(r, w, h) {
25578                         _t.onResize(w,h); // -something
25579                     }
25580                 }
25581             });
25582             
25583         }
25584         this.createToolbar(this);
25585        
25586         
25587         if(!this.width){
25588             this.setSize(this.wrap.getSize());
25589         }
25590         if (this.resizeEl) {
25591             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25592             // should trigger onReize..
25593         }
25594         
25595         this.keyNav = new Roo.KeyNav(this.el, {
25596             
25597             "tab" : function(e){
25598                 e.preventDefault();
25599                 
25600                 var value = this.getValue();
25601                 
25602                 var start = this.el.dom.selectionStart;
25603                 var end = this.el.dom.selectionEnd;
25604                 
25605                 if(!e.shiftKey){
25606                     
25607                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
25608                     this.el.dom.setSelectionRange(end + 1, end + 1);
25609                     return;
25610                 }
25611                 
25612                 var f = value.substring(0, start).split("\t");
25613                 
25614                 if(f.pop().length != 0){
25615                     return;
25616                 }
25617                 
25618                 this.setValue(f.join("\t") + value.substring(end));
25619                 this.el.dom.setSelectionRange(start - 1, start - 1);
25620                 
25621             },
25622             
25623             "home" : function(e){
25624                 e.preventDefault();
25625                 
25626                 var curr = this.el.dom.selectionStart;
25627                 var lines = this.getValue().split("\n");
25628                 
25629                 if(!lines.length){
25630                     return;
25631                 }
25632                 
25633                 if(e.ctrlKey){
25634                     this.el.dom.setSelectionRange(0, 0);
25635                     return;
25636                 }
25637                 
25638                 var pos = 0;
25639                 
25640                 for (var i = 0; i < lines.length;i++) {
25641                     pos += lines[i].length;
25642                     
25643                     if(i != 0){
25644                         pos += 1;
25645                     }
25646                     
25647                     if(pos < curr){
25648                         continue;
25649                     }
25650                     
25651                     pos -= lines[i].length;
25652                     
25653                     break;
25654                 }
25655                 
25656                 if(!e.shiftKey){
25657                     this.el.dom.setSelectionRange(pos, pos);
25658                     return;
25659                 }
25660                 
25661                 this.el.dom.selectionStart = pos;
25662                 this.el.dom.selectionEnd = curr;
25663             },
25664             
25665             "end" : function(e){
25666                 e.preventDefault();
25667                 
25668                 var curr = this.el.dom.selectionStart;
25669                 var lines = this.getValue().split("\n");
25670                 
25671                 if(!lines.length){
25672                     return;
25673                 }
25674                 
25675                 if(e.ctrlKey){
25676                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
25677                     return;
25678                 }
25679                 
25680                 var pos = 0;
25681                 
25682                 for (var i = 0; i < lines.length;i++) {
25683                     
25684                     pos += lines[i].length;
25685                     
25686                     if(i != 0){
25687                         pos += 1;
25688                     }
25689                     
25690                     if(pos < curr){
25691                         continue;
25692                     }
25693                     
25694                     break;
25695                 }
25696                 
25697                 if(!e.shiftKey){
25698                     this.el.dom.setSelectionRange(pos, pos);
25699                     return;
25700                 }
25701                 
25702                 this.el.dom.selectionStart = curr;
25703                 this.el.dom.selectionEnd = pos;
25704             },
25705
25706             scope : this,
25707
25708             doRelay : function(foo, bar, hname){
25709                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
25710             },
25711
25712             forceKeyDown: true
25713         });
25714         
25715 //        if(this.autosave && this.w){
25716 //            this.autoSaveFn = setInterval(this.autosave, 1000);
25717 //        }
25718     },
25719
25720     // private
25721     onResize : function(w, h)
25722     {
25723         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25724         var ew = false;
25725         var eh = false;
25726         
25727         if(this.el ){
25728             if(typeof w == 'number'){
25729                 var aw = w - this.wrap.getFrameWidth('lr');
25730                 this.el.setWidth(this.adjustWidth('textarea', aw));
25731                 ew = aw;
25732             }
25733             if(typeof h == 'number'){
25734                 var tbh = 0;
25735                 for (var i =0; i < this.toolbars.length;i++) {
25736                     // fixme - ask toolbars for heights?
25737                     tbh += this.toolbars[i].tb.el.getHeight();
25738                     if (this.toolbars[i].footer) {
25739                         tbh += this.toolbars[i].footer.el.getHeight();
25740                     }
25741                 }
25742                 
25743                 
25744                 
25745                 
25746                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25747                 ah -= 5; // knock a few pixes off for look..
25748 //                Roo.log(ah);
25749                 this.el.setHeight(this.adjustWidth('textarea', ah));
25750                 var eh = ah;
25751             }
25752         }
25753         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25754         this.editorcore.onResize(ew,eh);
25755         
25756     },
25757
25758     /**
25759      * Toggles the editor between standard and source edit mode.
25760      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25761      */
25762     toggleSourceEdit : function(sourceEditMode)
25763     {
25764         this.editorcore.toggleSourceEdit(sourceEditMode);
25765         
25766         if(this.editorcore.sourceEditMode){
25767             Roo.log('editor - showing textarea');
25768             
25769 //            Roo.log('in');
25770 //            Roo.log(this.syncValue());
25771             this.editorcore.syncValue();
25772             this.el.removeClass('x-hidden');
25773             this.el.dom.removeAttribute('tabIndex');
25774             this.el.focus();
25775             this.el.dom.scrollTop = 0;
25776             
25777             
25778             for (var i = 0; i < this.toolbars.length; i++) {
25779                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
25780                     this.toolbars[i].tb.hide();
25781                     this.toolbars[i].footer.hide();
25782                 }
25783             }
25784             
25785         }else{
25786             Roo.log('editor - hiding textarea');
25787 //            Roo.log('out')
25788 //            Roo.log(this.pushValue()); 
25789             this.editorcore.pushValue();
25790             
25791             this.el.addClass('x-hidden');
25792             this.el.dom.setAttribute('tabIndex', -1);
25793             
25794             for (var i = 0; i < this.toolbars.length; i++) {
25795                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
25796                     this.toolbars[i].tb.show();
25797                     this.toolbars[i].footer.show();
25798                 }
25799             }
25800             
25801             //this.deferFocus();
25802         }
25803         
25804         this.setSize(this.wrap.getSize());
25805         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
25806         
25807         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25808     },
25809  
25810     // private (for BoxComponent)
25811     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25812
25813     // private (for BoxComponent)
25814     getResizeEl : function(){
25815         return this.wrap;
25816     },
25817
25818     // private (for BoxComponent)
25819     getPositionEl : function(){
25820         return this.wrap;
25821     },
25822
25823     // private
25824     initEvents : function(){
25825         this.originalValue = this.getValue();
25826     },
25827
25828     /**
25829      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25830      * @method
25831      */
25832     markInvalid : Roo.emptyFn,
25833     /**
25834      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25835      * @method
25836      */
25837     clearInvalid : Roo.emptyFn,
25838
25839     setValue : function(v){
25840         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25841         this.editorcore.pushValue();
25842     },
25843
25844      
25845     // private
25846     deferFocus : function(){
25847         this.focus.defer(10, this);
25848     },
25849
25850     // doc'ed in Field
25851     focus : function(){
25852         this.editorcore.focus();
25853         
25854     },
25855       
25856
25857     // private
25858     onDestroy : function(){
25859         
25860         
25861         
25862         if(this.rendered){
25863             
25864             for (var i =0; i < this.toolbars.length;i++) {
25865                 // fixme - ask toolbars for heights?
25866                 this.toolbars[i].onDestroy();
25867             }
25868             
25869             this.wrap.dom.innerHTML = '';
25870             this.wrap.remove();
25871         }
25872     },
25873
25874     // private
25875     onFirstFocus : function(){
25876         //Roo.log("onFirstFocus");
25877         this.editorcore.onFirstFocus();
25878          for (var i =0; i < this.toolbars.length;i++) {
25879             this.toolbars[i].onFirstFocus();
25880         }
25881         
25882     },
25883     
25884     // private
25885     syncValue : function()
25886     {
25887         this.editorcore.syncValue();
25888     },
25889     
25890     pushValue : function()
25891     {
25892         this.editorcore.pushValue();
25893     },
25894     
25895     setStylesheets : function(stylesheets)
25896     {
25897         this.editorcore.setStylesheets(stylesheets);
25898     },
25899     
25900     removeStylesheets : function()
25901     {
25902         this.editorcore.removeStylesheets();
25903     }
25904      
25905     
25906     // hide stuff that is not compatible
25907     /**
25908      * @event blur
25909      * @hide
25910      */
25911     /**
25912      * @event change
25913      * @hide
25914      */
25915     /**
25916      * @event focus
25917      * @hide
25918      */
25919     /**
25920      * @event specialkey
25921      * @hide
25922      */
25923     /**
25924      * @cfg {String} fieldClass @hide
25925      */
25926     /**
25927      * @cfg {String} focusClass @hide
25928      */
25929     /**
25930      * @cfg {String} autoCreate @hide
25931      */
25932     /**
25933      * @cfg {String} inputType @hide
25934      */
25935     /**
25936      * @cfg {String} invalidClass @hide
25937      */
25938     /**
25939      * @cfg {String} invalidText @hide
25940      */
25941     /**
25942      * @cfg {String} msgFx @hide
25943      */
25944     /**
25945      * @cfg {String} validateOnBlur @hide
25946      */
25947 });
25948  
25949     // <script type="text/javascript">
25950 /*
25951  * Based on
25952  * Ext JS Library 1.1.1
25953  * Copyright(c) 2006-2007, Ext JS, LLC.
25954  *  
25955  
25956  */
25957
25958 /**
25959  * @class Roo.form.HtmlEditorToolbar1
25960  * Basic Toolbar
25961  * 
25962  * Usage:
25963  *
25964  new Roo.form.HtmlEditor({
25965     ....
25966     toolbars : [
25967         new Roo.form.HtmlEditorToolbar1({
25968             disable : { fonts: 1 , format: 1, ..., ... , ...],
25969             btns : [ .... ]
25970         })
25971     }
25972      
25973  * 
25974  * @cfg {Object} disable List of elements to disable..
25975  * @cfg {Array} btns List of additional buttons.
25976  * 
25977  * 
25978  * NEEDS Extra CSS? 
25979  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25980  */
25981  
25982 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25983 {
25984     
25985     Roo.apply(this, config);
25986     
25987     // default disabled, based on 'good practice'..
25988     this.disable = this.disable || {};
25989     Roo.applyIf(this.disable, {
25990         fontSize : true,
25991         colors : true,
25992         specialElements : true
25993     });
25994     
25995     
25996     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25997     // dont call parent... till later.
25998 }
25999
26000 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26001     
26002     tb: false,
26003     
26004     rendered: false,
26005     
26006     editor : false,
26007     editorcore : false,
26008     /**
26009      * @cfg {Object} disable  List of toolbar elements to disable
26010          
26011      */
26012     disable : false,
26013     
26014     
26015      /**
26016      * @cfg {String} createLinkText The default text for the create link prompt
26017      */
26018     createLinkText : 'Please enter the URL for the link:',
26019     /**
26020      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26021      */
26022     defaultLinkValue : 'http:/'+'/',
26023    
26024     
26025       /**
26026      * @cfg {Array} fontFamilies An array of available font families
26027      */
26028     fontFamilies : [
26029         'Arial',
26030         'Courier New',
26031         'Tahoma',
26032         'Times New Roman',
26033         'Verdana'
26034     ],
26035     
26036     specialChars : [
26037            "&#169;",
26038           "&#174;",     
26039           "&#8482;",    
26040           "&#163;" ,    
26041          // "&#8212;",    
26042           "&#8230;",    
26043           "&#247;" ,    
26044         //  "&#225;" ,     ?? a acute?
26045            "&#8364;"    , //Euro
26046        //   "&#8220;"    ,
26047         //  "&#8221;"    ,
26048         //  "&#8226;"    ,
26049           "&#176;"  //   , // degrees
26050
26051          // "&#233;"     , // e ecute
26052          // "&#250;"     , // u ecute?
26053     ],
26054     
26055     specialElements : [
26056         {
26057             text: "Insert Table",
26058             xtype: 'MenuItem',
26059             xns : Roo.Menu,
26060             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26061                 
26062         },
26063         {    
26064             text: "Insert Image",
26065             xtype: 'MenuItem',
26066             xns : Roo.Menu,
26067             ihtml : '<img src="about:blank"/>'
26068             
26069         }
26070         
26071          
26072     ],
26073     
26074     
26075     inputElements : [ 
26076             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26077             "input:submit", "input:button", "select", "textarea", "label" ],
26078     formats : [
26079         ["p"] ,  
26080         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26081         ["pre"],[ "code"], 
26082         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26083         ['div'],['span'],
26084         ['sup'],['sub']
26085     ],
26086     
26087     cleanStyles : [
26088         "font-size"
26089     ],
26090      /**
26091      * @cfg {String} defaultFont default font to use.
26092      */
26093     defaultFont: 'tahoma',
26094    
26095     fontSelect : false,
26096     
26097     
26098     formatCombo : false,
26099     
26100     init : function(editor)
26101     {
26102         this.editor = editor;
26103         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26104         var editorcore = this.editorcore;
26105         
26106         var _t = this;
26107         
26108         var fid = editorcore.frameId;
26109         var etb = this;
26110         function btn(id, toggle, handler){
26111             var xid = fid + '-'+ id ;
26112             return {
26113                 id : xid,
26114                 cmd : id,
26115                 cls : 'x-btn-icon x-edit-'+id,
26116                 enableToggle:toggle !== false,
26117                 scope: _t, // was editor...
26118                 handler:handler||_t.relayBtnCmd,
26119                 clickEvent:'mousedown',
26120                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26121                 tabIndex:-1
26122             };
26123         }
26124         
26125         
26126         
26127         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26128         this.tb = tb;
26129          // stop form submits
26130         tb.el.on('click', function(e){
26131             e.preventDefault(); // what does this do?
26132         });
26133
26134         if(!this.disable.font) { // && !Roo.isSafari){
26135             /* why no safari for fonts 
26136             editor.fontSelect = tb.el.createChild({
26137                 tag:'select',
26138                 tabIndex: -1,
26139                 cls:'x-font-select',
26140                 html: this.createFontOptions()
26141             });
26142             
26143             editor.fontSelect.on('change', function(){
26144                 var font = editor.fontSelect.dom.value;
26145                 editor.relayCmd('fontname', font);
26146                 editor.deferFocus();
26147             }, editor);
26148             
26149             tb.add(
26150                 editor.fontSelect.dom,
26151                 '-'
26152             );
26153             */
26154             
26155         };
26156         if(!this.disable.formats){
26157             this.formatCombo = new Roo.form.ComboBox({
26158                 store: new Roo.data.SimpleStore({
26159                     id : 'tag',
26160                     fields: ['tag'],
26161                     data : this.formats // from states.js
26162                 }),
26163                 blockFocus : true,
26164                 name : '',
26165                 //autoCreate : {tag: "div",  size: "20"},
26166                 displayField:'tag',
26167                 typeAhead: false,
26168                 mode: 'local',
26169                 editable : false,
26170                 triggerAction: 'all',
26171                 emptyText:'Add tag',
26172                 selectOnFocus:true,
26173                 width:135,
26174                 listeners : {
26175                     'select': function(c, r, i) {
26176                         editorcore.insertTag(r.get('tag'));
26177                         editor.focus();
26178                     }
26179                 }
26180
26181             });
26182             tb.addField(this.formatCombo);
26183             
26184         }
26185         
26186         if(!this.disable.format){
26187             tb.add(
26188                 btn('bold'),
26189                 btn('italic'),
26190                 btn('underline'),
26191                 btn('strikethrough')
26192             );
26193         };
26194         if(!this.disable.fontSize){
26195             tb.add(
26196                 '-',
26197                 
26198                 
26199                 btn('increasefontsize', false, editorcore.adjustFont),
26200                 btn('decreasefontsize', false, editorcore.adjustFont)
26201             );
26202         };
26203         
26204         
26205         if(!this.disable.colors){
26206             tb.add(
26207                 '-', {
26208                     id:editorcore.frameId +'-forecolor',
26209                     cls:'x-btn-icon x-edit-forecolor',
26210                     clickEvent:'mousedown',
26211                     tooltip: this.buttonTips['forecolor'] || undefined,
26212                     tabIndex:-1,
26213                     menu : new Roo.menu.ColorMenu({
26214                         allowReselect: true,
26215                         focus: Roo.emptyFn,
26216                         value:'000000',
26217                         plain:true,
26218                         selectHandler: function(cp, color){
26219                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26220                             editor.deferFocus();
26221                         },
26222                         scope: editorcore,
26223                         clickEvent:'mousedown'
26224                     })
26225                 }, {
26226                     id:editorcore.frameId +'backcolor',
26227                     cls:'x-btn-icon x-edit-backcolor',
26228                     clickEvent:'mousedown',
26229                     tooltip: this.buttonTips['backcolor'] || undefined,
26230                     tabIndex:-1,
26231                     menu : new Roo.menu.ColorMenu({
26232                         focus: Roo.emptyFn,
26233                         value:'FFFFFF',
26234                         plain:true,
26235                         allowReselect: true,
26236                         selectHandler: function(cp, color){
26237                             if(Roo.isGecko){
26238                                 editorcore.execCmd('useCSS', false);
26239                                 editorcore.execCmd('hilitecolor', color);
26240                                 editorcore.execCmd('useCSS', true);
26241                                 editor.deferFocus();
26242                             }else{
26243                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26244                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26245                                 editor.deferFocus();
26246                             }
26247                         },
26248                         scope:editorcore,
26249                         clickEvent:'mousedown'
26250                     })
26251                 }
26252             );
26253         };
26254         // now add all the items...
26255         
26256
26257         if(!this.disable.alignments){
26258             tb.add(
26259                 '-',
26260                 btn('justifyleft'),
26261                 btn('justifycenter'),
26262                 btn('justifyright')
26263             );
26264         };
26265
26266         //if(!Roo.isSafari){
26267             if(!this.disable.links){
26268                 tb.add(
26269                     '-',
26270                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26271                 );
26272             };
26273
26274             if(!this.disable.lists){
26275                 tb.add(
26276                     '-',
26277                     btn('insertorderedlist'),
26278                     btn('insertunorderedlist')
26279                 );
26280             }
26281             if(!this.disable.sourceEdit){
26282                 tb.add(
26283                     '-',
26284                     btn('sourceedit', true, function(btn){
26285                         this.toggleSourceEdit(btn.pressed);
26286                     })
26287                 );
26288             }
26289         //}
26290         
26291         var smenu = { };
26292         // special menu.. - needs to be tidied up..
26293         if (!this.disable.special) {
26294             smenu = {
26295                 text: "&#169;",
26296                 cls: 'x-edit-none',
26297                 
26298                 menu : {
26299                     items : []
26300                 }
26301             };
26302             for (var i =0; i < this.specialChars.length; i++) {
26303                 smenu.menu.items.push({
26304                     
26305                     html: this.specialChars[i],
26306                     handler: function(a,b) {
26307                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26308                         //editor.insertAtCursor(a.html);
26309                         
26310                     },
26311                     tabIndex:-1
26312                 });
26313             }
26314             
26315             
26316             tb.add(smenu);
26317             
26318             
26319         }
26320         
26321         var cmenu = { };
26322         if (!this.disable.cleanStyles) {
26323             cmenu = {
26324                 cls: 'x-btn-icon x-btn-clear',
26325                 
26326                 menu : {
26327                     items : []
26328                 }
26329             };
26330             for (var i =0; i < this.cleanStyles.length; i++) {
26331                 cmenu.menu.items.push({
26332                     actiontype : this.cleanStyles[i],
26333                     html: 'Remove ' + this.cleanStyles[i],
26334                     handler: function(a,b) {
26335 //                        Roo.log(a);
26336 //                        Roo.log(b);
26337                         var c = Roo.get(editorcore.doc.body);
26338                         c.select('[style]').each(function(s) {
26339                             s.dom.style.removeProperty(a.actiontype);
26340                         });
26341                         editorcore.syncValue();
26342                     },
26343                     tabIndex:-1
26344                 });
26345             }
26346             cmenu.menu.items.push({
26347                 actiontype : 'tablewidths',
26348                 html: 'Remove Table Widths',
26349                 handler: function(a,b) {
26350                     editorcore.cleanTableWidths();
26351                     editorcore.syncValue();
26352                 },
26353                 tabIndex:-1
26354             });
26355             cmenu.menu.items.push({
26356                 actiontype : 'word',
26357                 html: 'Remove MS Word Formating',
26358                 handler: function(a,b) {
26359                     editorcore.cleanWord();
26360                     editorcore.syncValue();
26361                 },
26362                 tabIndex:-1
26363             });
26364             
26365             cmenu.menu.items.push({
26366                 actiontype : 'all',
26367                 html: 'Remove All Styles',
26368                 handler: function(a,b) {
26369                     
26370                     var c = Roo.get(editorcore.doc.body);
26371                     c.select('[style]').each(function(s) {
26372                         s.dom.removeAttribute('style');
26373                     });
26374                     editorcore.syncValue();
26375                 },
26376                 tabIndex:-1
26377             });
26378             
26379             cmenu.menu.items.push({
26380                 actiontype : 'all',
26381                 html: 'Remove All CSS Classes',
26382                 handler: function(a,b) {
26383                     
26384                     var c = Roo.get(editorcore.doc.body);
26385                     c.select('[class]').each(function(s) {
26386                         s.dom.removeAttribute('class');
26387                     });
26388                     editorcore.cleanWord();
26389                     editorcore.syncValue();
26390                 },
26391                 tabIndex:-1
26392             });
26393             
26394              cmenu.menu.items.push({
26395                 actiontype : 'tidy',
26396                 html: 'Tidy HTML Source',
26397                 handler: function(a,b) {
26398                     new Roo.htmleditor.Tidy(editorcore.doc.body);
26399                     editorcore.syncValue();
26400                 },
26401                 tabIndex:-1
26402             });
26403             
26404             
26405             tb.add(cmenu);
26406         }
26407          
26408         if (!this.disable.specialElements) {
26409             var semenu = {
26410                 text: "Other;",
26411                 cls: 'x-edit-none',
26412                 menu : {
26413                     items : []
26414                 }
26415             };
26416             for (var i =0; i < this.specialElements.length; i++) {
26417                 semenu.menu.items.push(
26418                     Roo.apply({ 
26419                         handler: function(a,b) {
26420                             editor.insertAtCursor(this.ihtml);
26421                         }
26422                     }, this.specialElements[i])
26423                 );
26424                     
26425             }
26426             
26427             tb.add(semenu);
26428             
26429             
26430         }
26431          
26432         
26433         if (this.btns) {
26434             for(var i =0; i< this.btns.length;i++) {
26435                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
26436                 b.cls =  'x-edit-none';
26437                 
26438                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
26439                     b.cls += ' x-init-enable';
26440                 }
26441                 
26442                 b.scope = editorcore;
26443                 tb.add(b);
26444             }
26445         
26446         }
26447         
26448         
26449         
26450         // disable everything...
26451         
26452         this.tb.items.each(function(item){
26453             
26454            if(
26455                 item.id != editorcore.frameId+ '-sourceedit' && 
26456                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
26457             ){
26458                 
26459                 item.disable();
26460             }
26461         });
26462         this.rendered = true;
26463         
26464         // the all the btns;
26465         editor.on('editorevent', this.updateToolbar, this);
26466         // other toolbars need to implement this..
26467         //editor.on('editmodechange', this.updateToolbar, this);
26468     },
26469     
26470     
26471     relayBtnCmd : function(btn) {
26472         this.editorcore.relayCmd(btn.cmd);
26473     },
26474     // private used internally
26475     createLink : function(){
26476         Roo.log("create link?");
26477         var url = prompt(this.createLinkText, this.defaultLinkValue);
26478         if(url && url != 'http:/'+'/'){
26479             this.editorcore.relayCmd('createlink', url);
26480         }
26481     },
26482
26483     
26484     /**
26485      * Protected method that will not generally be called directly. It triggers
26486      * a toolbar update by reading the markup state of the current selection in the editor.
26487      */
26488     updateToolbar: function(){
26489
26490         if(!this.editorcore.activated){
26491             this.editor.onFirstFocus();
26492             return;
26493         }
26494
26495         var btns = this.tb.items.map, 
26496             doc = this.editorcore.doc,
26497             frameId = this.editorcore.frameId;
26498
26499         if(!this.disable.font && !Roo.isSafari){
26500             /*
26501             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26502             if(name != this.fontSelect.dom.value){
26503                 this.fontSelect.dom.value = name;
26504             }
26505             */
26506         }
26507         if(!this.disable.format){
26508             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26509             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26510             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26511             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
26512         }
26513         if(!this.disable.alignments){
26514             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26515             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26516             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26517         }
26518         if(!Roo.isSafari && !this.disable.lists){
26519             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26520             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26521         }
26522         
26523         var ans = this.editorcore.getAllAncestors();
26524         if (this.formatCombo) {
26525             
26526             
26527             var store = this.formatCombo.store;
26528             this.formatCombo.setValue("");
26529             for (var i =0; i < ans.length;i++) {
26530                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26531                     // select it..
26532                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26533                     break;
26534                 }
26535             }
26536         }
26537         
26538         
26539         
26540         // hides menus... - so this cant be on a menu...
26541         Roo.menu.MenuMgr.hideAll();
26542
26543         //this.editorsyncValue();
26544     },
26545    
26546     
26547     createFontOptions : function(){
26548         var buf = [], fs = this.fontFamilies, ff, lc;
26549         
26550         
26551         
26552         for(var i = 0, len = fs.length; i< len; i++){
26553             ff = fs[i];
26554             lc = ff.toLowerCase();
26555             buf.push(
26556                 '<option value="',lc,'" style="font-family:',ff,';"',
26557                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26558                     ff,
26559                 '</option>'
26560             );
26561         }
26562         return buf.join('');
26563     },
26564     
26565     toggleSourceEdit : function(sourceEditMode){
26566         
26567         Roo.log("toolbar toogle");
26568         if(sourceEditMode === undefined){
26569             sourceEditMode = !this.sourceEditMode;
26570         }
26571         this.sourceEditMode = sourceEditMode === true;
26572         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
26573         // just toggle the button?
26574         if(btn.pressed !== this.sourceEditMode){
26575             btn.toggle(this.sourceEditMode);
26576             return;
26577         }
26578         
26579         if(sourceEditMode){
26580             Roo.log("disabling buttons");
26581             this.tb.items.each(function(item){
26582                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
26583                     item.disable();
26584                 }
26585             });
26586           
26587         }else{
26588             Roo.log("enabling buttons");
26589             if(this.editorcore.initialized){
26590                 this.tb.items.each(function(item){
26591                     item.enable();
26592                 });
26593                 // initialize 'blocks'
26594                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
26595                     Roo.htmleditor.Block.factory(e).updateElement(e);
26596                 },this);
26597             
26598             }
26599             
26600         }
26601         Roo.log("calling toggole on editor");
26602         // tell the editor that it's been pressed..
26603         this.editor.toggleSourceEdit(sourceEditMode);
26604        
26605     },
26606      /**
26607      * Object collection of toolbar tooltips for the buttons in the editor. The key
26608      * is the command id associated with that button and the value is a valid QuickTips object.
26609      * For example:
26610 <pre><code>
26611 {
26612     bold : {
26613         title: 'Bold (Ctrl+B)',
26614         text: 'Make the selected text bold.',
26615         cls: 'x-html-editor-tip'
26616     },
26617     italic : {
26618         title: 'Italic (Ctrl+I)',
26619         text: 'Make the selected text italic.',
26620         cls: 'x-html-editor-tip'
26621     },
26622     ...
26623 </code></pre>
26624     * @type Object
26625      */
26626     buttonTips : {
26627         bold : {
26628             title: 'Bold (Ctrl+B)',
26629             text: 'Make the selected text bold.',
26630             cls: 'x-html-editor-tip'
26631         },
26632         italic : {
26633             title: 'Italic (Ctrl+I)',
26634             text: 'Make the selected text italic.',
26635             cls: 'x-html-editor-tip'
26636         },
26637         underline : {
26638             title: 'Underline (Ctrl+U)',
26639             text: 'Underline the selected text.',
26640             cls: 'x-html-editor-tip'
26641         },
26642         strikethrough : {
26643             title: 'Strikethrough',
26644             text: 'Strikethrough the selected text.',
26645             cls: 'x-html-editor-tip'
26646         },
26647         increasefontsize : {
26648             title: 'Grow Text',
26649             text: 'Increase the font size.',
26650             cls: 'x-html-editor-tip'
26651         },
26652         decreasefontsize : {
26653             title: 'Shrink Text',
26654             text: 'Decrease the font size.',
26655             cls: 'x-html-editor-tip'
26656         },
26657         backcolor : {
26658             title: 'Text Highlight Color',
26659             text: 'Change the background color of the selected text.',
26660             cls: 'x-html-editor-tip'
26661         },
26662         forecolor : {
26663             title: 'Font Color',
26664             text: 'Change the color of the selected text.',
26665             cls: 'x-html-editor-tip'
26666         },
26667         justifyleft : {
26668             title: 'Align Text Left',
26669             text: 'Align text to the left.',
26670             cls: 'x-html-editor-tip'
26671         },
26672         justifycenter : {
26673             title: 'Center Text',
26674             text: 'Center text in the editor.',
26675             cls: 'x-html-editor-tip'
26676         },
26677         justifyright : {
26678             title: 'Align Text Right',
26679             text: 'Align text to the right.',
26680             cls: 'x-html-editor-tip'
26681         },
26682         insertunorderedlist : {
26683             title: 'Bullet List',
26684             text: 'Start a bulleted list.',
26685             cls: 'x-html-editor-tip'
26686         },
26687         insertorderedlist : {
26688             title: 'Numbered List',
26689             text: 'Start a numbered list.',
26690             cls: 'x-html-editor-tip'
26691         },
26692         createlink : {
26693             title: 'Hyperlink',
26694             text: 'Make the selected text a hyperlink.',
26695             cls: 'x-html-editor-tip'
26696         },
26697         sourceedit : {
26698             title: 'Source Edit',
26699             text: 'Switch to source editing mode.',
26700             cls: 'x-html-editor-tip'
26701         }
26702     },
26703     // private
26704     onDestroy : function(){
26705         if(this.rendered){
26706             
26707             this.tb.items.each(function(item){
26708                 if(item.menu){
26709                     item.menu.removeAll();
26710                     if(item.menu.el){
26711                         item.menu.el.destroy();
26712                     }
26713                 }
26714                 item.destroy();
26715             });
26716              
26717         }
26718     },
26719     onFirstFocus: function() {
26720         this.tb.items.each(function(item){
26721            item.enable();
26722         });
26723     }
26724 });
26725
26726
26727
26728
26729 // <script type="text/javascript">
26730 /*
26731  * Based on
26732  * Ext JS Library 1.1.1
26733  * Copyright(c) 2006-2007, Ext JS, LLC.
26734  *  
26735  
26736  */
26737
26738  
26739 /**
26740  * @class Roo.form.HtmlEditor.ToolbarContext
26741  * Context Toolbar
26742  * 
26743  * Usage:
26744  *
26745  new Roo.form.HtmlEditor({
26746     ....
26747     toolbars : [
26748         { xtype: 'ToolbarStandard', styles : {} }
26749         { xtype: 'ToolbarContext', disable : {} }
26750     ]
26751 })
26752
26753      
26754  * 
26755  * @config : {Object} disable List of elements to disable.. (not done yet.)
26756  * @config : {Object} styles  Map of styles available.
26757  * 
26758  */
26759
26760 Roo.form.HtmlEditor.ToolbarContext = function(config)
26761 {
26762     
26763     Roo.apply(this, config);
26764     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26765     // dont call parent... till later.
26766     this.styles = this.styles || {};
26767 }
26768
26769  
26770
26771 Roo.form.HtmlEditor.ToolbarContext.types = {
26772     'IMG' : [
26773         {
26774             name : 'width',
26775             title: "Width",
26776             width: 40
26777         },
26778         {
26779             name : 'height',
26780             title: "Height",
26781             width: 40
26782         },
26783         {
26784             name : 'align',
26785             title: "Align",
26786             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26787             width : 80
26788             
26789         },
26790         {
26791             name : 'border',
26792             title: "Border",
26793             width: 40
26794         },
26795         {
26796             name : 'alt',
26797             title: "Alt",
26798             width: 120
26799         },
26800         {
26801             name : 'src',
26802             title: "Src",
26803             width: 220
26804         }
26805         
26806     ],
26807     
26808     'FIGURE' : [
26809         {
26810             name : 'align',
26811             title: "Align",
26812             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26813             width : 80  
26814         }
26815     ],
26816     'A' : [
26817         {
26818             name : 'name',
26819             title: "Name",
26820             width: 50
26821         },
26822         {
26823             name : 'target',
26824             title: "Target",
26825             width: 120
26826         },
26827         {
26828             name : 'href',
26829             title: "Href",
26830             width: 220
26831         } // border?
26832         
26833     ],
26834     
26835     'INPUT' : [
26836         {
26837             name : 'name',
26838             title: "name",
26839             width: 120
26840         },
26841         {
26842             name : 'value',
26843             title: "Value",
26844             width: 120
26845         },
26846         {
26847             name : 'width',
26848             title: "Width",
26849             width: 40
26850         }
26851     ],
26852     'LABEL' : [
26853          {
26854             name : 'for',
26855             title: "For",
26856             width: 120
26857         }
26858     ],
26859     'TEXTAREA' : [
26860         {
26861             name : 'name',
26862             title: "name",
26863             width: 120
26864         },
26865         {
26866             name : 'rows',
26867             title: "Rows",
26868             width: 20
26869         },
26870         {
26871             name : 'cols',
26872             title: "Cols",
26873             width: 20
26874         }
26875     ],
26876     'SELECT' : [
26877         {
26878             name : 'name',
26879             title: "name",
26880             width: 120
26881         },
26882         {
26883             name : 'selectoptions',
26884             title: "Options",
26885             width: 200
26886         }
26887     ],
26888     
26889     // should we really allow this??
26890     // should this just be 
26891     'BODY' : [
26892         
26893         {
26894             name : 'title',
26895             title: "Title",
26896             width: 200,
26897             disabled : true
26898         }
26899     ],
26900  
26901     '*' : [
26902         // empty.
26903     ]
26904
26905 };
26906
26907 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
26908 Roo.form.HtmlEditor.ToolbarContext.stores = false;
26909
26910 Roo.form.HtmlEditor.ToolbarContext.options = {
26911         'font-family'  : [ 
26912                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
26913                 [ 'Courier New', 'Courier New'],
26914                 [ 'Tahoma', 'Tahoma'],
26915                 [ 'Times New Roman,serif', 'Times'],
26916                 [ 'Verdana','Verdana' ]
26917         ]
26918 };
26919
26920 // fixme - these need to be configurable..
26921  
26922
26923 //Roo.form.HtmlEditor.ToolbarContext.types
26924
26925
26926 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26927     
26928     tb: false,
26929     
26930     rendered: false,
26931     
26932     editor : false,
26933     editorcore : false,
26934     /**
26935      * @cfg {Object} disable  List of toolbar elements to disable
26936          
26937      */
26938     disable : false,
26939     /**
26940      * @cfg {Object} styles List of styles 
26941      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26942      *
26943      * These must be defined in the page, so they get rendered correctly..
26944      * .headline { }
26945      * TD.underline { }
26946      * 
26947      */
26948     styles : false,
26949     
26950     options: false,
26951     
26952     toolbars : false,
26953     
26954     init : function(editor)
26955     {
26956         this.editor = editor;
26957         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26958         var editorcore = this.editorcore;
26959         
26960         var fid = editorcore.frameId;
26961         var etb = this;
26962         function btn(id, toggle, handler){
26963             var xid = fid + '-'+ id ;
26964             return {
26965                 id : xid,
26966                 cmd : id,
26967                 cls : 'x-btn-icon x-edit-'+id,
26968                 enableToggle:toggle !== false,
26969                 scope: editorcore, // was editor...
26970                 handler:handler||editorcore.relayBtnCmd,
26971                 clickEvent:'mousedown',
26972                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26973                 tabIndex:-1
26974             };
26975         }
26976         // create a new element.
26977         var wdiv = editor.wrap.createChild({
26978                 tag: 'div'
26979             }, editor.wrap.dom.firstChild.nextSibling, true);
26980         
26981         // can we do this more than once??
26982         
26983          // stop form submits
26984       
26985  
26986         // disable everything...
26987         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26988         this.toolbars = {};
26989         // block toolbars are built in updateToolbar when needed.
26990         for (var i in  ty) {
26991             
26992             this.toolbars[i] = this.buildToolbar(ty[i],i);
26993         }
26994         this.tb = this.toolbars.BODY;
26995         this.tb.el.show();
26996         this.buildFooter();
26997         this.footer.show();
26998         editor.on('hide', function( ) { this.footer.hide() }, this);
26999         editor.on('show', function( ) { this.footer.show() }, this);
27000         
27001          
27002         this.rendered = true;
27003         
27004         // the all the btns;
27005         editor.on('editorevent', this.updateToolbar, this);
27006         // other toolbars need to implement this..
27007         //editor.on('editmodechange', this.updateToolbar, this);
27008     },
27009     
27010     
27011     
27012     /**
27013      * Protected method that will not generally be called directly. It triggers
27014      * a toolbar update by reading the markup state of the current selection in the editor.
27015      *
27016      * Note you can force an update by calling on('editorevent', scope, false)
27017      */
27018     updateToolbar: function(editor ,ev, sel)
27019     {
27020         
27021         if (ev) {
27022             ev.stopEvent(); // se if we can stop this looping with mutiple events.
27023         }
27024         
27025         //Roo.log(ev);
27026         // capture mouse up - this is handy for selecting images..
27027         // perhaps should go somewhere else...
27028         if(!this.editorcore.activated){
27029              this.editor.onFirstFocus();
27030             return;
27031         }
27032         //Roo.log(ev ? ev.target : 'NOTARGET');
27033         
27034         
27035         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27036         // selectNode - might want to handle IE?
27037         
27038         
27039         
27040         if (ev &&
27041             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27042             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
27043             // they have click on an image...
27044             // let's see if we can change the selection...
27045             sel = ev.target;
27046             
27047             // this triggers looping?
27048             //this.editorcore.selectNode(sel);
27049              
27050         }
27051         
27052         // this forces an id..
27053         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
27054              e.classList.remove('roo-ed-selection');
27055         });
27056         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
27057         //Roo.get(node).addClass('roo-ed-selection');
27058       
27059         //var updateFooter = sel ? false : true; 
27060         
27061         
27062         var ans = this.editorcore.getAllAncestors();
27063         
27064         // pick
27065         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
27066         
27067         if (!sel) { 
27068             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27069             sel = sel ? sel : this.editorcore.doc.body;
27070             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27071             
27072         }
27073         
27074         var tn = sel.tagName.toUpperCase();
27075         var lastSel = this.tb.selectedNode;
27076         this.tb.selectedNode = sel;
27077         var left_label = tn;
27078         
27079         // ok see if we are editing a block?
27080         
27081         var db = false;
27082         // you are not actually selecting the block.
27083         if (sel && sel.hasAttribute('data-block')) {
27084             db = sel;
27085         } else if (sel && sel.closest('[data-block]')) {
27086             
27087             db = sel.closest('[data-block]');
27088             //var cepar = sel.closest('[contenteditable=true]');
27089             //if (db && cepar && cepar.tagName != 'BODY') {
27090             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
27091             //}   
27092         }
27093         
27094         
27095         var block = false;
27096         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
27097         if (db && this.editorcore.enableBlocks) {
27098             block = Roo.htmleditor.Block.factory(db);
27099             
27100             
27101             if (block) {
27102                  db.className = (
27103                         db.classList.length > 0  ? db.className + ' ' : ''
27104                     )  + 'roo-ed-selection';
27105                  
27106                  // since we removed it earlier... its not there..
27107                 tn = 'BLOCK.' + db.getAttribute('data-block');
27108                 
27109                 //this.editorcore.selectNode(db);
27110                 if (typeof(this.toolbars[tn]) == 'undefined') {
27111                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
27112                 }
27113                 this.toolbars[tn].selectedNode = db;
27114                 left_label = block.friendly_name;
27115                 ans = this.editorcore.getAllAncestors();
27116             }
27117             
27118                 
27119             
27120         }
27121         
27122         
27123         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
27124             return; // no change?
27125         }
27126         
27127         
27128           
27129         this.tb.el.hide();
27130         ///console.log("show: " + tn);
27131         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27132         
27133         this.tb.el.show();
27134         // update name
27135         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
27136         
27137         
27138         // update attributes
27139         if (block && this.tb.fields) {
27140              
27141             this.tb.fields.each(function(e) {
27142                 e.setValue(block[e.name]);
27143             });
27144             
27145             
27146         } else  if (this.tb.fields && this.tb.selectedNode) {
27147             this.tb.fields.each( function(e) {
27148                 if (e.stylename) {
27149                     e.setValue(this.tb.selectedNode.style[e.stylename]);
27150                     return;
27151                 } 
27152                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
27153             }, this);
27154             this.updateToolbarStyles(this.tb.selectedNode);  
27155         }
27156         
27157         
27158        
27159         Roo.menu.MenuMgr.hideAll();
27160
27161         
27162         
27163     
27164         // update the footer
27165         //
27166         this.updateFooter(ans);
27167              
27168     },
27169     
27170     updateToolbarStyles : function(sel)
27171     {
27172         var hasStyles = false;
27173         for(var i in this.styles) {
27174             hasStyles = true;
27175             break;
27176         }
27177         
27178         // update styles
27179         if (hasStyles && this.tb.hasStyles) { 
27180             var st = this.tb.fields.item(0);
27181             
27182             st.store.removeAll();
27183             var cn = sel.className.split(/\s+/);
27184             
27185             var avs = [];
27186             if (this.styles['*']) {
27187                 
27188                 Roo.each(this.styles['*'], function(v) {
27189                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27190                 });
27191             }
27192             if (this.styles[tn]) { 
27193                 Roo.each(this.styles[tn], function(v) {
27194                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27195                 });
27196             }
27197             
27198             st.store.loadData(avs);
27199             st.collapse();
27200             st.setValue(cn);
27201         }
27202     },
27203     
27204      
27205     updateFooter : function(ans)
27206     {
27207         var html = '';
27208         if (ans === false) {
27209             this.footDisp.dom.innerHTML = '';
27210             return;
27211         }
27212         
27213         this.footerEls = ans.reverse();
27214         Roo.each(this.footerEls, function(a,i) {
27215             if (!a) { return; }
27216             html += html.length ? ' &gt; '  :  '';
27217             
27218             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27219             
27220         });
27221        
27222         // 
27223         var sz = this.footDisp.up('td').getSize();
27224         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27225         this.footDisp.dom.style.marginLeft = '5px';
27226         
27227         this.footDisp.dom.style.overflow = 'hidden';
27228         
27229         this.footDisp.dom.innerHTML = html;
27230             
27231         
27232     },
27233    
27234        
27235     // private
27236     onDestroy : function(){
27237         if(this.rendered){
27238             
27239             this.tb.items.each(function(item){
27240                 if(item.menu){
27241                     item.menu.removeAll();
27242                     if(item.menu.el){
27243                         item.menu.el.destroy();
27244                     }
27245                 }
27246                 item.destroy();
27247             });
27248              
27249         }
27250     },
27251     onFirstFocus: function() {
27252         // need to do this for all the toolbars..
27253         this.tb.items.each(function(item){
27254            item.enable();
27255         });
27256     },
27257     buildToolbar: function(tlist, nm, friendly_name, block)
27258     {
27259         var editor = this.editor;
27260         var editorcore = this.editorcore;
27261          // create a new element.
27262         var wdiv = editor.wrap.createChild({
27263                 tag: 'div'
27264             }, editor.wrap.dom.firstChild.nextSibling, true);
27265         
27266        
27267         var tb = new Roo.Toolbar(wdiv);
27268         ///this.tb = tb; // << this sets the active toolbar..
27269         if (tlist === false && block) {
27270             tlist = block.contextMenu(this);
27271         }
27272         
27273         tb.hasStyles = false;
27274         tb.name = nm;
27275         
27276         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
27277         
27278         var styles = Array.from(this.styles);
27279         
27280         
27281         // styles...
27282         if (styles && styles.length) {
27283             tb.hasStyles = true;
27284             // this needs a multi-select checkbox...
27285             tb.addField( new Roo.form.ComboBox({
27286                 store: new Roo.data.SimpleStore({
27287                     id : 'val',
27288                     fields: ['val', 'selected'],
27289                     data : [] 
27290                 }),
27291                 name : '-roo-edit-className',
27292                 attrname : 'className',
27293                 displayField: 'val',
27294                 typeAhead: false,
27295                 mode: 'local',
27296                 editable : false,
27297                 triggerAction: 'all',
27298                 emptyText:'Select Style',
27299                 selectOnFocus:true,
27300                 width: 130,
27301                 listeners : {
27302                     'select': function(c, r, i) {
27303                         // initial support only for on class per el..
27304                         tb.selectedNode.className =  r ? r.get('val') : '';
27305                         editorcore.syncValue();
27306                     }
27307                 }
27308     
27309             }));
27310         }
27311         
27312         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27313         
27314         
27315         for (var i = 0; i < tlist.length; i++) {
27316             
27317             // newer versions will use xtype cfg to create menus.
27318             if (typeof(tlist[i].xtype) != 'undefined') {
27319                 
27320                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
27321                 
27322                 
27323                 continue;
27324             }
27325             
27326             var item = tlist[i];
27327             tb.add(item.title + ":&nbsp;");
27328             
27329             
27330             //optname == used so you can configure the options available..
27331             var opts = item.opts ? item.opts : false;
27332             if (item.optname) { // use the b
27333                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
27334            
27335             }
27336             
27337             if (opts) {
27338                 // opts == pulldown..
27339                 tb.addField( new Roo.form.ComboBox({
27340                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27341                         id : 'val',
27342                         fields: ['val', 'display'],
27343                         data : opts  
27344                     }),
27345                     name : '-roo-edit-' + tlist[i].name,
27346                     
27347                     attrname : tlist[i].name,
27348                     stylename : item.style ? item.style : false,
27349                     
27350                     displayField: item.displayField ? item.displayField : 'val',
27351                     valueField :  'val',
27352                     typeAhead: false,
27353                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
27354                     editable : false,
27355                     triggerAction: 'all',
27356                     emptyText:'Select',
27357                     selectOnFocus:true,
27358                     width: item.width ? item.width  : 130,
27359                     listeners : {
27360                         'select': function(c, r, i) {
27361                              
27362                             
27363                             if (c.stylename) {
27364                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27365                                 editorcore.syncValue();
27366                                 return;
27367                             }
27368                             if (r === false) {
27369                                 tb.selectedNode.removeAttribute(c.attrname);
27370                                 editorcore.syncValue();
27371                                 return;
27372                             }
27373                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27374                             editorcore.syncValue();
27375                         }
27376                     }
27377
27378                 }));
27379                 continue;
27380                     
27381                  
27382                 /*
27383                 tb.addField( new Roo.form.TextField({
27384                     name: i,
27385                     width: 100,
27386                     //allowBlank:false,
27387                     value: ''
27388                 }));
27389                 continue;
27390                 */
27391             }
27392             tb.addField( new Roo.form.TextField({
27393                 name: '-roo-edit-' + tlist[i].name,
27394                 attrname : tlist[i].name,
27395                 
27396                 width: item.width,
27397                 //allowBlank:true,
27398                 value: '',
27399                 listeners: {
27400                     'change' : function(f, nv, ov) {
27401                         
27402                          
27403                         tb.selectedNode.setAttribute(f.attrname, nv);
27404                         editorcore.syncValue();
27405                     }
27406                 }
27407             }));
27408              
27409         }
27410         
27411         var _this = this;
27412         var show_delete = !block || block.deleteTitle !== false;
27413         if(nm == 'BODY'){
27414             show_delete = false;
27415             tb.addSeparator();
27416         
27417             tb.addButton( {
27418                 text: 'Stylesheets',
27419
27420                 listeners : {
27421                     click : function ()
27422                     {
27423                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
27424                     }
27425                 }
27426             });
27427         }
27428         
27429         tb.addFill();
27430         if (show_delete) {
27431             tb.addButton({
27432                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
27433         
27434                 listeners : {
27435                     click : function ()
27436                     {
27437                         var sn = tb.selectedNode;
27438                         if (block) {
27439                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
27440                             
27441                         }
27442                         if (!sn) {
27443                             return;
27444                         }
27445                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
27446                         if (sn.hasAttribute('data-block')) {
27447                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
27448                             sn.parentNode.removeChild(sn);
27449                             
27450                         } else if (sn && sn.tagName != 'BODY') {
27451                             // remove and keep parents.
27452                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
27453                             a.replaceTag(sn);
27454                         }
27455                         
27456                         
27457                         var range = editorcore.createRange();
27458             
27459                         range.setStart(stn,0);
27460                         range.setEnd(stn,0); 
27461                         var selection = editorcore.getSelection();
27462                         selection.removeAllRanges();
27463                         selection.addRange(range);
27464                         
27465                         
27466                         //_this.updateToolbar(null, null, pn);
27467                         _this.updateToolbar(null, null, null);
27468                         _this.updateFooter(false);
27469                         
27470                     }
27471                 }
27472                 
27473                         
27474                     
27475                 
27476             });
27477         }    
27478         
27479         tb.el.on('click', function(e){
27480             e.preventDefault(); // what does this do?
27481         });
27482         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27483         tb.el.hide();
27484         
27485         // dont need to disable them... as they will get hidden
27486         return tb;
27487          
27488         
27489     },
27490     buildFooter : function()
27491     {
27492         
27493         var fel = this.editor.wrap.createChild();
27494         this.footer = new Roo.Toolbar(fel);
27495         // toolbar has scrolly on left / right?
27496         var footDisp= new Roo.Toolbar.Fill();
27497         var _t = this;
27498         this.footer.add(
27499             {
27500                 text : '&lt;',
27501                 xtype: 'Button',
27502                 handler : function() {
27503                     _t.footDisp.scrollTo('left',0,true)
27504                 }
27505             }
27506         );
27507         this.footer.add( footDisp );
27508         this.footer.add( 
27509             {
27510                 text : '&gt;',
27511                 xtype: 'Button',
27512                 handler : function() {
27513                     // no animation..
27514                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27515                 }
27516             }
27517         );
27518         var fel = Roo.get(footDisp.el);
27519         fel.addClass('x-editor-context');
27520         this.footDispWrap = fel; 
27521         this.footDispWrap.overflow  = 'hidden';
27522         
27523         this.footDisp = fel.createChild();
27524         this.footDispWrap.on('click', this.onContextClick, this)
27525         
27526         
27527     },
27528     // when the footer contect changes
27529     onContextClick : function (ev,dom)
27530     {
27531         ev.preventDefault();
27532         var  cn = dom.className;
27533         //Roo.log(cn);
27534         if (!cn.match(/x-ed-loc-/)) {
27535             return;
27536         }
27537         var n = cn.split('-').pop();
27538         var ans = this.footerEls;
27539         var sel = ans[n];
27540         
27541         this.editorcore.selectNode(sel);
27542         
27543         
27544         this.updateToolbar(null, null, sel);
27545         
27546         
27547     }
27548     
27549     
27550     
27551     
27552     
27553 });
27554
27555
27556
27557
27558
27559 /*
27560  * Based on:
27561  * Ext JS Library 1.1.1
27562  * Copyright(c) 2006-2007, Ext JS, LLC.
27563  *
27564  * Originally Released Under LGPL - original licence link has changed is not relivant.
27565  *
27566  * Fork - LGPL
27567  * <script type="text/javascript">
27568  */
27569  
27570 /**
27571  * @class Roo.form.BasicForm
27572  * @extends Roo.util.Observable
27573  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27574  * @constructor
27575  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27576  * @param {Object} config Configuration options
27577  */
27578 Roo.form.BasicForm = function(el, config){
27579     this.allItems = [];
27580     this.childForms = [];
27581     Roo.apply(this, config);
27582     /*
27583      * The Roo.form.Field items in this form.
27584      * @type MixedCollection
27585      */
27586      
27587      
27588     this.items = new Roo.util.MixedCollection(false, function(o){
27589         return o.id || (o.id = Roo.id());
27590     });
27591     this.addEvents({
27592         /**
27593          * @event beforeaction
27594          * Fires before any action is performed. Return false to cancel the action.
27595          * @param {Form} this
27596          * @param {Action} action The action to be performed
27597          */
27598         beforeaction: true,
27599         /**
27600          * @event actionfailed
27601          * Fires when an action fails.
27602          * @param {Form} this
27603          * @param {Action} action The action that failed
27604          */
27605         actionfailed : true,
27606         /**
27607          * @event actioncomplete
27608          * Fires when an action is completed.
27609          * @param {Form} this
27610          * @param {Action} action The action that completed
27611          */
27612         actioncomplete : true
27613     });
27614     if(el){
27615         this.initEl(el);
27616     }
27617     Roo.form.BasicForm.superclass.constructor.call(this);
27618     
27619     Roo.form.BasicForm.popover.apply();
27620 };
27621
27622 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27623     /**
27624      * @cfg {String} method
27625      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27626      */
27627     /**
27628      * @cfg {DataReader} reader
27629      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27630      * This is optional as there is built-in support for processing JSON.
27631      */
27632     /**
27633      * @cfg {DataReader} errorReader
27634      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27635      * This is completely optional as there is built-in support for processing JSON.
27636      */
27637     /**
27638      * @cfg {String} url
27639      * The URL to use for form actions if one isn't supplied in the action options.
27640      */
27641     /**
27642      * @cfg {Boolean} fileUpload
27643      * Set to true if this form is a file upload.
27644      */
27645      
27646     /**
27647      * @cfg {Object} baseParams
27648      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27649      */
27650      /**
27651      
27652     /**
27653      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27654      */
27655     timeout: 30,
27656
27657     // private
27658     activeAction : null,
27659
27660     /**
27661      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27662      * or setValues() data instead of when the form was first created.
27663      */
27664     trackResetOnLoad : false,
27665     
27666     
27667     /**
27668      * childForms - used for multi-tab forms
27669      * @type {Array}
27670      */
27671     childForms : false,
27672     
27673     /**
27674      * allItems - full list of fields.
27675      * @type {Array}
27676      */
27677     allItems : false,
27678     
27679     /**
27680      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27681      * element by passing it or its id or mask the form itself by passing in true.
27682      * @type Mixed
27683      */
27684     waitMsgTarget : false,
27685     
27686     /**
27687      * @type Boolean
27688      */
27689     disableMask : false,
27690     
27691     /**
27692      * @cfg {Boolean} errorMask (true|false) default false
27693      */
27694     errorMask : false,
27695     
27696     /**
27697      * @cfg {Number} maskOffset Default 100
27698      */
27699     maskOffset : 100,
27700
27701     // private
27702     initEl : function(el){
27703         this.el = Roo.get(el);
27704         this.id = this.el.id || Roo.id();
27705         this.el.on('submit', this.onSubmit, this);
27706         this.el.addClass('x-form');
27707     },
27708
27709     // private
27710     onSubmit : function(e){
27711         e.stopEvent();
27712     },
27713
27714     /**
27715      * Returns true if client-side validation on the form is successful.
27716      * @return Boolean
27717      */
27718     isValid : function(){
27719         var valid = true;
27720         var target = false;
27721         this.items.each(function(f){
27722             if(f.validate()){
27723                 return;
27724             }
27725             
27726             valid = false;
27727                 
27728             if(!target && f.el.isVisible(true)){
27729                 target = f;
27730             }
27731         });
27732         
27733         if(this.errorMask && !valid){
27734             Roo.form.BasicForm.popover.mask(this, target);
27735         }
27736         
27737         return valid;
27738     },
27739     /**
27740      * Returns array of invalid form fields.
27741      * @return Array
27742      */
27743     
27744     invalidFields : function()
27745     {
27746         var ret = [];
27747         this.items.each(function(f){
27748             if(f.validate()){
27749                 return;
27750             }
27751             ret.push(f);
27752             
27753         });
27754         
27755         return ret;
27756     },
27757     
27758     
27759     /**
27760      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
27761      * @return Boolean
27762      */
27763     isDirty : function(){
27764         var dirty = false;
27765         this.items.each(function(f){
27766            if(f.isDirty()){
27767                dirty = true;
27768                return false;
27769            }
27770         });
27771         return dirty;
27772     },
27773     
27774     /**
27775      * Returns true if any fields in this form have changed since their original load. (New version)
27776      * @return Boolean
27777      */
27778     
27779     hasChanged : function()
27780     {
27781         var dirty = false;
27782         this.items.each(function(f){
27783            if(f.hasChanged()){
27784                dirty = true;
27785                return false;
27786            }
27787         });
27788         return dirty;
27789         
27790     },
27791     /**
27792      * Resets all hasChanged to 'false' -
27793      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
27794      * So hasChanged storage is only to be used for this purpose
27795      * @return Boolean
27796      */
27797     resetHasChanged : function()
27798     {
27799         this.items.each(function(f){
27800            f.resetHasChanged();
27801         });
27802         
27803     },
27804     
27805     
27806     /**
27807      * Performs a predefined action (submit or load) or custom actions you define on this form.
27808      * @param {String} actionName The name of the action type
27809      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27810      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27811      * accept other config options):
27812      * <pre>
27813 Property          Type             Description
27814 ----------------  ---------------  ----------------------------------------------------------------------------------
27815 url               String           The url for the action (defaults to the form's url)
27816 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27817 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27818 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27819                                    validate the form on the client (defaults to false)
27820      * </pre>
27821      * @return {BasicForm} this
27822      */
27823     doAction : function(action, options){
27824         if(typeof action == 'string'){
27825             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27826         }
27827         if(this.fireEvent('beforeaction', this, action) !== false){
27828             this.beforeAction(action);
27829             action.run.defer(100, action);
27830         }
27831         return this;
27832     },
27833
27834     /**
27835      * Shortcut to do a submit action.
27836      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27837      * @return {BasicForm} this
27838      */
27839     submit : function(options){
27840         this.doAction('submit', options);
27841         return this;
27842     },
27843
27844     /**
27845      * Shortcut to do a load action.
27846      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27847      * @return {BasicForm} this
27848      */
27849     load : function(options){
27850         this.doAction('load', options);
27851         return this;
27852     },
27853
27854     /**
27855      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27856      * @param {Record} record The record to edit
27857      * @return {BasicForm} this
27858      */
27859     updateRecord : function(record){
27860         record.beginEdit();
27861         var fs = record.fields;
27862         fs.each(function(f){
27863             var field = this.findField(f.name);
27864             if(field){
27865                 record.set(f.name, field.getValue());
27866             }
27867         }, this);
27868         record.endEdit();
27869         return this;
27870     },
27871
27872     /**
27873      * Loads an Roo.data.Record into this form.
27874      * @param {Record} record The record to load
27875      * @return {BasicForm} this
27876      */
27877     loadRecord : function(record){
27878         this.setValues(record.data);
27879         return this;
27880     },
27881
27882     // private
27883     beforeAction : function(action){
27884         var o = action.options;
27885         
27886         if(!this.disableMask) {
27887             if(this.waitMsgTarget === true){
27888                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27889             }else if(this.waitMsgTarget){
27890                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27891                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27892             }else {
27893                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27894             }
27895         }
27896         
27897          
27898     },
27899
27900     // private
27901     afterAction : function(action, success){
27902         this.activeAction = null;
27903         var o = action.options;
27904         
27905         if(!this.disableMask) {
27906             if(this.waitMsgTarget === true){
27907                 this.el.unmask();
27908             }else if(this.waitMsgTarget){
27909                 this.waitMsgTarget.unmask();
27910             }else{
27911                 Roo.MessageBox.updateProgress(1);
27912                 Roo.MessageBox.hide();
27913             }
27914         }
27915         
27916         if(success){
27917             if(o.reset){
27918                 this.reset();
27919             }
27920             Roo.callback(o.success, o.scope, [this, action]);
27921             this.fireEvent('actioncomplete', this, action);
27922             
27923         }else{
27924             
27925             // failure condition..
27926             // we have a scenario where updates need confirming.
27927             // eg. if a locking scenario exists..
27928             // we look for { errors : { needs_confirm : true }} in the response.
27929             if (
27930                 (typeof(action.result) != 'undefined')  &&
27931                 (typeof(action.result.errors) != 'undefined')  &&
27932                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27933            ){
27934                 var _t = this;
27935                 Roo.MessageBox.confirm(
27936                     "Change requires confirmation",
27937                     action.result.errorMsg,
27938                     function(r) {
27939                         if (r != 'yes') {
27940                             return;
27941                         }
27942                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27943                     }
27944                     
27945                 );
27946                 
27947                 
27948                 
27949                 return;
27950             }
27951             
27952             Roo.callback(o.failure, o.scope, [this, action]);
27953             // show an error message if no failed handler is set..
27954             if (!this.hasListener('actionfailed')) {
27955                 Roo.MessageBox.alert("Error",
27956                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27957                         action.result.errorMsg :
27958                         "Saving Failed, please check your entries or try again"
27959                 );
27960             }
27961             
27962             this.fireEvent('actionfailed', this, action);
27963         }
27964         
27965     },
27966
27967     /**
27968      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27969      * @param {String} id The value to search for
27970      * @return Field
27971      */
27972     findField : function(id){
27973         var field = this.items.get(id);
27974         if(!field){
27975             this.items.each(function(f){
27976                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27977                     field = f;
27978                     return false;
27979                 }
27980             });
27981         }
27982         return field || null;
27983     },
27984
27985     /**
27986      * Add a secondary form to this one, 
27987      * Used to provide tabbed forms. One form is primary, with hidden values 
27988      * which mirror the elements from the other forms.
27989      * 
27990      * @param {Roo.form.Form} form to add.
27991      * 
27992      */
27993     addForm : function(form)
27994     {
27995        
27996         if (this.childForms.indexOf(form) > -1) {
27997             // already added..
27998             return;
27999         }
28000         this.childForms.push(form);
28001         var n = '';
28002         Roo.each(form.allItems, function (fe) {
28003             
28004             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28005             if (this.findField(n)) { // already added..
28006                 return;
28007             }
28008             var add = new Roo.form.Hidden({
28009                 name : n
28010             });
28011             add.render(this.el);
28012             
28013             this.add( add );
28014         }, this);
28015         
28016     },
28017     /**
28018      * Mark fields in this form invalid in bulk.
28019      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28020      * @return {BasicForm} this
28021      */
28022     markInvalid : function(errors){
28023         if(errors instanceof Array){
28024             for(var i = 0, len = errors.length; i < len; i++){
28025                 var fieldError = errors[i];
28026                 var f = this.findField(fieldError.id);
28027                 if(f){
28028                     f.markInvalid(fieldError.msg);
28029                 }
28030             }
28031         }else{
28032             var field, id;
28033             for(id in errors){
28034                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28035                     field.markInvalid(errors[id]);
28036                 }
28037             }
28038         }
28039         Roo.each(this.childForms || [], function (f) {
28040             f.markInvalid(errors);
28041         });
28042         
28043         return this;
28044     },
28045
28046     /**
28047      * Set values for fields in this form in bulk.
28048      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28049      * @return {BasicForm} this
28050      */
28051     setValues : function(values){
28052         if(values instanceof Array){ // array of objects
28053             for(var i = 0, len = values.length; i < len; i++){
28054                 var v = values[i];
28055                 var f = this.findField(v.id);
28056                 if(f){
28057                     f.setValue(v.value);
28058                     if(this.trackResetOnLoad){
28059                         f.originalValue = f.getValue();
28060                     }
28061                 }
28062             }
28063         }else{ // object hash
28064             var field, id;
28065             for(id in values){
28066                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28067                     
28068                     if (field.setFromData && 
28069                         field.valueField && 
28070                         field.displayField &&
28071                         // combos' with local stores can 
28072                         // be queried via setValue()
28073                         // to set their value..
28074                         (field.store && !field.store.isLocal)
28075                         ) {
28076                         // it's a combo
28077                         var sd = { };
28078                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28079                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28080                         field.setFromData(sd);
28081                         
28082                     } else {
28083                         field.setValue(values[id]);
28084                     }
28085                     
28086                     
28087                     if(this.trackResetOnLoad){
28088                         field.originalValue = field.getValue();
28089                     }
28090                 }
28091             }
28092         }
28093         this.resetHasChanged();
28094         
28095         
28096         Roo.each(this.childForms || [], function (f) {
28097             f.setValues(values);
28098             f.resetHasChanged();
28099         });
28100                 
28101         return this;
28102     },
28103  
28104     /**
28105      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28106      * they are returned as an array.
28107      * @param {Boolean} asString
28108      * @return {Object}
28109      */
28110     getValues : function(asString)
28111     {
28112         if (this.childForms) {
28113             // copy values from the child forms
28114             Roo.each(this.childForms, function (f) {
28115                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
28116             }, this);
28117         }
28118         
28119         // use formdata
28120         if (typeof(FormData) != 'undefined' && asString !== true) {
28121             // this relies on a 'recent' version of chrome apparently...
28122             try {
28123                 var fd = (new FormData(this.el.dom)).entries();
28124                 var ret = {};
28125                 var ent = fd.next();
28126                 while (!ent.done) {
28127                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
28128                     ent = fd.next();
28129                 };
28130                 return ret;
28131             } catch(e) {
28132                 
28133             }
28134             
28135         }
28136         
28137         
28138         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28139         if(asString === true){
28140             return fs;
28141         }
28142         return Roo.urlDecode(fs);
28143     },
28144     
28145     /**
28146      * Returns the fields in this form as an object with key/value pairs. 
28147      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28148      * Normally this will not return readOnly data 
28149      * @param {Boolean} with_readonly return readonly field data.
28150      * @return {Object}
28151      */
28152     getFieldValues : function(with_readonly)
28153     {
28154         if (this.childForms) {
28155             // copy values from the child forms
28156             // should this call getFieldValues - probably not as we do not currently copy
28157             // hidden fields when we generate..
28158             Roo.each(this.childForms, function (f) {
28159                 this.setValues(f.getFieldValues());
28160             }, this);
28161         }
28162         
28163         var ret = {};
28164         this.items.each(function(f){
28165             
28166             if (f.readOnly && with_readonly !== true) {
28167                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
28168                         // if a subform contains a copy of them.
28169                         // if you have subforms with the same editable data, you will need to copy the data back
28170                         // and forth.
28171             }
28172             
28173             if (!f.getName()) {
28174                 return;
28175             }
28176             var v = f.getValue();
28177             if (f.inputType =='radio') {
28178                 if (typeof(ret[f.getName()]) == 'undefined') {
28179                     ret[f.getName()] = ''; // empty..
28180                 }
28181                 
28182                 if (!f.el.dom.checked) {
28183                     return;
28184                     
28185                 }
28186                 v = f.el.dom.value;
28187                 
28188             }
28189             
28190             // not sure if this supported any more..
28191             if ((typeof(v) == 'object') && f.getRawValue) {
28192                 v = f.getRawValue() ; // dates..
28193             }
28194             // combo boxes where name != hiddenName...
28195             if (f.name != f.getName()) {
28196                 ret[f.name] = f.getRawValue();
28197             }
28198             ret[f.getName()] = v;
28199         });
28200         
28201         return ret;
28202     },
28203
28204     /**
28205      * Clears all invalid messages in this form.
28206      * @return {BasicForm} this
28207      */
28208     clearInvalid : function(){
28209         this.items.each(function(f){
28210            f.clearInvalid();
28211         });
28212         
28213         Roo.each(this.childForms || [], function (f) {
28214             f.clearInvalid();
28215         });
28216         
28217         
28218         return this;
28219     },
28220
28221     /**
28222      * Resets this form.
28223      * @return {BasicForm} this
28224      */
28225     reset : function(){
28226         this.items.each(function(f){
28227             f.reset();
28228         });
28229         
28230         Roo.each(this.childForms || [], function (f) {
28231             f.reset();
28232         });
28233         this.resetHasChanged();
28234         
28235         return this;
28236     },
28237
28238     /**
28239      * Add Roo.form components to this form.
28240      * @param {Field} field1
28241      * @param {Field} field2 (optional)
28242      * @param {Field} etc (optional)
28243      * @return {BasicForm} this
28244      */
28245     add : function(){
28246         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28247         return this;
28248     },
28249
28250
28251     /**
28252      * Removes a field from the items collection (does NOT remove its markup).
28253      * @param {Field} field
28254      * @return {BasicForm} this
28255      */
28256     remove : function(field){
28257         this.items.remove(field);
28258         return this;
28259     },
28260
28261     /**
28262      * Looks at the fields in this form, checks them for an id attribute,
28263      * and calls applyTo on the existing dom element with that id.
28264      * @return {BasicForm} this
28265      */
28266     render : function(){
28267         this.items.each(function(f){
28268             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28269                 f.applyTo(f.id);
28270             }
28271         });
28272         return this;
28273     },
28274
28275     /**
28276      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28277      * @param {Object} values
28278      * @return {BasicForm} this
28279      */
28280     applyToFields : function(o){
28281         this.items.each(function(f){
28282            Roo.apply(f, o);
28283         });
28284         return this;
28285     },
28286
28287     /**
28288      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28289      * @param {Object} values
28290      * @return {BasicForm} this
28291      */
28292     applyIfToFields : function(o){
28293         this.items.each(function(f){
28294            Roo.applyIf(f, o);
28295         });
28296         return this;
28297     }
28298 });
28299
28300 // back compat
28301 Roo.BasicForm = Roo.form.BasicForm;
28302
28303 Roo.apply(Roo.form.BasicForm, {
28304     
28305     popover : {
28306         
28307         padding : 5,
28308         
28309         isApplied : false,
28310         
28311         isMasked : false,
28312         
28313         form : false,
28314         
28315         target : false,
28316         
28317         intervalID : false,
28318         
28319         maskEl : false,
28320         
28321         apply : function()
28322         {
28323             if(this.isApplied){
28324                 return;
28325             }
28326             
28327             this.maskEl = {
28328                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
28329                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
28330                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
28331                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
28332             };
28333             
28334             this.maskEl.top.enableDisplayMode("block");
28335             this.maskEl.left.enableDisplayMode("block");
28336             this.maskEl.bottom.enableDisplayMode("block");
28337             this.maskEl.right.enableDisplayMode("block");
28338             
28339             Roo.get(document.body).on('click', function(){
28340                 this.unmask();
28341             }, this);
28342             
28343             Roo.get(document.body).on('touchstart', function(){
28344                 this.unmask();
28345             }, this);
28346             
28347             this.isApplied = true
28348         },
28349         
28350         mask : function(form, target)
28351         {
28352             this.form = form;
28353             
28354             this.target = target;
28355             
28356             if(!this.form.errorMask || !target.el){
28357                 return;
28358             }
28359             
28360             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
28361             
28362             var ot = this.target.el.calcOffsetsTo(scrollable);
28363             
28364             var scrollTo = ot[1] - this.form.maskOffset;
28365             
28366             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
28367             
28368             scrollable.scrollTo('top', scrollTo);
28369             
28370             var el = this.target.wrap || this.target.el;
28371             
28372             var box = el.getBox();
28373             
28374             this.maskEl.top.setStyle('position', 'absolute');
28375             this.maskEl.top.setStyle('z-index', 10000);
28376             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
28377             this.maskEl.top.setLeft(0);
28378             this.maskEl.top.setTop(0);
28379             this.maskEl.top.show();
28380             
28381             this.maskEl.left.setStyle('position', 'absolute');
28382             this.maskEl.left.setStyle('z-index', 10000);
28383             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
28384             this.maskEl.left.setLeft(0);
28385             this.maskEl.left.setTop(box.y - this.padding);
28386             this.maskEl.left.show();
28387
28388             this.maskEl.bottom.setStyle('position', 'absolute');
28389             this.maskEl.bottom.setStyle('z-index', 10000);
28390             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
28391             this.maskEl.bottom.setLeft(0);
28392             this.maskEl.bottom.setTop(box.bottom + this.padding);
28393             this.maskEl.bottom.show();
28394
28395             this.maskEl.right.setStyle('position', 'absolute');
28396             this.maskEl.right.setStyle('z-index', 10000);
28397             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
28398             this.maskEl.right.setLeft(box.right + this.padding);
28399             this.maskEl.right.setTop(box.y - this.padding);
28400             this.maskEl.right.show();
28401
28402             this.intervalID = window.setInterval(function() {
28403                 Roo.form.BasicForm.popover.unmask();
28404             }, 10000);
28405
28406             window.onwheel = function(){ return false;};
28407             
28408             (function(){ this.isMasked = true; }).defer(500, this);
28409             
28410         },
28411         
28412         unmask : function()
28413         {
28414             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
28415                 return;
28416             }
28417             
28418             this.maskEl.top.setStyle('position', 'absolute');
28419             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
28420             this.maskEl.top.hide();
28421
28422             this.maskEl.left.setStyle('position', 'absolute');
28423             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
28424             this.maskEl.left.hide();
28425
28426             this.maskEl.bottom.setStyle('position', 'absolute');
28427             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
28428             this.maskEl.bottom.hide();
28429
28430             this.maskEl.right.setStyle('position', 'absolute');
28431             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
28432             this.maskEl.right.hide();
28433             
28434             window.onwheel = function(){ return true;};
28435             
28436             if(this.intervalID){
28437                 window.clearInterval(this.intervalID);
28438                 this.intervalID = false;
28439             }
28440             
28441             this.isMasked = false;
28442             
28443         }
28444         
28445     }
28446     
28447 });/*
28448  * Based on:
28449  * Ext JS Library 1.1.1
28450  * Copyright(c) 2006-2007, Ext JS, LLC.
28451  *
28452  * Originally Released Under LGPL - original licence link has changed is not relivant.
28453  *
28454  * Fork - LGPL
28455  * <script type="text/javascript">
28456  */
28457
28458 /**
28459  * @class Roo.form.Form
28460  * @extends Roo.form.BasicForm
28461  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
28462  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28463  * @constructor
28464  * @param {Object} config Configuration options
28465  */
28466 Roo.form.Form = function(config){
28467     var xitems =  [];
28468     if (config.items) {
28469         xitems = config.items;
28470         delete config.items;
28471     }
28472    
28473     
28474     Roo.form.Form.superclass.constructor.call(this, null, config);
28475     this.url = this.url || this.action;
28476     if(!this.root){
28477         this.root = new Roo.form.Layout(Roo.applyIf({
28478             id: Roo.id()
28479         }, config));
28480     }
28481     this.active = this.root;
28482     /**
28483      * Array of all the buttons that have been added to this form via {@link addButton}
28484      * @type Array
28485      */
28486     this.buttons = [];
28487     this.allItems = [];
28488     this.addEvents({
28489         /**
28490          * @event clientvalidation
28491          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28492          * @param {Form} this
28493          * @param {Boolean} valid true if the form has passed client-side validation
28494          */
28495         clientvalidation: true,
28496         /**
28497          * @event rendered
28498          * Fires when the form is rendered
28499          * @param {Roo.form.Form} form
28500          */
28501         rendered : true
28502     });
28503     
28504     if (this.progressUrl) {
28505             // push a hidden field onto the list of fields..
28506             this.addxtype( {
28507                     xns: Roo.form, 
28508                     xtype : 'Hidden', 
28509                     name : 'UPLOAD_IDENTIFIER' 
28510             });
28511         }
28512         
28513     
28514     Roo.each(xitems, this.addxtype, this);
28515     
28516 };
28517
28518 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28519      /**
28520      * @cfg {Roo.Button} buttons[] buttons at bottom of form
28521      */
28522     
28523     /**
28524      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28525      */
28526     /**
28527      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28528      */
28529     /**
28530      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28531      */
28532     buttonAlign:'center',
28533
28534     /**
28535      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28536      */
28537     minButtonWidth:75,
28538
28539     /**
28540      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28541      * This property cascades to child containers if not set.
28542      */
28543     labelAlign:'left',
28544
28545     /**
28546      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28547      * fires a looping event with that state. This is required to bind buttons to the valid
28548      * state using the config value formBind:true on the button.
28549      */
28550     monitorValid : false,
28551
28552     /**
28553      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28554      */
28555     monitorPoll : 200,
28556     
28557     /**
28558      * @cfg {String} progressUrl - Url to return progress data 
28559      */
28560     
28561     progressUrl : false,
28562     /**
28563      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
28564      * sending a formdata with extra parameters - eg uploaded elements.
28565      */
28566     
28567     formData : false,
28568     
28569     /**
28570      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28571      * fields are added and the column is closed. If no fields are passed the column remains open
28572      * until end() is called.
28573      * @param {Object} config The config to pass to the column
28574      * @param {Field} field1 (optional)
28575      * @param {Field} field2 (optional)
28576      * @param {Field} etc (optional)
28577      * @return Column The column container object
28578      */
28579     column : function(c){
28580         var col = new Roo.form.Column(c);
28581         this.start(col);
28582         if(arguments.length > 1){ // duplicate code required because of Opera
28583             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28584             this.end();
28585         }
28586         return col;
28587     },
28588
28589     /**
28590      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28591      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28592      * until end() is called.
28593      * @param {Object} config The config to pass to the fieldset
28594      * @param {Field} field1 (optional)
28595      * @param {Field} field2 (optional)
28596      * @param {Field} etc (optional)
28597      * @return FieldSet The fieldset container object
28598      */
28599     fieldset : function(c){
28600         var fs = new Roo.form.FieldSet(c);
28601         this.start(fs);
28602         if(arguments.length > 1){ // duplicate code required because of Opera
28603             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28604             this.end();
28605         }
28606         return fs;
28607     },
28608
28609     /**
28610      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28611      * fields are added and the container is closed. If no fields are passed the container remains open
28612      * until end() is called.
28613      * @param {Object} config The config to pass to the Layout
28614      * @param {Field} field1 (optional)
28615      * @param {Field} field2 (optional)
28616      * @param {Field} etc (optional)
28617      * @return Layout The container object
28618      */
28619     container : function(c){
28620         var l = new Roo.form.Layout(c);
28621         this.start(l);
28622         if(arguments.length > 1){ // duplicate code required because of Opera
28623             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28624             this.end();
28625         }
28626         return l;
28627     },
28628
28629     /**
28630      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28631      * @param {Object} container A Roo.form.Layout or subclass of Layout
28632      * @return {Form} this
28633      */
28634     start : function(c){
28635         // cascade label info
28636         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28637         this.active.stack.push(c);
28638         c.ownerCt = this.active;
28639         this.active = c;
28640         return this;
28641     },
28642
28643     /**
28644      * Closes the current open container
28645      * @return {Form} this
28646      */
28647     end : function(){
28648         if(this.active == this.root){
28649             return this;
28650         }
28651         this.active = this.active.ownerCt;
28652         return this;
28653     },
28654
28655     /**
28656      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28657      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28658      * as the label of the field.
28659      * @param {Field} field1
28660      * @param {Field} field2 (optional)
28661      * @param {Field} etc. (optional)
28662      * @return {Form} this
28663      */
28664     add : function(){
28665         this.active.stack.push.apply(this.active.stack, arguments);
28666         this.allItems.push.apply(this.allItems,arguments);
28667         var r = [];
28668         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28669             if(a[i].isFormField){
28670                 r.push(a[i]);
28671             }
28672         }
28673         if(r.length > 0){
28674             Roo.form.Form.superclass.add.apply(this, r);
28675         }
28676         return this;
28677     },
28678     
28679
28680     
28681     
28682     
28683      /**
28684      * Find any element that has been added to a form, using it's ID or name
28685      * This can include framesets, columns etc. along with regular fields..
28686      * @param {String} id - id or name to find.
28687      
28688      * @return {Element} e - or false if nothing found.
28689      */
28690     findbyId : function(id)
28691     {
28692         var ret = false;
28693         if (!id) {
28694             return ret;
28695         }
28696         Roo.each(this.allItems, function(f){
28697             if (f.id == id || f.name == id ){
28698                 ret = f;
28699                 return false;
28700             }
28701         });
28702         return ret;
28703     },
28704
28705     
28706     
28707     /**
28708      * Render this form into the passed container. This should only be called once!
28709      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28710      * @return {Form} this
28711      */
28712     render : function(ct)
28713     {
28714         
28715         
28716         
28717         ct = Roo.get(ct);
28718         var o = this.autoCreate || {
28719             tag: 'form',
28720             method : this.method || 'POST',
28721             id : this.id || Roo.id()
28722         };
28723         this.initEl(ct.createChild(o));
28724
28725         this.root.render(this.el);
28726         
28727        
28728              
28729         this.items.each(function(f){
28730             f.render('x-form-el-'+f.id);
28731         });
28732
28733         if(this.buttons.length > 0){
28734             // tables are required to maintain order and for correct IE layout
28735             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28736                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28737                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28738             }}, null, true);
28739             var tr = tb.getElementsByTagName('tr')[0];
28740             for(var i = 0, len = this.buttons.length; i < len; i++) {
28741                 var b = this.buttons[i];
28742                 var td = document.createElement('td');
28743                 td.className = 'x-form-btn-td';
28744                 b.render(tr.appendChild(td));
28745             }
28746         }
28747         if(this.monitorValid){ // initialize after render
28748             this.startMonitoring();
28749         }
28750         this.fireEvent('rendered', this);
28751         return this;
28752     },
28753
28754     /**
28755      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28756      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28757      * object or a valid Roo.DomHelper element config
28758      * @param {Function} handler The function called when the button is clicked
28759      * @param {Object} scope (optional) The scope of the handler function
28760      * @return {Roo.Button}
28761      */
28762     addButton : function(config, handler, scope){
28763         var bc = {
28764             handler: handler,
28765             scope: scope,
28766             minWidth: this.minButtonWidth,
28767             hideParent:true
28768         };
28769         if(typeof config == "string"){
28770             bc.text = config;
28771         }else{
28772             Roo.apply(bc, config);
28773         }
28774         var btn = new Roo.Button(null, bc);
28775         this.buttons.push(btn);
28776         return btn;
28777     },
28778
28779      /**
28780      * Adds a series of form elements (using the xtype property as the factory method.
28781      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28782      * @param {Object} config 
28783      */
28784     
28785     addxtype : function()
28786     {
28787         var ar = Array.prototype.slice.call(arguments, 0);
28788         var ret = false;
28789         for(var i = 0; i < ar.length; i++) {
28790             if (!ar[i]) {
28791                 continue; // skip -- if this happends something invalid got sent, we 
28792                 // should ignore it, as basically that interface element will not show up
28793                 // and that should be pretty obvious!!
28794             }
28795             
28796             if (Roo.form[ar[i].xtype]) {
28797                 ar[i].form = this;
28798                 var fe = Roo.factory(ar[i], Roo.form);
28799                 if (!ret) {
28800                     ret = fe;
28801                 }
28802                 fe.form = this;
28803                 if (fe.store) {
28804                     fe.store.form = this;
28805                 }
28806                 if (fe.isLayout) {  
28807                          
28808                     this.start(fe);
28809                     this.allItems.push(fe);
28810                     if (fe.items && fe.addxtype) {
28811                         fe.addxtype.apply(fe, fe.items);
28812                         delete fe.items;
28813                     }
28814                      this.end();
28815                     continue;
28816                 }
28817                 
28818                 
28819                  
28820                 this.add(fe);
28821               //  console.log('adding ' + ar[i].xtype);
28822             }
28823             if (ar[i].xtype == 'Button') {  
28824                 //console.log('adding button');
28825                 //console.log(ar[i]);
28826                 this.addButton(ar[i]);
28827                 this.allItems.push(fe);
28828                 continue;
28829             }
28830             
28831             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28832                 alert('end is not supported on xtype any more, use items');
28833             //    this.end();
28834             //    //console.log('adding end');
28835             }
28836             
28837         }
28838         return ret;
28839     },
28840     
28841     /**
28842      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28843      * option "monitorValid"
28844      */
28845     startMonitoring : function(){
28846         if(!this.bound){
28847             this.bound = true;
28848             Roo.TaskMgr.start({
28849                 run : this.bindHandler,
28850                 interval : this.monitorPoll || 200,
28851                 scope: this
28852             });
28853         }
28854     },
28855
28856     /**
28857      * Stops monitoring of the valid state of this form
28858      */
28859     stopMonitoring : function(){
28860         this.bound = false;
28861     },
28862
28863     // private
28864     bindHandler : function(){
28865         if(!this.bound){
28866             return false; // stops binding
28867         }
28868         var valid = true;
28869         this.items.each(function(f){
28870             if(!f.isValid(true)){
28871                 valid = false;
28872                 return false;
28873             }
28874         });
28875         for(var i = 0, len = this.buttons.length; i < len; i++){
28876             var btn = this.buttons[i];
28877             if(btn.formBind === true && btn.disabled === valid){
28878                 btn.setDisabled(!valid);
28879             }
28880         }
28881         this.fireEvent('clientvalidation', this, valid);
28882     }
28883     
28884     
28885     
28886     
28887     
28888     
28889     
28890     
28891 });
28892
28893
28894 // back compat
28895 Roo.Form = Roo.form.Form;
28896 /*
28897  * Based on:
28898  * Ext JS Library 1.1.1
28899  * Copyright(c) 2006-2007, Ext JS, LLC.
28900  *
28901  * Originally Released Under LGPL - original licence link has changed is not relivant.
28902  *
28903  * Fork - LGPL
28904  * <script type="text/javascript">
28905  */
28906
28907 // as we use this in bootstrap.
28908 Roo.namespace('Roo.form');
28909  /**
28910  * @class Roo.form.Action
28911  * Internal Class used to handle form actions
28912  * @constructor
28913  * @param {Roo.form.BasicForm} el The form element or its id
28914  * @param {Object} config Configuration options
28915  */
28916
28917  
28918  
28919 // define the action interface
28920 Roo.form.Action = function(form, options){
28921     this.form = form;
28922     this.options = options || {};
28923 };
28924 /**
28925  * Client Validation Failed
28926  * @const 
28927  */
28928 Roo.form.Action.CLIENT_INVALID = 'client';
28929 /**
28930  * Server Validation Failed
28931  * @const 
28932  */
28933 Roo.form.Action.SERVER_INVALID = 'server';
28934  /**
28935  * Connect to Server Failed
28936  * @const 
28937  */
28938 Roo.form.Action.CONNECT_FAILURE = 'connect';
28939 /**
28940  * Reading Data from Server Failed
28941  * @const 
28942  */
28943 Roo.form.Action.LOAD_FAILURE = 'load';
28944
28945 Roo.form.Action.prototype = {
28946     type : 'default',
28947     failureType : undefined,
28948     response : undefined,
28949     result : undefined,
28950
28951     // interface method
28952     run : function(options){
28953
28954     },
28955
28956     // interface method
28957     success : function(response){
28958
28959     },
28960
28961     // interface method
28962     handleResponse : function(response){
28963
28964     },
28965
28966     // default connection failure
28967     failure : function(response){
28968         
28969         this.response = response;
28970         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28971         this.form.afterAction(this, false);
28972     },
28973
28974     processResponse : function(response){
28975         this.response = response;
28976         if(!response.responseText){
28977             return true;
28978         }
28979         this.result = this.handleResponse(response);
28980         return this.result;
28981     },
28982
28983     // utility functions used internally
28984     getUrl : function(appendParams){
28985         var url = this.options.url || this.form.url || this.form.el.dom.action;
28986         if(appendParams){
28987             var p = this.getParams();
28988             if(p){
28989                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28990             }
28991         }
28992         return url;
28993     },
28994
28995     getMethod : function(){
28996         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28997     },
28998
28999     getParams : function(){
29000         var bp = this.form.baseParams;
29001         var p = this.options.params;
29002         if(p){
29003             if(typeof p == "object"){
29004                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29005             }else if(typeof p == 'string' && bp){
29006                 p += '&' + Roo.urlEncode(bp);
29007             }
29008         }else if(bp){
29009             p = Roo.urlEncode(bp);
29010         }
29011         return p;
29012     },
29013
29014     createCallback : function(){
29015         return {
29016             success: this.success,
29017             failure: this.failure,
29018             scope: this,
29019             timeout: (this.form.timeout*1000),
29020             upload: this.form.fileUpload ? this.success : undefined
29021         };
29022     }
29023 };
29024
29025 Roo.form.Action.Submit = function(form, options){
29026     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29027 };
29028
29029 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29030     type : 'submit',
29031
29032     haveProgress : false,
29033     uploadComplete : false,
29034     
29035     // uploadProgress indicator.
29036     uploadProgress : function()
29037     {
29038         if (!this.form.progressUrl) {
29039             return;
29040         }
29041         
29042         if (!this.haveProgress) {
29043             Roo.MessageBox.progress("Uploading", "Uploading");
29044         }
29045         if (this.uploadComplete) {
29046            Roo.MessageBox.hide();
29047            return;
29048         }
29049         
29050         this.haveProgress = true;
29051    
29052         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29053         
29054         var c = new Roo.data.Connection();
29055         c.request({
29056             url : this.form.progressUrl,
29057             params: {
29058                 id : uid
29059             },
29060             method: 'GET',
29061             success : function(req){
29062                //console.log(data);
29063                 var rdata = false;
29064                 var edata;
29065                 try  {
29066                    rdata = Roo.decode(req.responseText)
29067                 } catch (e) {
29068                     Roo.log("Invalid data from server..");
29069                     Roo.log(edata);
29070                     return;
29071                 }
29072                 if (!rdata || !rdata.success) {
29073                     Roo.log(rdata);
29074                     Roo.MessageBox.alert(Roo.encode(rdata));
29075                     return;
29076                 }
29077                 var data = rdata.data;
29078                 
29079                 if (this.uploadComplete) {
29080                    Roo.MessageBox.hide();
29081                    return;
29082                 }
29083                    
29084                 if (data){
29085                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29086                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29087                     );
29088                 }
29089                 this.uploadProgress.defer(2000,this);
29090             },
29091        
29092             failure: function(data) {
29093                 Roo.log('progress url failed ');
29094                 Roo.log(data);
29095             },
29096             scope : this
29097         });
29098            
29099     },
29100     
29101     
29102     run : function()
29103     {
29104         // run get Values on the form, so it syncs any secondary forms.
29105         this.form.getValues();
29106         
29107         var o = this.options;
29108         var method = this.getMethod();
29109         var isPost = method == 'POST';
29110         if(o.clientValidation === false || this.form.isValid()){
29111             
29112             if (this.form.progressUrl) {
29113                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29114                     (new Date() * 1) + '' + Math.random());
29115                     
29116             } 
29117             
29118             
29119             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29120                 form:this.form.el.dom,
29121                 url:this.getUrl(!isPost),
29122                 method: method,
29123                 params:isPost ? this.getParams() : null,
29124                 isUpload: this.form.fileUpload,
29125                 formData : this.form.formData
29126             }));
29127             
29128             this.uploadProgress();
29129
29130         }else if (o.clientValidation !== false){ // client validation failed
29131             this.failureType = Roo.form.Action.CLIENT_INVALID;
29132             this.form.afterAction(this, false);
29133         }
29134     },
29135
29136     success : function(response)
29137     {
29138         this.uploadComplete= true;
29139         if (this.haveProgress) {
29140             Roo.MessageBox.hide();
29141         }
29142         
29143         
29144         var result = this.processResponse(response);
29145         if(result === true || result.success){
29146             this.form.afterAction(this, true);
29147             return;
29148         }
29149         if(result.errors){
29150             this.form.markInvalid(result.errors);
29151             this.failureType = Roo.form.Action.SERVER_INVALID;
29152         }
29153         this.form.afterAction(this, false);
29154     },
29155     failure : function(response)
29156     {
29157         this.uploadComplete= true;
29158         if (this.haveProgress) {
29159             Roo.MessageBox.hide();
29160         }
29161         
29162         this.response = response;
29163         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29164         this.form.afterAction(this, false);
29165     },
29166     
29167     handleResponse : function(response){
29168         if(this.form.errorReader){
29169             var rs = this.form.errorReader.read(response);
29170             var errors = [];
29171             if(rs.records){
29172                 for(var i = 0, len = rs.records.length; i < len; i++) {
29173                     var r = rs.records[i];
29174                     errors[i] = r.data;
29175                 }
29176             }
29177             if(errors.length < 1){
29178                 errors = null;
29179             }
29180             return {
29181                 success : rs.success,
29182                 errors : errors
29183             };
29184         }
29185         var ret = false;
29186         try {
29187             ret = Roo.decode(response.responseText);
29188         } catch (e) {
29189             ret = {
29190                 success: false,
29191                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29192                 errors : []
29193             };
29194         }
29195         return ret;
29196         
29197     }
29198 });
29199
29200
29201 Roo.form.Action.Load = function(form, options){
29202     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29203     this.reader = this.form.reader;
29204 };
29205
29206 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29207     type : 'load',
29208
29209     run : function(){
29210         
29211         Roo.Ajax.request(Roo.apply(
29212                 this.createCallback(), {
29213                     method:this.getMethod(),
29214                     url:this.getUrl(false),
29215                     params:this.getParams()
29216         }));
29217     },
29218
29219     success : function(response){
29220         
29221         var result = this.processResponse(response);
29222         if(result === true || !result.success || !result.data){
29223             this.failureType = Roo.form.Action.LOAD_FAILURE;
29224             this.form.afterAction(this, false);
29225             return;
29226         }
29227         this.form.clearInvalid();
29228         this.form.setValues(result.data);
29229         this.form.afterAction(this, true);
29230     },
29231
29232     handleResponse : function(response){
29233         if(this.form.reader){
29234             var rs = this.form.reader.read(response);
29235             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29236             return {
29237                 success : rs.success,
29238                 data : data
29239             };
29240         }
29241         return Roo.decode(response.responseText);
29242     }
29243 });
29244
29245 Roo.form.Action.ACTION_TYPES = {
29246     'load' : Roo.form.Action.Load,
29247     'submit' : Roo.form.Action.Submit
29248 };/*
29249  * Based on:
29250  * Ext JS Library 1.1.1
29251  * Copyright(c) 2006-2007, Ext JS, LLC.
29252  *
29253  * Originally Released Under LGPL - original licence link has changed is not relivant.
29254  *
29255  * Fork - LGPL
29256  * <script type="text/javascript">
29257  */
29258  
29259 /**
29260  * @class Roo.form.Layout
29261  * @extends Roo.Component
29262  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
29263  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29264  * @constructor
29265  * @param {Object} config Configuration options
29266  */
29267 Roo.form.Layout = function(config){
29268     var xitems = [];
29269     if (config.items) {
29270         xitems = config.items;
29271         delete config.items;
29272     }
29273     Roo.form.Layout.superclass.constructor.call(this, config);
29274     this.stack = [];
29275     Roo.each(xitems, this.addxtype, this);
29276      
29277 };
29278
29279 Roo.extend(Roo.form.Layout, Roo.Component, {
29280     /**
29281      * @cfg {String/Object} autoCreate
29282      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29283      */
29284     /**
29285      * @cfg {String/Object/Function} style
29286      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29287      * a function which returns such a specification.
29288      */
29289     /**
29290      * @cfg {String} labelAlign
29291      * Valid values are "left," "top" and "right" (defaults to "left")
29292      */
29293     /**
29294      * @cfg {Number} labelWidth
29295      * Fixed width in pixels of all field labels (defaults to undefined)
29296      */
29297     /**
29298      * @cfg {Boolean} clear
29299      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29300      */
29301     clear : true,
29302     /**
29303      * @cfg {String} labelSeparator
29304      * The separator to use after field labels (defaults to ':')
29305      */
29306     labelSeparator : ':',
29307     /**
29308      * @cfg {Boolean} hideLabels
29309      * True to suppress the display of field labels in this layout (defaults to false)
29310      */
29311     hideLabels : false,
29312
29313     // private
29314     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29315     
29316     isLayout : true,
29317     
29318     // private
29319     onRender : function(ct, position){
29320         if(this.el){ // from markup
29321             this.el = Roo.get(this.el);
29322         }else {  // generate
29323             var cfg = this.getAutoCreate();
29324             this.el = ct.createChild(cfg, position);
29325         }
29326         if(this.style){
29327             this.el.applyStyles(this.style);
29328         }
29329         if(this.labelAlign){
29330             this.el.addClass('x-form-label-'+this.labelAlign);
29331         }
29332         if(this.hideLabels){
29333             this.labelStyle = "display:none";
29334             this.elementStyle = "padding-left:0;";
29335         }else{
29336             if(typeof this.labelWidth == 'number'){
29337                 this.labelStyle = "width:"+this.labelWidth+"px;";
29338                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29339             }
29340             if(this.labelAlign == 'top'){
29341                 this.labelStyle = "width:auto;";
29342                 this.elementStyle = "padding-left:0;";
29343             }
29344         }
29345         var stack = this.stack;
29346         var slen = stack.length;
29347         if(slen > 0){
29348             if(!this.fieldTpl){
29349                 var t = new Roo.Template(
29350                     '<div class="x-form-item {5}">',
29351                         '<label for="{0}" style="{2}">{1}{4}</label>',
29352                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29353                         '</div>',
29354                     '</div><div class="x-form-clear-left"></div>'
29355                 );
29356                 t.disableFormats = true;
29357                 t.compile();
29358                 Roo.form.Layout.prototype.fieldTpl = t;
29359             }
29360             for(var i = 0; i < slen; i++) {
29361                 if(stack[i].isFormField){
29362                     this.renderField(stack[i]);
29363                 }else{
29364                     this.renderComponent(stack[i]);
29365                 }
29366             }
29367         }
29368         if(this.clear){
29369             this.el.createChild({cls:'x-form-clear'});
29370         }
29371     },
29372
29373     // private
29374     renderField : function(f){
29375         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29376                f.id, //0
29377                f.fieldLabel, //1
29378                f.labelStyle||this.labelStyle||'', //2
29379                this.elementStyle||'', //3
29380                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29381                f.itemCls||this.itemCls||''  //5
29382        ], true).getPrevSibling());
29383     },
29384
29385     // private
29386     renderComponent : function(c){
29387         c.render(c.isLayout ? this.el : this.el.createChild());    
29388     },
29389     /**
29390      * Adds a object form elements (using the xtype property as the factory method.)
29391      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29392      * @param {Object} config 
29393      */
29394     addxtype : function(o)
29395     {
29396         // create the lement.
29397         o.form = this.form;
29398         var fe = Roo.factory(o, Roo.form);
29399         this.form.allItems.push(fe);
29400         this.stack.push(fe);
29401         
29402         if (fe.isFormField) {
29403             this.form.items.add(fe);
29404         }
29405          
29406         return fe;
29407     }
29408 });
29409
29410 /**
29411  * @class Roo.form.Column
29412  * @extends Roo.form.Layout
29413  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
29414  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29415  * @constructor
29416  * @param {Object} config Configuration options
29417  */
29418 Roo.form.Column = function(config){
29419     Roo.form.Column.superclass.constructor.call(this, config);
29420 };
29421
29422 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29423     /**
29424      * @cfg {Number/String} width
29425      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29426      */
29427     /**
29428      * @cfg {String/Object} autoCreate
29429      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29430      */
29431
29432     // private
29433     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29434
29435     // private
29436     onRender : function(ct, position){
29437         Roo.form.Column.superclass.onRender.call(this, ct, position);
29438         if(this.width){
29439             this.el.setWidth(this.width);
29440         }
29441     }
29442 });
29443
29444
29445 /**
29446  * @class Roo.form.Row
29447  * @extends Roo.form.Layout
29448  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
29449  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29450  * @constructor
29451  * @param {Object} config Configuration options
29452  */
29453
29454  
29455 Roo.form.Row = function(config){
29456     Roo.form.Row.superclass.constructor.call(this, config);
29457 };
29458  
29459 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29460       /**
29461      * @cfg {Number/String} width
29462      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29463      */
29464     /**
29465      * @cfg {Number/String} height
29466      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29467      */
29468     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29469     
29470     padWidth : 20,
29471     // private
29472     onRender : function(ct, position){
29473         //console.log('row render');
29474         if(!this.rowTpl){
29475             var t = new Roo.Template(
29476                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29477                     '<label for="{0}" style="{2}">{1}{4}</label>',
29478                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29479                     '</div>',
29480                 '</div>'
29481             );
29482             t.disableFormats = true;
29483             t.compile();
29484             Roo.form.Layout.prototype.rowTpl = t;
29485         }
29486         this.fieldTpl = this.rowTpl;
29487         
29488         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29489         var labelWidth = 100;
29490         
29491         if ((this.labelAlign != 'top')) {
29492             if (typeof this.labelWidth == 'number') {
29493                 labelWidth = this.labelWidth
29494             }
29495             this.padWidth =  20 + labelWidth;
29496             
29497         }
29498         
29499         Roo.form.Column.superclass.onRender.call(this, ct, position);
29500         if(this.width){
29501             this.el.setWidth(this.width);
29502         }
29503         if(this.height){
29504             this.el.setHeight(this.height);
29505         }
29506     },
29507     
29508     // private
29509     renderField : function(f){
29510         f.fieldEl = this.fieldTpl.append(this.el, [
29511                f.id, f.fieldLabel,
29512                f.labelStyle||this.labelStyle||'',
29513                this.elementStyle||'',
29514                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29515                f.itemCls||this.itemCls||'',
29516                f.width ? f.width + this.padWidth : 160 + this.padWidth
29517        ],true);
29518     }
29519 });
29520  
29521
29522 /**
29523  * @class Roo.form.FieldSet
29524  * @extends Roo.form.Layout
29525  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29526  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29527  * @constructor
29528  * @param {Object} config Configuration options
29529  */
29530 Roo.form.FieldSet = function(config){
29531     Roo.form.FieldSet.superclass.constructor.call(this, config);
29532 };
29533
29534 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29535     /**
29536      * @cfg {String} legend
29537      * The text to display as the legend for the FieldSet (defaults to '')
29538      */
29539     /**
29540      * @cfg {String/Object} autoCreate
29541      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29542      */
29543
29544     // private
29545     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29546
29547     // private
29548     onRender : function(ct, position){
29549         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29550         if(this.legend){
29551             this.setLegend(this.legend);
29552         }
29553     },
29554
29555     // private
29556     setLegend : function(text){
29557         if(this.rendered){
29558             this.el.child('legend').update(text);
29559         }
29560     }
29561 });/*
29562  * Based on:
29563  * Ext JS Library 1.1.1
29564  * Copyright(c) 2006-2007, Ext JS, LLC.
29565  *
29566  * Originally Released Under LGPL - original licence link has changed is not relivant.
29567  *
29568  * Fork - LGPL
29569  * <script type="text/javascript">
29570  */
29571 /**
29572  * @class Roo.form.VTypes
29573  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29574  * @static
29575  */
29576 Roo.form.VTypes = function(){
29577     // closure these in so they are only created once.
29578     var alpha = /^[a-zA-Z_]+$/;
29579     var alphanum = /^[a-zA-Z0-9_]+$/;
29580     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
29581     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29582
29583     // All these messages and functions are configurable
29584     return {
29585         /**
29586          * The function used to validate email addresses
29587          * @param {String} value The email address
29588          */
29589         'email' : function(v){
29590             return email.test(v);
29591         },
29592         /**
29593          * The error text to display when the email validation function returns false
29594          * @type String
29595          */
29596         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29597         /**
29598          * The keystroke filter mask to be applied on email input
29599          * @type RegExp
29600          */
29601         'emailMask' : /[a-z0-9_\.\-@]/i,
29602
29603         /**
29604          * The function used to validate URLs
29605          * @param {String} value The URL
29606          */
29607         'url' : function(v){
29608             return url.test(v);
29609         },
29610         /**
29611          * The error text to display when the url validation function returns false
29612          * @type String
29613          */
29614         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29615         
29616         /**
29617          * The function used to validate alpha values
29618          * @param {String} value The value
29619          */
29620         'alpha' : function(v){
29621             return alpha.test(v);
29622         },
29623         /**
29624          * The error text to display when the alpha validation function returns false
29625          * @type String
29626          */
29627         'alphaText' : 'This field should only contain letters and _',
29628         /**
29629          * The keystroke filter mask to be applied on alpha input
29630          * @type RegExp
29631          */
29632         'alphaMask' : /[a-z_]/i,
29633
29634         /**
29635          * The function used to validate alphanumeric values
29636          * @param {String} value The value
29637          */
29638         'alphanum' : function(v){
29639             return alphanum.test(v);
29640         },
29641         /**
29642          * The error text to display when the alphanumeric validation function returns false
29643          * @type String
29644          */
29645         'alphanumText' : 'This field should only contain letters, numbers and _',
29646         /**
29647          * The keystroke filter mask to be applied on alphanumeric input
29648          * @type RegExp
29649          */
29650         'alphanumMask' : /[a-z0-9_]/i
29651     };
29652 }();//<script type="text/javascript">
29653
29654 /**
29655  * @class Roo.form.FCKeditor
29656  * @extends Roo.form.TextArea
29657  * Wrapper around the FCKEditor http://www.fckeditor.net
29658  * @constructor
29659  * Creates a new FCKeditor
29660  * @param {Object} config Configuration options
29661  */
29662 Roo.form.FCKeditor = function(config){
29663     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29664     this.addEvents({
29665          /**
29666          * @event editorinit
29667          * Fired when the editor is initialized - you can add extra handlers here..
29668          * @param {FCKeditor} this
29669          * @param {Object} the FCK object.
29670          */
29671         editorinit : true
29672     });
29673     
29674     
29675 };
29676 Roo.form.FCKeditor.editors = { };
29677 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29678 {
29679     //defaultAutoCreate : {
29680     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29681     //},
29682     // private
29683     /**
29684      * @cfg {Object} fck options - see fck manual for details.
29685      */
29686     fckconfig : false,
29687     
29688     /**
29689      * @cfg {Object} fck toolbar set (Basic or Default)
29690      */
29691     toolbarSet : 'Basic',
29692     /**
29693      * @cfg {Object} fck BasePath
29694      */ 
29695     basePath : '/fckeditor/',
29696     
29697     
29698     frame : false,
29699     
29700     value : '',
29701     
29702    
29703     onRender : function(ct, position)
29704     {
29705         if(!this.el){
29706             this.defaultAutoCreate = {
29707                 tag: "textarea",
29708                 style:"width:300px;height:60px;",
29709                 autocomplete: "new-password"
29710             };
29711         }
29712         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29713         /*
29714         if(this.grow){
29715             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29716             if(this.preventScrollbars){
29717                 this.el.setStyle("overflow", "hidden");
29718             }
29719             this.el.setHeight(this.growMin);
29720         }
29721         */
29722         //console.log('onrender' + this.getId() );
29723         Roo.form.FCKeditor.editors[this.getId()] = this;
29724          
29725
29726         this.replaceTextarea() ;
29727         
29728     },
29729     
29730     getEditor : function() {
29731         return this.fckEditor;
29732     },
29733     /**
29734      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29735      * @param {Mixed} value The value to set
29736      */
29737     
29738     
29739     setValue : function(value)
29740     {
29741         //console.log('setValue: ' + value);
29742         
29743         if(typeof(value) == 'undefined') { // not sure why this is happending...
29744             return;
29745         }
29746         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29747         
29748         //if(!this.el || !this.getEditor()) {
29749         //    this.value = value;
29750             //this.setValue.defer(100,this,[value]);    
29751         //    return;
29752         //} 
29753         
29754         if(!this.getEditor()) {
29755             return;
29756         }
29757         
29758         this.getEditor().SetData(value);
29759         
29760         //
29761
29762     },
29763
29764     /**
29765      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29766      * @return {Mixed} value The field value
29767      */
29768     getValue : function()
29769     {
29770         
29771         if (this.frame && this.frame.dom.style.display == 'none') {
29772             return Roo.form.FCKeditor.superclass.getValue.call(this);
29773         }
29774         
29775         if(!this.el || !this.getEditor()) {
29776            
29777            // this.getValue.defer(100,this); 
29778             return this.value;
29779         }
29780        
29781         
29782         var value=this.getEditor().GetData();
29783         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29784         return Roo.form.FCKeditor.superclass.getValue.call(this);
29785         
29786
29787     },
29788
29789     /**
29790      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29791      * @return {Mixed} value The field value
29792      */
29793     getRawValue : function()
29794     {
29795         if (this.frame && this.frame.dom.style.display == 'none') {
29796             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29797         }
29798         
29799         if(!this.el || !this.getEditor()) {
29800             //this.getRawValue.defer(100,this); 
29801             return this.value;
29802             return;
29803         }
29804         
29805         
29806         
29807         var value=this.getEditor().GetData();
29808         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29809         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29810          
29811     },
29812     
29813     setSize : function(w,h) {
29814         
29815         
29816         
29817         //if (this.frame && this.frame.dom.style.display == 'none') {
29818         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29819         //    return;
29820         //}
29821         //if(!this.el || !this.getEditor()) {
29822         //    this.setSize.defer(100,this, [w,h]); 
29823         //    return;
29824         //}
29825         
29826         
29827         
29828         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29829         
29830         this.frame.dom.setAttribute('width', w);
29831         this.frame.dom.setAttribute('height', h);
29832         this.frame.setSize(w,h);
29833         
29834     },
29835     
29836     toggleSourceEdit : function(value) {
29837         
29838       
29839          
29840         this.el.dom.style.display = value ? '' : 'none';
29841         this.frame.dom.style.display = value ?  'none' : '';
29842         
29843     },
29844     
29845     
29846     focus: function(tag)
29847     {
29848         if (this.frame.dom.style.display == 'none') {
29849             return Roo.form.FCKeditor.superclass.focus.call(this);
29850         }
29851         if(!this.el || !this.getEditor()) {
29852             this.focus.defer(100,this, [tag]); 
29853             return;
29854         }
29855         
29856         
29857         
29858         
29859         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29860         this.getEditor().Focus();
29861         if (tgs.length) {
29862             if (!this.getEditor().Selection.GetSelection()) {
29863                 this.focus.defer(100,this, [tag]); 
29864                 return;
29865             }
29866             
29867             
29868             var r = this.getEditor().EditorDocument.createRange();
29869             r.setStart(tgs[0],0);
29870             r.setEnd(tgs[0],0);
29871             this.getEditor().Selection.GetSelection().removeAllRanges();
29872             this.getEditor().Selection.GetSelection().addRange(r);
29873             this.getEditor().Focus();
29874         }
29875         
29876     },
29877     
29878     
29879     
29880     replaceTextarea : function()
29881     {
29882         if ( document.getElementById( this.getId() + '___Frame' ) ) {
29883             return ;
29884         }
29885         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29886         //{
29887             // We must check the elements firstly using the Id and then the name.
29888         var oTextarea = document.getElementById( this.getId() );
29889         
29890         var colElementsByName = document.getElementsByName( this.getId() ) ;
29891          
29892         oTextarea.style.display = 'none' ;
29893
29894         if ( oTextarea.tabIndex ) {            
29895             this.TabIndex = oTextarea.tabIndex ;
29896         }
29897         
29898         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29899         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29900         this.frame = Roo.get(this.getId() + '___Frame')
29901     },
29902     
29903     _getConfigHtml : function()
29904     {
29905         var sConfig = '' ;
29906
29907         for ( var o in this.fckconfig ) {
29908             sConfig += sConfig.length > 0  ? '&amp;' : '';
29909             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29910         }
29911
29912         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29913     },
29914     
29915     
29916     _getIFrameHtml : function()
29917     {
29918         var sFile = 'fckeditor.html' ;
29919         /* no idea what this is about..
29920         try
29921         {
29922             if ( (/fcksource=true/i).test( window.top.location.search ) )
29923                 sFile = 'fckeditor.original.html' ;
29924         }
29925         catch (e) { 
29926         */
29927
29928         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29929         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29930         
29931         
29932         var html = '<iframe id="' + this.getId() +
29933             '___Frame" src="' + sLink +
29934             '" width="' + this.width +
29935             '" height="' + this.height + '"' +
29936             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29937             ' frameborder="0" scrolling="no"></iframe>' ;
29938
29939         return html ;
29940     },
29941     
29942     _insertHtmlBefore : function( html, element )
29943     {
29944         if ( element.insertAdjacentHTML )       {
29945             // IE
29946             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29947         } else { // Gecko
29948             var oRange = document.createRange() ;
29949             oRange.setStartBefore( element ) ;
29950             var oFragment = oRange.createContextualFragment( html );
29951             element.parentNode.insertBefore( oFragment, element ) ;
29952         }
29953     }
29954     
29955     
29956   
29957     
29958     
29959     
29960     
29961
29962 });
29963
29964 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29965
29966 function FCKeditor_OnComplete(editorInstance){
29967     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29968     f.fckEditor = editorInstance;
29969     //console.log("loaded");
29970     f.fireEvent('editorinit', f, editorInstance);
29971
29972   
29973
29974  
29975
29976
29977
29978
29979
29980
29981
29982
29983
29984
29985
29986
29987
29988
29989
29990 //<script type="text/javascript">
29991 /**
29992  * @class Roo.form.GridField
29993  * @extends Roo.form.Field
29994  * Embed a grid (or editable grid into a form)
29995  * STATUS ALPHA
29996  * 
29997  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29998  * it needs 
29999  * xgrid.store = Roo.data.Store
30000  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30001  * xgrid.store.reader = Roo.data.JsonReader 
30002  * 
30003  * 
30004  * @constructor
30005  * Creates a new GridField
30006  * @param {Object} config Configuration options
30007  */
30008 Roo.form.GridField = function(config){
30009     Roo.form.GridField.superclass.constructor.call(this, config);
30010      
30011 };
30012
30013 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30014     /**
30015      * @cfg {Number} width  - used to restrict width of grid..
30016      */
30017     width : 100,
30018     /**
30019      * @cfg {Number} height - used to restrict height of grid..
30020      */
30021     height : 50,
30022      /**
30023      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30024          * 
30025          *}
30026      */
30027     xgrid : false, 
30028     /**
30029      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30030      * {tag: "input", type: "checkbox", autocomplete: "off"})
30031      */
30032    // defaultAutoCreate : { tag: 'div' },
30033     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30034     /**
30035      * @cfg {String} addTitle Text to include for adding a title.
30036      */
30037     addTitle : false,
30038     //
30039     onResize : function(){
30040         Roo.form.Field.superclass.onResize.apply(this, arguments);
30041     },
30042
30043     initEvents : function(){
30044         // Roo.form.Checkbox.superclass.initEvents.call(this);
30045         // has no events...
30046        
30047     },
30048
30049
30050     getResizeEl : function(){
30051         return this.wrap;
30052     },
30053
30054     getPositionEl : function(){
30055         return this.wrap;
30056     },
30057
30058     // private
30059     onRender : function(ct, position){
30060         
30061         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30062         var style = this.style;
30063         delete this.style;
30064         
30065         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30066         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30067         this.viewEl = this.wrap.createChild({ tag: 'div' });
30068         if (style) {
30069             this.viewEl.applyStyles(style);
30070         }
30071         if (this.width) {
30072             this.viewEl.setWidth(this.width);
30073         }
30074         if (this.height) {
30075             this.viewEl.setHeight(this.height);
30076         }
30077         //if(this.inputValue !== undefined){
30078         //this.setValue(this.value);
30079         
30080         
30081         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30082         
30083         
30084         this.grid.render();
30085         this.grid.getDataSource().on('remove', this.refreshValue, this);
30086         this.grid.getDataSource().on('update', this.refreshValue, this);
30087         this.grid.on('afteredit', this.refreshValue, this);
30088  
30089     },
30090      
30091     
30092     /**
30093      * Sets the value of the item. 
30094      * @param {String} either an object  or a string..
30095      */
30096     setValue : function(v){
30097         //this.value = v;
30098         v = v || []; // empty set..
30099         // this does not seem smart - it really only affects memoryproxy grids..
30100         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30101             var ds = this.grid.getDataSource();
30102             // assumes a json reader..
30103             var data = {}
30104             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30105             ds.loadData( data);
30106         }
30107         // clear selection so it does not get stale.
30108         if (this.grid.sm) { 
30109             this.grid.sm.clearSelections();
30110         }
30111         
30112         Roo.form.GridField.superclass.setValue.call(this, v);
30113         this.refreshValue();
30114         // should load data in the grid really....
30115     },
30116     
30117     // private
30118     refreshValue: function() {
30119          var val = [];
30120         this.grid.getDataSource().each(function(r) {
30121             val.push(r.data);
30122         });
30123         this.el.dom.value = Roo.encode(val);
30124     }
30125     
30126      
30127     
30128     
30129 });/*
30130  * Based on:
30131  * Ext JS Library 1.1.1
30132  * Copyright(c) 2006-2007, Ext JS, LLC.
30133  *
30134  * Originally Released Under LGPL - original licence link has changed is not relivant.
30135  *
30136  * Fork - LGPL
30137  * <script type="text/javascript">
30138  */
30139 /**
30140  * @class Roo.form.DisplayField
30141  * @extends Roo.form.Field
30142  * A generic Field to display non-editable data.
30143  * @cfg {Boolean} closable (true|false) default false
30144  * @constructor
30145  * Creates a new Display Field item.
30146  * @param {Object} config Configuration options
30147  */
30148 Roo.form.DisplayField = function(config){
30149     Roo.form.DisplayField.superclass.constructor.call(this, config);
30150     
30151     this.addEvents({
30152         /**
30153          * @event close
30154          * Fires after the click the close btn
30155              * @param {Roo.form.DisplayField} this
30156              */
30157         close : true
30158     });
30159 };
30160
30161 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30162     inputType:      'hidden',
30163     allowBlank:     true,
30164     readOnly:         true,
30165     
30166  
30167     /**
30168      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30169      */
30170     focusClass : undefined,
30171     /**
30172      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30173      */
30174     fieldClass: 'x-form-field',
30175     
30176      /**
30177      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30178      */
30179     valueRenderer: undefined,
30180     
30181     width: 100,
30182     /**
30183      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30184      * {tag: "input", type: "checkbox", autocomplete: "off"})
30185      */
30186      
30187  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30188  
30189     closable : false,
30190     
30191     onResize : function(){
30192         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30193         
30194     },
30195
30196     initEvents : function(){
30197         // Roo.form.Checkbox.superclass.initEvents.call(this);
30198         // has no events...
30199         
30200         if(this.closable){
30201             this.closeEl.on('click', this.onClose, this);
30202         }
30203        
30204     },
30205
30206
30207     getResizeEl : function(){
30208         return this.wrap;
30209     },
30210
30211     getPositionEl : function(){
30212         return this.wrap;
30213     },
30214
30215     // private
30216     onRender : function(ct, position){
30217         
30218         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30219         //if(this.inputValue !== undefined){
30220         this.wrap = this.el.wrap();
30221         
30222         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30223         
30224         if(this.closable){
30225             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
30226         }
30227         
30228         if (this.bodyStyle) {
30229             this.viewEl.applyStyles(this.bodyStyle);
30230         }
30231         //this.viewEl.setStyle('padding', '2px');
30232         
30233         this.setValue(this.value);
30234         
30235     },
30236 /*
30237     // private
30238     initValue : Roo.emptyFn,
30239
30240   */
30241
30242         // private
30243     onClick : function(){
30244         
30245     },
30246
30247     /**
30248      * Sets the checked state of the checkbox.
30249      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30250      */
30251     setValue : function(v){
30252         this.value = v;
30253         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30254         // this might be called before we have a dom element..
30255         if (!this.viewEl) {
30256             return;
30257         }
30258         this.viewEl.dom.innerHTML = html;
30259         Roo.form.DisplayField.superclass.setValue.call(this, v);
30260
30261     },
30262     
30263     onClose : function(e)
30264     {
30265         e.preventDefault();
30266         
30267         this.fireEvent('close', this);
30268     }
30269 });/*
30270  * 
30271  * Licence- LGPL
30272  * 
30273  */
30274
30275 /**
30276  * @class Roo.form.DayPicker
30277  * @extends Roo.form.Field
30278  * A Day picker show [M] [T] [W] ....
30279  * @constructor
30280  * Creates a new Day Picker
30281  * @param {Object} config Configuration options
30282  */
30283 Roo.form.DayPicker= function(config){
30284     Roo.form.DayPicker.superclass.constructor.call(this, config);
30285      
30286 };
30287
30288 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30289     /**
30290      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30291      */
30292     focusClass : undefined,
30293     /**
30294      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30295      */
30296     fieldClass: "x-form-field",
30297    
30298     /**
30299      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30300      * {tag: "input", type: "checkbox", autocomplete: "off"})
30301      */
30302     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
30303     
30304    
30305     actionMode : 'viewEl', 
30306     //
30307     // private
30308  
30309     inputType : 'hidden',
30310     
30311      
30312     inputElement: false, // real input element?
30313     basedOn: false, // ????
30314     
30315     isFormField: true, // not sure where this is needed!!!!
30316
30317     onResize : function(){
30318         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30319         if(!this.boxLabel){
30320             this.el.alignTo(this.wrap, 'c-c');
30321         }
30322     },
30323
30324     initEvents : function(){
30325         Roo.form.Checkbox.superclass.initEvents.call(this);
30326         this.el.on("click", this.onClick,  this);
30327         this.el.on("change", this.onClick,  this);
30328     },
30329
30330
30331     getResizeEl : function(){
30332         return this.wrap;
30333     },
30334
30335     getPositionEl : function(){
30336         return this.wrap;
30337     },
30338
30339     
30340     // private
30341     onRender : function(ct, position){
30342         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30343        
30344         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30345         
30346         var r1 = '<table><tr>';
30347         var r2 = '<tr class="x-form-daypick-icons">';
30348         for (var i=0; i < 7; i++) {
30349             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30350             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30351         }
30352         
30353         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30354         viewEl.select('img').on('click', this.onClick, this);
30355         this.viewEl = viewEl;   
30356         
30357         
30358         // this will not work on Chrome!!!
30359         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30360         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30361         
30362         
30363           
30364
30365     },
30366
30367     // private
30368     initValue : Roo.emptyFn,
30369
30370     /**
30371      * Returns the checked state of the checkbox.
30372      * @return {Boolean} True if checked, else false
30373      */
30374     getValue : function(){
30375         return this.el.dom.value;
30376         
30377     },
30378
30379         // private
30380     onClick : function(e){ 
30381         //this.setChecked(!this.checked);
30382         Roo.get(e.target).toggleClass('x-menu-item-checked');
30383         this.refreshValue();
30384         //if(this.el.dom.checked != this.checked){
30385         //    this.setValue(this.el.dom.checked);
30386        // }
30387     },
30388     
30389     // private
30390     refreshValue : function()
30391     {
30392         var val = '';
30393         this.viewEl.select('img',true).each(function(e,i,n)  {
30394             val += e.is(".x-menu-item-checked") ? String(n) : '';
30395         });
30396         this.setValue(val, true);
30397     },
30398
30399     /**
30400      * Sets the checked state of the checkbox.
30401      * On is always based on a string comparison between inputValue and the param.
30402      * @param {Boolean/String} value - the value to set 
30403      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30404      */
30405     setValue : function(v,suppressEvent){
30406         if (!this.el.dom) {
30407             return;
30408         }
30409         var old = this.el.dom.value ;
30410         this.el.dom.value = v;
30411         if (suppressEvent) {
30412             return ;
30413         }
30414          
30415         // update display..
30416         this.viewEl.select('img',true).each(function(e,i,n)  {
30417             
30418             var on = e.is(".x-menu-item-checked");
30419             var newv = v.indexOf(String(n)) > -1;
30420             if (on != newv) {
30421                 e.toggleClass('x-menu-item-checked');
30422             }
30423             
30424         });
30425         
30426         
30427         this.fireEvent('change', this, v, old);
30428         
30429         
30430     },
30431    
30432     // handle setting of hidden value by some other method!!?!?
30433     setFromHidden: function()
30434     {
30435         if(!this.el){
30436             return;
30437         }
30438         //console.log("SET FROM HIDDEN");
30439         //alert('setFrom hidden');
30440         this.setValue(this.el.dom.value);
30441     },
30442     
30443     onDestroy : function()
30444     {
30445         if(this.viewEl){
30446             Roo.get(this.viewEl).remove();
30447         }
30448          
30449         Roo.form.DayPicker.superclass.onDestroy.call(this);
30450     }
30451
30452 });/*
30453  * RooJS Library 1.1.1
30454  * Copyright(c) 2008-2011  Alan Knowles
30455  *
30456  * License - LGPL
30457  */
30458  
30459
30460 /**
30461  * @class Roo.form.ComboCheck
30462  * @extends Roo.form.ComboBox
30463  * A combobox for multiple select items.
30464  *
30465  * FIXME - could do with a reset button..
30466  * 
30467  * @constructor
30468  * Create a new ComboCheck
30469  * @param {Object} config Configuration options
30470  */
30471 Roo.form.ComboCheck = function(config){
30472     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30473     // should verify some data...
30474     // like
30475     // hiddenName = required..
30476     // displayField = required
30477     // valudField == required
30478     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30479     var _t = this;
30480     Roo.each(req, function(e) {
30481         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30482             throw "Roo.form.ComboCheck : missing value for: " + e;
30483         }
30484     });
30485     
30486     
30487 };
30488
30489 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30490      
30491      
30492     editable : false,
30493      
30494     selectedClass: 'x-menu-item-checked', 
30495     
30496     // private
30497     onRender : function(ct, position){
30498         var _t = this;
30499         
30500         
30501         
30502         if(!this.tpl){
30503             var cls = 'x-combo-list';
30504
30505             
30506             this.tpl =  new Roo.Template({
30507                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30508                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30509                    '<span>{' + this.displayField + '}</span>' +
30510                     '</div>' 
30511                 
30512             });
30513         }
30514  
30515         
30516         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30517         this.view.singleSelect = false;
30518         this.view.multiSelect = true;
30519         this.view.toggleSelect = true;
30520         this.pageTb.add(new Roo.Toolbar.Fill(), {
30521             
30522             text: 'Done',
30523             handler: function()
30524             {
30525                 _t.collapse();
30526             }
30527         });
30528     },
30529     
30530     onViewOver : function(e, t){
30531         // do nothing...
30532         return;
30533         
30534     },
30535     
30536     onViewClick : function(doFocus,index){
30537         return;
30538         
30539     },
30540     select: function () {
30541         //Roo.log("SELECT CALLED");
30542     },
30543      
30544     selectByValue : function(xv, scrollIntoView){
30545         var ar = this.getValueArray();
30546         var sels = [];
30547         
30548         Roo.each(ar, function(v) {
30549             if(v === undefined || v === null){
30550                 return;
30551             }
30552             var r = this.findRecord(this.valueField, v);
30553             if(r){
30554                 sels.push(this.store.indexOf(r))
30555                 
30556             }
30557         },this);
30558         this.view.select(sels);
30559         return false;
30560     },
30561     
30562     
30563     
30564     onSelect : function(record, index){
30565        // Roo.log("onselect Called");
30566        // this is only called by the clear button now..
30567         this.view.clearSelections();
30568         this.setValue('[]');
30569         if (this.value != this.valueBefore) {
30570             this.fireEvent('change', this, this.value, this.valueBefore);
30571             this.valueBefore = this.value;
30572         }
30573     },
30574     getValueArray : function()
30575     {
30576         var ar = [] ;
30577         
30578         try {
30579             //Roo.log(this.value);
30580             if (typeof(this.value) == 'undefined') {
30581                 return [];
30582             }
30583             var ar = Roo.decode(this.value);
30584             return  ar instanceof Array ? ar : []; //?? valid?
30585             
30586         } catch(e) {
30587             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30588             return [];
30589         }
30590          
30591     },
30592     expand : function ()
30593     {
30594         
30595         Roo.form.ComboCheck.superclass.expand.call(this);
30596         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30597         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30598         
30599
30600     },
30601     
30602     collapse : function(){
30603         Roo.form.ComboCheck.superclass.collapse.call(this);
30604         var sl = this.view.getSelectedIndexes();
30605         var st = this.store;
30606         var nv = [];
30607         var tv = [];
30608         var r;
30609         Roo.each(sl, function(i) {
30610             r = st.getAt(i);
30611             nv.push(r.get(this.valueField));
30612         },this);
30613         this.setValue(Roo.encode(nv));
30614         if (this.value != this.valueBefore) {
30615
30616             this.fireEvent('change', this, this.value, this.valueBefore);
30617             this.valueBefore = this.value;
30618         }
30619         
30620     },
30621     
30622     setValue : function(v){
30623         // Roo.log(v);
30624         this.value = v;
30625         
30626         var vals = this.getValueArray();
30627         var tv = [];
30628         Roo.each(vals, function(k) {
30629             var r = this.findRecord(this.valueField, k);
30630             if(r){
30631                 tv.push(r.data[this.displayField]);
30632             }else if(this.valueNotFoundText !== undefined){
30633                 tv.push( this.valueNotFoundText );
30634             }
30635         },this);
30636        // Roo.log(tv);
30637         
30638         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30639         this.hiddenField.value = v;
30640         this.value = v;
30641     }
30642     
30643 });/*
30644  * Based on:
30645  * Ext JS Library 1.1.1
30646  * Copyright(c) 2006-2007, Ext JS, LLC.
30647  *
30648  * Originally Released Under LGPL - original licence link has changed is not relivant.
30649  *
30650  * Fork - LGPL
30651  * <script type="text/javascript">
30652  */
30653  
30654 /**
30655  * @class Roo.form.Signature
30656  * @extends Roo.form.Field
30657  * Signature field.  
30658  * @constructor
30659  * 
30660  * @param {Object} config Configuration options
30661  */
30662
30663 Roo.form.Signature = function(config){
30664     Roo.form.Signature.superclass.constructor.call(this, config);
30665     
30666     this.addEvents({// not in used??
30667          /**
30668          * @event confirm
30669          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30670              * @param {Roo.form.Signature} combo This combo box
30671              */
30672         'confirm' : true,
30673         /**
30674          * @event reset
30675          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30676              * @param {Roo.form.ComboBox} combo This combo box
30677              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30678              */
30679         'reset' : true
30680     });
30681 };
30682
30683 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30684     /**
30685      * @cfg {Object} labels Label to use when rendering a form.
30686      * defaults to 
30687      * labels : { 
30688      *      clear : "Clear",
30689      *      confirm : "Confirm"
30690      *  }
30691      */
30692     labels : { 
30693         clear : "Clear",
30694         confirm : "Confirm"
30695     },
30696     /**
30697      * @cfg {Number} width The signature panel width (defaults to 300)
30698      */
30699     width: 300,
30700     /**
30701      * @cfg {Number} height The signature panel height (defaults to 100)
30702      */
30703     height : 100,
30704     /**
30705      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30706      */
30707     allowBlank : false,
30708     
30709     //private
30710     // {Object} signPanel The signature SVG panel element (defaults to {})
30711     signPanel : {},
30712     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30713     isMouseDown : false,
30714     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30715     isConfirmed : false,
30716     // {String} signatureTmp SVG mapping string (defaults to empty string)
30717     signatureTmp : '',
30718     
30719     
30720     defaultAutoCreate : { // modified by initCompnoent..
30721         tag: "input",
30722         type:"hidden"
30723     },
30724
30725     // private
30726     onRender : function(ct, position){
30727         
30728         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30729         
30730         this.wrap = this.el.wrap({
30731             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30732         });
30733         
30734         this.createToolbar(this);
30735         this.signPanel = this.wrap.createChild({
30736                 tag: 'div',
30737                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30738             }, this.el
30739         );
30740             
30741         this.svgID = Roo.id();
30742         this.svgEl = this.signPanel.createChild({
30743               xmlns : 'http://www.w3.org/2000/svg',
30744               tag : 'svg',
30745               id : this.svgID + "-svg",
30746               width: this.width,
30747               height: this.height,
30748               viewBox: '0 0 '+this.width+' '+this.height,
30749               cn : [
30750                 {
30751                     tag: "rect",
30752                     id: this.svgID + "-svg-r",
30753                     width: this.width,
30754                     height: this.height,
30755                     fill: "#ffa"
30756                 },
30757                 {
30758                     tag: "line",
30759                     id: this.svgID + "-svg-l",
30760                     x1: "0", // start
30761                     y1: (this.height*0.8), // start set the line in 80% of height
30762                     x2: this.width, // end
30763                     y2: (this.height*0.8), // end set the line in 80% of height
30764                     'stroke': "#666",
30765                     'stroke-width': "1",
30766                     'stroke-dasharray': "3",
30767                     'shape-rendering': "crispEdges",
30768                     'pointer-events': "none"
30769                 },
30770                 {
30771                     tag: "path",
30772                     id: this.svgID + "-svg-p",
30773                     'stroke': "navy",
30774                     'stroke-width': "3",
30775                     'fill': "none",
30776                     'pointer-events': 'none'
30777                 }
30778               ]
30779         });
30780         this.createSVG();
30781         this.svgBox = this.svgEl.dom.getScreenCTM();
30782     },
30783     createSVG : function(){ 
30784         var svg = this.signPanel;
30785         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30786         var t = this;
30787
30788         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30789         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30790         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30791         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30792         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30793         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30794         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30795         
30796     },
30797     isTouchEvent : function(e){
30798         return e.type.match(/^touch/);
30799     },
30800     getCoords : function (e) {
30801         var pt    = this.svgEl.dom.createSVGPoint();
30802         pt.x = e.clientX; 
30803         pt.y = e.clientY;
30804         if (this.isTouchEvent(e)) {
30805             pt.x =  e.targetTouches[0].clientX;
30806             pt.y = e.targetTouches[0].clientY;
30807         }
30808         var a = this.svgEl.dom.getScreenCTM();
30809         var b = a.inverse();
30810         var mx = pt.matrixTransform(b);
30811         return mx.x + ',' + mx.y;
30812     },
30813     //mouse event headler 
30814     down : function (e) {
30815         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30816         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30817         
30818         this.isMouseDown = true;
30819         
30820         e.preventDefault();
30821     },
30822     move : function (e) {
30823         if (this.isMouseDown) {
30824             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30825             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30826         }
30827         
30828         e.preventDefault();
30829     },
30830     up : function (e) {
30831         this.isMouseDown = false;
30832         var sp = this.signatureTmp.split(' ');
30833         
30834         if(sp.length > 1){
30835             if(!sp[sp.length-2].match(/^L/)){
30836                 sp.pop();
30837                 sp.pop();
30838                 sp.push("");
30839                 this.signatureTmp = sp.join(" ");
30840             }
30841         }
30842         if(this.getValue() != this.signatureTmp){
30843             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30844             this.isConfirmed = false;
30845         }
30846         e.preventDefault();
30847     },
30848     
30849     /**
30850      * Protected method that will not generally be called directly. It
30851      * is called when the editor creates its toolbar. Override this method if you need to
30852      * add custom toolbar buttons.
30853      * @param {HtmlEditor} editor
30854      */
30855     createToolbar : function(editor){
30856          function btn(id, toggle, handler){
30857             var xid = fid + '-'+ id ;
30858             return {
30859                 id : xid,
30860                 cmd : id,
30861                 cls : 'x-btn-icon x-edit-'+id,
30862                 enableToggle:toggle !== false,
30863                 scope: editor, // was editor...
30864                 handler:handler||editor.relayBtnCmd,
30865                 clickEvent:'mousedown',
30866                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30867                 tabIndex:-1
30868             };
30869         }
30870         
30871         
30872         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
30873         this.tb = tb;
30874         this.tb.add(
30875            {
30876                 cls : ' x-signature-btn x-signature-'+id,
30877                 scope: editor, // was editor...
30878                 handler: this.reset,
30879                 clickEvent:'mousedown',
30880                 text: this.labels.clear
30881             },
30882             {
30883                  xtype : 'Fill',
30884                  xns: Roo.Toolbar
30885             }, 
30886             {
30887                 cls : '  x-signature-btn x-signature-'+id,
30888                 scope: editor, // was editor...
30889                 handler: this.confirmHandler,
30890                 clickEvent:'mousedown',
30891                 text: this.labels.confirm
30892             }
30893         );
30894     
30895     },
30896     //public
30897     /**
30898      * when user is clicked confirm then show this image.....
30899      * 
30900      * @return {String} Image Data URI
30901      */
30902     getImageDataURI : function(){
30903         var svg = this.svgEl.dom.parentNode.innerHTML;
30904         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
30905         return src; 
30906     },
30907     /**
30908      * 
30909      * @return {Boolean} this.isConfirmed
30910      */
30911     getConfirmed : function(){
30912         return this.isConfirmed;
30913     },
30914     /**
30915      * 
30916      * @return {Number} this.width
30917      */
30918     getWidth : function(){
30919         return this.width;
30920     },
30921     /**
30922      * 
30923      * @return {Number} this.height
30924      */
30925     getHeight : function(){
30926         return this.height;
30927     },
30928     // private
30929     getSignature : function(){
30930         return this.signatureTmp;
30931     },
30932     // private
30933     reset : function(){
30934         this.signatureTmp = '';
30935         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30936         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
30937         this.isConfirmed = false;
30938         Roo.form.Signature.superclass.reset.call(this);
30939     },
30940     setSignature : function(s){
30941         this.signatureTmp = s;
30942         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30943         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
30944         this.setValue(s);
30945         this.isConfirmed = false;
30946         Roo.form.Signature.superclass.reset.call(this);
30947     }, 
30948     test : function(){
30949 //        Roo.log(this.signPanel.dom.contentWindow.up())
30950     },
30951     //private
30952     setConfirmed : function(){
30953         
30954         
30955         
30956 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
30957     },
30958     // private
30959     confirmHandler : function(){
30960         if(!this.getSignature()){
30961             return;
30962         }
30963         
30964         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
30965         this.setValue(this.getSignature());
30966         this.isConfirmed = true;
30967         
30968         this.fireEvent('confirm', this);
30969     },
30970     // private
30971     // Subclasses should provide the validation implementation by overriding this
30972     validateValue : function(value){
30973         if(this.allowBlank){
30974             return true;
30975         }
30976         
30977         if(this.isConfirmed){
30978             return true;
30979         }
30980         return false;
30981     }
30982 });/*
30983  * Based on:
30984  * Ext JS Library 1.1.1
30985  * Copyright(c) 2006-2007, Ext JS, LLC.
30986  *
30987  * Originally Released Under LGPL - original licence link has changed is not relivant.
30988  *
30989  * Fork - LGPL
30990  * <script type="text/javascript">
30991  */
30992  
30993
30994 /**
30995  * @class Roo.form.ComboBox
30996  * @extends Roo.form.TriggerField
30997  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
30998  * @constructor
30999  * Create a new ComboBox.
31000  * @param {Object} config Configuration options
31001  */
31002 Roo.form.Select = function(config){
31003     Roo.form.Select.superclass.constructor.call(this, config);
31004      
31005 };
31006
31007 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31008     /**
31009      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31010      */
31011     /**
31012      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31013      * rendering into an Roo.Editor, defaults to false)
31014      */
31015     /**
31016      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31017      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31018      */
31019     /**
31020      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31021      */
31022     /**
31023      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31024      * the dropdown list (defaults to undefined, with no header element)
31025      */
31026
31027      /**
31028      * @cfg {String/Roo.Template} tpl The template to use to render the output
31029      */
31030      
31031     // private
31032     defaultAutoCreate : {tag: "select"  },
31033     /**
31034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31035      */
31036     listWidth: undefined,
31037     /**
31038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31039      * mode = 'remote' or 'text' if mode = 'local')
31040      */
31041     displayField: undefined,
31042     /**
31043      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31044      * mode = 'remote' or 'value' if mode = 'local'). 
31045      * Note: use of a valueField requires the user make a selection
31046      * in order for a value to be mapped.
31047      */
31048     valueField: undefined,
31049     
31050     
31051     /**
31052      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31053      * field's data value (defaults to the underlying DOM element's name)
31054      */
31055     hiddenName: undefined,
31056     /**
31057      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31058      */
31059     listClass: '',
31060     /**
31061      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31062      */
31063     selectedClass: 'x-combo-selected',
31064     /**
31065      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31066      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31067      * which displays a downward arrow icon).
31068      */
31069     triggerClass : 'x-form-arrow-trigger',
31070     /**
31071      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31072      */
31073     shadow:'sides',
31074     /**
31075      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31076      * anchor positions (defaults to 'tl-bl')
31077      */
31078     listAlign: 'tl-bl?',
31079     /**
31080      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31081      */
31082     maxHeight: 300,
31083     /**
31084      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31085      * query specified by the allQuery config option (defaults to 'query')
31086      */
31087     triggerAction: 'query',
31088     /**
31089      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31090      * (defaults to 4, does not apply if editable = false)
31091      */
31092     minChars : 4,
31093     /**
31094      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31095      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31096      */
31097     typeAhead: false,
31098     /**
31099      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31100      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31101      */
31102     queryDelay: 500,
31103     /**
31104      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31105      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31106      */
31107     pageSize: 0,
31108     /**
31109      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31110      * when editable = true (defaults to false)
31111      */
31112     selectOnFocus:false,
31113     /**
31114      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31115      */
31116     queryParam: 'query',
31117     /**
31118      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31119      * when mode = 'remote' (defaults to 'Loading...')
31120      */
31121     loadingText: 'Loading...',
31122     /**
31123      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31124      */
31125     resizable: false,
31126     /**
31127      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31128      */
31129     handleHeight : 8,
31130     /**
31131      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31132      * traditional select (defaults to true)
31133      */
31134     editable: true,
31135     /**
31136      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31137      */
31138     allQuery: '',
31139     /**
31140      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31141      */
31142     mode: 'remote',
31143     /**
31144      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31145      * listWidth has a higher value)
31146      */
31147     minListWidth : 70,
31148     /**
31149      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31150      * allow the user to set arbitrary text into the field (defaults to false)
31151      */
31152     forceSelection:false,
31153     /**
31154      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31155      * if typeAhead = true (defaults to 250)
31156      */
31157     typeAheadDelay : 250,
31158     /**
31159      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31160      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31161      */
31162     valueNotFoundText : undefined,
31163     
31164     /**
31165      * @cfg {String} defaultValue The value displayed after loading the store.
31166      */
31167     defaultValue: '',
31168     
31169     /**
31170      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31171      */
31172     blockFocus : false,
31173     
31174     /**
31175      * @cfg {Boolean} disableClear Disable showing of clear button.
31176      */
31177     disableClear : false,
31178     /**
31179      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31180      */
31181     alwaysQuery : false,
31182     
31183     //private
31184     addicon : false,
31185     editicon: false,
31186     
31187     // element that contains real text value.. (when hidden is used..)
31188      
31189     // private
31190     onRender : function(ct, position){
31191         Roo.form.Field.prototype.onRender.call(this, ct, position);
31192         
31193         if(this.store){
31194             this.store.on('beforeload', this.onBeforeLoad, this);
31195             this.store.on('load', this.onLoad, this);
31196             this.store.on('loadexception', this.onLoadException, this);
31197             this.store.load({});
31198         }
31199         
31200         
31201         
31202     },
31203
31204     // private
31205     initEvents : function(){
31206         //Roo.form.ComboBox.superclass.initEvents.call(this);
31207  
31208     },
31209
31210     onDestroy : function(){
31211        
31212         if(this.store){
31213             this.store.un('beforeload', this.onBeforeLoad, this);
31214             this.store.un('load', this.onLoad, this);
31215             this.store.un('loadexception', this.onLoadException, this);
31216         }
31217         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31218     },
31219
31220     // private
31221     fireKey : function(e){
31222         if(e.isNavKeyPress() && !this.list.isVisible()){
31223             this.fireEvent("specialkey", this, e);
31224         }
31225     },
31226
31227     // private
31228     onResize: function(w, h){
31229         
31230         return; 
31231     
31232         
31233     },
31234
31235     /**
31236      * Allow or prevent the user from directly editing the field text.  If false is passed,
31237      * the user will only be able to select from the items defined in the dropdown list.  This method
31238      * is the runtime equivalent of setting the 'editable' config option at config time.
31239      * @param {Boolean} value True to allow the user to directly edit the field text
31240      */
31241     setEditable : function(value){
31242          
31243     },
31244
31245     // private
31246     onBeforeLoad : function(){
31247         
31248         Roo.log("Select before load");
31249         return;
31250     
31251         this.innerList.update(this.loadingText ?
31252                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31253         //this.restrictHeight();
31254         this.selectedIndex = -1;
31255     },
31256
31257     // private
31258     onLoad : function(){
31259
31260     
31261         var dom = this.el.dom;
31262         dom.innerHTML = '';
31263          var od = dom.ownerDocument;
31264          
31265         if (this.emptyText) {
31266             var op = od.createElement('option');
31267             op.setAttribute('value', '');
31268             op.innerHTML = String.format('{0}', this.emptyText);
31269             dom.appendChild(op);
31270         }
31271         if(this.store.getCount() > 0){
31272            
31273             var vf = this.valueField;
31274             var df = this.displayField;
31275             this.store.data.each(function(r) {
31276                 // which colmsn to use... testing - cdoe / title..
31277                 var op = od.createElement('option');
31278                 op.setAttribute('value', r.data[vf]);
31279                 op.innerHTML = String.format('{0}', r.data[df]);
31280                 dom.appendChild(op);
31281             });
31282             if (typeof(this.defaultValue != 'undefined')) {
31283                 this.setValue(this.defaultValue);
31284             }
31285             
31286              
31287         }else{
31288             //this.onEmptyResults();
31289         }
31290         //this.el.focus();
31291     },
31292     // private
31293     onLoadException : function()
31294     {
31295         dom.innerHTML = '';
31296             
31297         Roo.log("Select on load exception");
31298         return;
31299     
31300         this.collapse();
31301         Roo.log(this.store.reader.jsonData);
31302         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31303             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31304         }
31305         
31306         
31307     },
31308     // private
31309     onTypeAhead : function(){
31310          
31311     },
31312
31313     // private
31314     onSelect : function(record, index){
31315         Roo.log('on select?');
31316         return;
31317         if(this.fireEvent('beforeselect', this, record, index) !== false){
31318             this.setFromData(index > -1 ? record.data : false);
31319             this.collapse();
31320             this.fireEvent('select', this, record, index);
31321         }
31322     },
31323
31324     /**
31325      * Returns the currently selected field value or empty string if no value is set.
31326      * @return {String} value The selected value
31327      */
31328     getValue : function(){
31329         var dom = this.el.dom;
31330         this.value = dom.options[dom.selectedIndex].value;
31331         return this.value;
31332         
31333     },
31334
31335     /**
31336      * Clears any text/value currently set in the field
31337      */
31338     clearValue : function(){
31339         this.value = '';
31340         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31341         
31342     },
31343
31344     /**
31345      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31346      * will be displayed in the field.  If the value does not match the data value of an existing item,
31347      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31348      * Otherwise the field will be blank (although the value will still be set).
31349      * @param {String} value The value to match
31350      */
31351     setValue : function(v){
31352         var d = this.el.dom;
31353         for (var i =0; i < d.options.length;i++) {
31354             if (v == d.options[i].value) {
31355                 d.selectedIndex = i;
31356                 this.value = v;
31357                 return;
31358             }
31359         }
31360         this.clearValue();
31361     },
31362     /**
31363      * @property {Object} the last set data for the element
31364      */
31365     
31366     lastData : false,
31367     /**
31368      * Sets the value of the field based on a object which is related to the record format for the store.
31369      * @param {Object} value the value to set as. or false on reset?
31370      */
31371     setFromData : function(o){
31372         Roo.log('setfrom data?');
31373          
31374         
31375         
31376     },
31377     // private
31378     reset : function(){
31379         this.clearValue();
31380     },
31381     // private
31382     findRecord : function(prop, value){
31383         
31384         return false;
31385     
31386         var record;
31387         if(this.store.getCount() > 0){
31388             this.store.each(function(r){
31389                 if(r.data[prop] == value){
31390                     record = r;
31391                     return false;
31392                 }
31393                 return true;
31394             });
31395         }
31396         return record;
31397     },
31398     
31399     getName: function()
31400     {
31401         // returns hidden if it's set..
31402         if (!this.rendered) {return ''};
31403         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31404         
31405     },
31406      
31407
31408     
31409
31410     // private
31411     onEmptyResults : function(){
31412         Roo.log('empty results');
31413         //this.collapse();
31414     },
31415
31416     /**
31417      * Returns true if the dropdown list is expanded, else false.
31418      */
31419     isExpanded : function(){
31420         return false;
31421     },
31422
31423     /**
31424      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31425      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31426      * @param {String} value The data value of the item to select
31427      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31428      * selected item if it is not currently in view (defaults to true)
31429      * @return {Boolean} True if the value matched an item in the list, else false
31430      */
31431     selectByValue : function(v, scrollIntoView){
31432         Roo.log('select By Value');
31433         return false;
31434     
31435         if(v !== undefined && v !== null){
31436             var r = this.findRecord(this.valueField || this.displayField, v);
31437             if(r){
31438                 this.select(this.store.indexOf(r), scrollIntoView);
31439                 return true;
31440             }
31441         }
31442         return false;
31443     },
31444
31445     /**
31446      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31447      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31448      * @param {Number} index The zero-based index of the list item to select
31449      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31450      * selected item if it is not currently in view (defaults to true)
31451      */
31452     select : function(index, scrollIntoView){
31453         Roo.log('select ');
31454         return  ;
31455         
31456         this.selectedIndex = index;
31457         this.view.select(index);
31458         if(scrollIntoView !== false){
31459             var el = this.view.getNode(index);
31460             if(el){
31461                 this.innerList.scrollChildIntoView(el, false);
31462             }
31463         }
31464     },
31465
31466       
31467
31468     // private
31469     validateBlur : function(){
31470         
31471         return;
31472         
31473     },
31474
31475     // private
31476     initQuery : function(){
31477         this.doQuery(this.getRawValue());
31478     },
31479
31480     // private
31481     doForce : function(){
31482         if(this.el.dom.value.length > 0){
31483             this.el.dom.value =
31484                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31485              
31486         }
31487     },
31488
31489     /**
31490      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31491      * query allowing the query action to be canceled if needed.
31492      * @param {String} query The SQL query to execute
31493      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31494      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31495      * saved in the current store (defaults to false)
31496      */
31497     doQuery : function(q, forceAll){
31498         
31499         Roo.log('doQuery?');
31500         if(q === undefined || q === null){
31501             q = '';
31502         }
31503         var qe = {
31504             query: q,
31505             forceAll: forceAll,
31506             combo: this,
31507             cancel:false
31508         };
31509         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31510             return false;
31511         }
31512         q = qe.query;
31513         forceAll = qe.forceAll;
31514         if(forceAll === true || (q.length >= this.minChars)){
31515             if(this.lastQuery != q || this.alwaysQuery){
31516                 this.lastQuery = q;
31517                 if(this.mode == 'local'){
31518                     this.selectedIndex = -1;
31519                     if(forceAll){
31520                         this.store.clearFilter();
31521                     }else{
31522                         this.store.filter(this.displayField, q);
31523                     }
31524                     this.onLoad();
31525                 }else{
31526                     this.store.baseParams[this.queryParam] = q;
31527                     this.store.load({
31528                         params: this.getParams(q)
31529                     });
31530                     this.expand();
31531                 }
31532             }else{
31533                 this.selectedIndex = -1;
31534                 this.onLoad();   
31535             }
31536         }
31537     },
31538
31539     // private
31540     getParams : function(q){
31541         var p = {};
31542         //p[this.queryParam] = q;
31543         if(this.pageSize){
31544             p.start = 0;
31545             p.limit = this.pageSize;
31546         }
31547         return p;
31548     },
31549
31550     /**
31551      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31552      */
31553     collapse : function(){
31554         
31555     },
31556
31557     // private
31558     collapseIf : function(e){
31559         
31560     },
31561
31562     /**
31563      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31564      */
31565     expand : function(){
31566         
31567     } ,
31568
31569     // private
31570      
31571
31572     /** 
31573     * @cfg {Boolean} grow 
31574     * @hide 
31575     */
31576     /** 
31577     * @cfg {Number} growMin 
31578     * @hide 
31579     */
31580     /** 
31581     * @cfg {Number} growMax 
31582     * @hide 
31583     */
31584     /**
31585      * @hide
31586      * @method autoSize
31587      */
31588     
31589     setWidth : function()
31590     {
31591         
31592     },
31593     getResizeEl : function(){
31594         return this.el;
31595     }
31596 });//<script type="text/javasscript">
31597  
31598
31599 /**
31600  * @class Roo.DDView
31601  * A DnD enabled version of Roo.View.
31602  * @param {Element/String} container The Element in which to create the View.
31603  * @param {String} tpl The template string used to create the markup for each element of the View
31604  * @param {Object} config The configuration properties. These include all the config options of
31605  * {@link Roo.View} plus some specific to this class.<br>
31606  * <p>
31607  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31608  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31609  * <p>
31610  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31611 .x-view-drag-insert-above {
31612         border-top:1px dotted #3366cc;
31613 }
31614 .x-view-drag-insert-below {
31615         border-bottom:1px dotted #3366cc;
31616 }
31617 </code></pre>
31618  * 
31619  */
31620  
31621 Roo.DDView = function(container, tpl, config) {
31622     Roo.DDView.superclass.constructor.apply(this, arguments);
31623     this.getEl().setStyle("outline", "0px none");
31624     this.getEl().unselectable();
31625     if (this.dragGroup) {
31626         this.setDraggable(this.dragGroup.split(","));
31627     }
31628     if (this.dropGroup) {
31629         this.setDroppable(this.dropGroup.split(","));
31630     }
31631     if (this.deletable) {
31632         this.setDeletable();
31633     }
31634     this.isDirtyFlag = false;
31635         this.addEvents({
31636                 "drop" : true
31637         });
31638 };
31639
31640 Roo.extend(Roo.DDView, Roo.View, {
31641 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31642 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31643 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31644 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31645
31646         isFormField: true,
31647
31648         reset: Roo.emptyFn,
31649         
31650         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31651
31652         validate: function() {
31653                 return true;
31654         },
31655         
31656         destroy: function() {
31657                 this.purgeListeners();
31658                 this.getEl.removeAllListeners();
31659                 this.getEl().remove();
31660                 if (this.dragZone) {
31661                         if (this.dragZone.destroy) {
31662                                 this.dragZone.destroy();
31663                         }
31664                 }
31665                 if (this.dropZone) {
31666                         if (this.dropZone.destroy) {
31667                                 this.dropZone.destroy();
31668                         }
31669                 }
31670         },
31671
31672 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31673         getName: function() {
31674                 return this.name;
31675         },
31676
31677 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31678         setValue: function(v) {
31679                 if (!this.store) {
31680                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31681                 }
31682                 var data = {};
31683                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31684                 this.store.proxy = new Roo.data.MemoryProxy(data);
31685                 this.store.load();
31686         },
31687
31688 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31689         getValue: function() {
31690                 var result = '(';
31691                 this.store.each(function(rec) {
31692                         result += rec.id + ',';
31693                 });
31694                 return result.substr(0, result.length - 1) + ')';
31695         },
31696         
31697         getIds: function() {
31698                 var i = 0, result = new Array(this.store.getCount());
31699                 this.store.each(function(rec) {
31700                         result[i++] = rec.id;
31701                 });
31702                 return result;
31703         },
31704         
31705         isDirty: function() {
31706                 return this.isDirtyFlag;
31707         },
31708
31709 /**
31710  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31711  *      whole Element becomes the target, and this causes the drop gesture to append.
31712  */
31713     getTargetFromEvent : function(e) {
31714                 var target = e.getTarget();
31715                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31716                 target = target.parentNode;
31717                 }
31718                 if (!target) {
31719                         target = this.el.dom.lastChild || this.el.dom;
31720                 }
31721                 return target;
31722     },
31723
31724 /**
31725  *      Create the drag data which consists of an object which has the property "ddel" as
31726  *      the drag proxy element. 
31727  */
31728     getDragData : function(e) {
31729         var target = this.findItemFromChild(e.getTarget());
31730                 if(target) {
31731                         this.handleSelection(e);
31732                         var selNodes = this.getSelectedNodes();
31733             var dragData = {
31734                 source: this,
31735                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31736                 nodes: selNodes,
31737                 records: []
31738                         };
31739                         var selectedIndices = this.getSelectedIndexes();
31740                         for (var i = 0; i < selectedIndices.length; i++) {
31741                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31742                         }
31743                         if (selNodes.length == 1) {
31744                                 dragData.ddel = target.cloneNode(true); // the div element
31745                         } else {
31746                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31747                                 div.className = 'multi-proxy';
31748                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31749                                         div.appendChild(selNodes[i].cloneNode(true));
31750                                 }
31751                                 dragData.ddel = div;
31752                         }
31753             //console.log(dragData)
31754             //console.log(dragData.ddel.innerHTML)
31755                         return dragData;
31756                 }
31757         //console.log('nodragData')
31758                 return false;
31759     },
31760     
31761 /**     Specify to which ddGroup items in this DDView may be dragged. */
31762     setDraggable: function(ddGroup) {
31763         if (ddGroup instanceof Array) {
31764                 Roo.each(ddGroup, this.setDraggable, this);
31765                 return;
31766         }
31767         if (this.dragZone) {
31768                 this.dragZone.addToGroup(ddGroup);
31769         } else {
31770                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31771                                 containerScroll: true,
31772                                 ddGroup: ddGroup 
31773
31774                         });
31775 //                      Draggability implies selection. DragZone's mousedown selects the element.
31776                         if (!this.multiSelect) { this.singleSelect = true; }
31777
31778 //                      Wire the DragZone's handlers up to methods in *this*
31779                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31780                 }
31781     },
31782
31783 /**     Specify from which ddGroup this DDView accepts drops. */
31784     setDroppable: function(ddGroup) {
31785         if (ddGroup instanceof Array) {
31786                 Roo.each(ddGroup, this.setDroppable, this);
31787                 return;
31788         }
31789         if (this.dropZone) {
31790                 this.dropZone.addToGroup(ddGroup);
31791         } else {
31792                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31793                                 containerScroll: true,
31794                                 ddGroup: ddGroup
31795                         });
31796
31797 //                      Wire the DropZone's handlers up to methods in *this*
31798                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31799                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31800                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31801                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31802                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31803                 }
31804     },
31805
31806 /**     Decide whether to drop above or below a View node. */
31807     getDropPoint : function(e, n, dd){
31808         if (n == this.el.dom) { return "above"; }
31809                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31810                 var c = t + (b - t) / 2;
31811                 var y = Roo.lib.Event.getPageY(e);
31812                 if(y <= c) {
31813                         return "above";
31814                 }else{
31815                         return "below";
31816                 }
31817     },
31818
31819     onNodeEnter : function(n, dd, e, data){
31820                 return false;
31821     },
31822     
31823     onNodeOver : function(n, dd, e, data){
31824                 var pt = this.getDropPoint(e, n, dd);
31825                 // set the insert point style on the target node
31826                 var dragElClass = this.dropNotAllowed;
31827                 if (pt) {
31828                         var targetElClass;
31829                         if (pt == "above"){
31830                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31831                                 targetElClass = "x-view-drag-insert-above";
31832                         } else {
31833                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31834                                 targetElClass = "x-view-drag-insert-below";
31835                         }
31836                         if (this.lastInsertClass != targetElClass){
31837                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31838                                 this.lastInsertClass = targetElClass;
31839                         }
31840                 }
31841                 return dragElClass;
31842         },
31843
31844     onNodeOut : function(n, dd, e, data){
31845                 this.removeDropIndicators(n);
31846     },
31847
31848     onNodeDrop : function(n, dd, e, data){
31849         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31850                 return false;
31851         }
31852         var pt = this.getDropPoint(e, n, dd);
31853                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31854                 if (pt == "below") { insertAt++; }
31855                 for (var i = 0; i < data.records.length; i++) {
31856                         var r = data.records[i];
31857                         var dup = this.store.getById(r.id);
31858                         if (dup && (dd != this.dragZone)) {
31859                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
31860                         } else {
31861                                 if (data.copy) {
31862                                         this.store.insert(insertAt++, r.copy());
31863                                 } else {
31864                                         data.source.isDirtyFlag = true;
31865                                         r.store.remove(r);
31866                                         this.store.insert(insertAt++, r);
31867                                 }
31868                                 this.isDirtyFlag = true;
31869                         }
31870                 }
31871                 this.dragZone.cachedTarget = null;
31872                 return true;
31873     },
31874
31875     removeDropIndicators : function(n){
31876                 if(n){
31877                         Roo.fly(n).removeClass([
31878                                 "x-view-drag-insert-above",
31879                                 "x-view-drag-insert-below"]);
31880                         this.lastInsertClass = "_noclass";
31881                 }
31882     },
31883
31884 /**
31885  *      Utility method. Add a delete option to the DDView's context menu.
31886  *      @param {String} imageUrl The URL of the "delete" icon image.
31887  */
31888         setDeletable: function(imageUrl) {
31889                 if (!this.singleSelect && !this.multiSelect) {
31890                         this.singleSelect = true;
31891                 }
31892                 var c = this.getContextMenu();
31893                 this.contextMenu.on("itemclick", function(item) {
31894                         switch (item.id) {
31895                                 case "delete":
31896                                         this.remove(this.getSelectedIndexes());
31897                                         break;
31898                         }
31899                 }, this);
31900                 this.contextMenu.add({
31901                         icon: imageUrl,
31902                         id: "delete",
31903                         text: 'Delete'
31904                 });
31905         },
31906         
31907 /**     Return the context menu for this DDView. */
31908         getContextMenu: function() {
31909                 if (!this.contextMenu) {
31910 //                      Create the View's context menu
31911                         this.contextMenu = new Roo.menu.Menu({
31912                                 id: this.id + "-contextmenu"
31913                         });
31914                         this.el.on("contextmenu", this.showContextMenu, this);
31915                 }
31916                 return this.contextMenu;
31917         },
31918         
31919         disableContextMenu: function() {
31920                 if (this.contextMenu) {
31921                         this.el.un("contextmenu", this.showContextMenu, this);
31922                 }
31923         },
31924
31925         showContextMenu: function(e, item) {
31926         item = this.findItemFromChild(e.getTarget());
31927                 if (item) {
31928                         e.stopEvent();
31929                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
31930                         this.contextMenu.showAt(e.getXY());
31931             }
31932     },
31933
31934 /**
31935  *      Remove {@link Roo.data.Record}s at the specified indices.
31936  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
31937  */
31938     remove: function(selectedIndices) {
31939                 selectedIndices = [].concat(selectedIndices);
31940                 for (var i = 0; i < selectedIndices.length; i++) {
31941                         var rec = this.store.getAt(selectedIndices[i]);
31942                         this.store.remove(rec);
31943                 }
31944     },
31945
31946 /**
31947  *      Double click fires the event, but also, if this is draggable, and there is only one other
31948  *      related DropZone, it transfers the selected node.
31949  */
31950     onDblClick : function(e){
31951         var item = this.findItemFromChild(e.getTarget());
31952         if(item){
31953             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
31954                 return false;
31955             }
31956             if (this.dragGroup) {
31957                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
31958                     while (targets.indexOf(this.dropZone) > -1) {
31959                             targets.remove(this.dropZone);
31960                                 }
31961                     if (targets.length == 1) {
31962                                         this.dragZone.cachedTarget = null;
31963                         var el = Roo.get(targets[0].getEl());
31964                         var box = el.getBox(true);
31965                         targets[0].onNodeDrop(el.dom, {
31966                                 target: el.dom,
31967                                 xy: [box.x, box.y + box.height - 1]
31968                         }, null, this.getDragData(e));
31969                     }
31970                 }
31971         }
31972     },
31973     
31974     handleSelection: function(e) {
31975                 this.dragZone.cachedTarget = null;
31976         var item = this.findItemFromChild(e.getTarget());
31977         if (!item) {
31978                 this.clearSelections(true);
31979                 return;
31980         }
31981                 if (item && (this.multiSelect || this.singleSelect)){
31982                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
31983                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
31984                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
31985                                 this.unselect(item);
31986                         } else {
31987                                 this.select(item, this.multiSelect && e.ctrlKey);
31988                                 this.lastSelection = item;
31989                         }
31990                 }
31991     },
31992
31993     onItemClick : function(item, index, e){
31994                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
31995                         return false;
31996                 }
31997                 return true;
31998     },
31999
32000     unselect : function(nodeInfo, suppressEvent){
32001                 var node = this.getNode(nodeInfo);
32002                 if(node && this.isSelected(node)){
32003                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32004                                 Roo.fly(node).removeClass(this.selectedClass);
32005                                 this.selections.remove(node);
32006                                 if(!suppressEvent){
32007                                         this.fireEvent("selectionchange", this, this.selections);
32008                                 }
32009                         }
32010                 }
32011     }
32012 });
32013 /*
32014  * Based on:
32015  * Ext JS Library 1.1.1
32016  * Copyright(c) 2006-2007, Ext JS, LLC.
32017  *
32018  * Originally Released Under LGPL - original licence link has changed is not relivant.
32019  *
32020  * Fork - LGPL
32021  * <script type="text/javascript">
32022  */
32023  
32024 /**
32025  * @class Roo.LayoutManager
32026  * @extends Roo.util.Observable
32027  * Base class for layout managers.
32028  */
32029 Roo.LayoutManager = function(container, config){
32030     Roo.LayoutManager.superclass.constructor.call(this);
32031     this.el = Roo.get(container);
32032     // ie scrollbar fix
32033     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32034         document.body.scroll = "no";
32035     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32036         this.el.position('relative');
32037     }
32038     this.id = this.el.id;
32039     this.el.addClass("x-layout-container");
32040     /** false to disable window resize monitoring @type Boolean */
32041     this.monitorWindowResize = true;
32042     this.regions = {};
32043     this.addEvents({
32044         /**
32045          * @event layout
32046          * Fires when a layout is performed. 
32047          * @param {Roo.LayoutManager} this
32048          */
32049         "layout" : true,
32050         /**
32051          * @event regionresized
32052          * Fires when the user resizes a region. 
32053          * @param {Roo.LayoutRegion} region The resized region
32054          * @param {Number} newSize The new size (width for east/west, height for north/south)
32055          */
32056         "regionresized" : true,
32057         /**
32058          * @event regioncollapsed
32059          * Fires when a region is collapsed. 
32060          * @param {Roo.LayoutRegion} region The collapsed region
32061          */
32062         "regioncollapsed" : true,
32063         /**
32064          * @event regionexpanded
32065          * Fires when a region is expanded.  
32066          * @param {Roo.LayoutRegion} region The expanded region
32067          */
32068         "regionexpanded" : true
32069     });
32070     this.updating = false;
32071     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32072 };
32073
32074 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32075     /**
32076      * Returns true if this layout is currently being updated
32077      * @return {Boolean}
32078      */
32079     isUpdating : function(){
32080         return this.updating; 
32081     },
32082     
32083     /**
32084      * Suspend the LayoutManager from doing auto-layouts while
32085      * making multiple add or remove calls
32086      */
32087     beginUpdate : function(){
32088         this.updating = true;    
32089     },
32090     
32091     /**
32092      * Restore auto-layouts and optionally disable the manager from performing a layout
32093      * @param {Boolean} noLayout true to disable a layout update 
32094      */
32095     endUpdate : function(noLayout){
32096         this.updating = false;
32097         if(!noLayout){
32098             this.layout();
32099         }    
32100     },
32101     
32102     layout: function(){
32103         
32104     },
32105     
32106     onRegionResized : function(region, newSize){
32107         this.fireEvent("regionresized", region, newSize);
32108         this.layout();
32109     },
32110     
32111     onRegionCollapsed : function(region){
32112         this.fireEvent("regioncollapsed", region);
32113     },
32114     
32115     onRegionExpanded : function(region){
32116         this.fireEvent("regionexpanded", region);
32117     },
32118         
32119     /**
32120      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32121      * performs box-model adjustments.
32122      * @return {Object} The size as an object {width: (the width), height: (the height)}
32123      */
32124     getViewSize : function(){
32125         var size;
32126         if(this.el.dom != document.body){
32127             size = this.el.getSize();
32128         }else{
32129             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32130         }
32131         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32132         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32133         return size;
32134     },
32135     
32136     /**
32137      * Returns the Element this layout is bound to.
32138      * @return {Roo.Element}
32139      */
32140     getEl : function(){
32141         return this.el;
32142     },
32143     
32144     /**
32145      * Returns the specified region.
32146      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32147      * @return {Roo.LayoutRegion}
32148      */
32149     getRegion : function(target){
32150         return this.regions[target.toLowerCase()];
32151     },
32152     
32153     onWindowResize : function(){
32154         if(this.monitorWindowResize){
32155             this.layout();
32156         }
32157     }
32158 });/*
32159  * Based on:
32160  * Ext JS Library 1.1.1
32161  * Copyright(c) 2006-2007, Ext JS, LLC.
32162  *
32163  * Originally Released Under LGPL - original licence link has changed is not relivant.
32164  *
32165  * Fork - LGPL
32166  * <script type="text/javascript">
32167  */
32168 /**
32169  * @class Roo.BorderLayout
32170  * @extends Roo.LayoutManager
32171  * @children Roo.ContentPanel
32172  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32173  * please see: <br><br>
32174  * <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>
32175  * <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>
32176  * Example:
32177  <pre><code>
32178  var layout = new Roo.BorderLayout(document.body, {
32179     north: {
32180         initialSize: 25,
32181         titlebar: false
32182     },
32183     west: {
32184         split:true,
32185         initialSize: 200,
32186         minSize: 175,
32187         maxSize: 400,
32188         titlebar: true,
32189         collapsible: true
32190     },
32191     east: {
32192         split:true,
32193         initialSize: 202,
32194         minSize: 175,
32195         maxSize: 400,
32196         titlebar: true,
32197         collapsible: true
32198     },
32199     south: {
32200         split:true,
32201         initialSize: 100,
32202         minSize: 100,
32203         maxSize: 200,
32204         titlebar: true,
32205         collapsible: true
32206     },
32207     center: {
32208         titlebar: true,
32209         autoScroll:true,
32210         resizeTabs: true,
32211         minTabWidth: 50,
32212         preferredTabWidth: 150
32213     }
32214 });
32215
32216 // shorthand
32217 var CP = Roo.ContentPanel;
32218
32219 layout.beginUpdate();
32220 layout.add("north", new CP("north", "North"));
32221 layout.add("south", new CP("south", {title: "South", closable: true}));
32222 layout.add("west", new CP("west", {title: "West"}));
32223 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32224 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32225 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32226 layout.getRegion("center").showPanel("center1");
32227 layout.endUpdate();
32228 </code></pre>
32229
32230 <b>The container the layout is rendered into can be either the body element or any other element.
32231 If it is not the body element, the container needs to either be an absolute positioned element,
32232 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32233 the container size if it is not the body element.</b>
32234
32235 * @constructor
32236 * Create a new BorderLayout
32237 * @param {String/HTMLElement/Element} container The container this layout is bound to
32238 * @param {Object} config Configuration options
32239  */
32240 Roo.BorderLayout = function(container, config){
32241     config = config || {};
32242     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32243     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32244     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32245         var target = this.factory.validRegions[i];
32246         if(config[target]){
32247             this.addRegion(target, config[target]);
32248         }
32249     }
32250 };
32251
32252 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32253         
32254         /**
32255          * @cfg {Roo.LayoutRegion} east
32256          */
32257         /**
32258          * @cfg {Roo.LayoutRegion} west
32259          */
32260         /**
32261          * @cfg {Roo.LayoutRegion} north
32262          */
32263         /**
32264          * @cfg {Roo.LayoutRegion} south
32265          */
32266         /**
32267          * @cfg {Roo.LayoutRegion} center
32268          */
32269     /**
32270      * Creates and adds a new region if it doesn't already exist.
32271      * @param {String} target The target region key (north, south, east, west or center).
32272      * @param {Object} config The regions config object
32273      * @return {BorderLayoutRegion} The new region
32274      */
32275     addRegion : function(target, config){
32276         if(!this.regions[target]){
32277             var r = this.factory.create(target, this, config);
32278             this.bindRegion(target, r);
32279         }
32280         return this.regions[target];
32281     },
32282
32283     // private (kinda)
32284     bindRegion : function(name, r){
32285         this.regions[name] = r;
32286         r.on("visibilitychange", this.layout, this);
32287         r.on("paneladded", this.layout, this);
32288         r.on("panelremoved", this.layout, this);
32289         r.on("invalidated", this.layout, this);
32290         r.on("resized", this.onRegionResized, this);
32291         r.on("collapsed", this.onRegionCollapsed, this);
32292         r.on("expanded", this.onRegionExpanded, this);
32293     },
32294
32295     /**
32296      * Performs a layout update.
32297      */
32298     layout : function(){
32299         if(this.updating) {
32300             return;
32301         }
32302         var size = this.getViewSize();
32303         var w = size.width;
32304         var h = size.height;
32305         var centerW = w;
32306         var centerH = h;
32307         var centerY = 0;
32308         var centerX = 0;
32309         //var x = 0, y = 0;
32310
32311         var rs = this.regions;
32312         var north = rs["north"];
32313         var south = rs["south"]; 
32314         var west = rs["west"];
32315         var east = rs["east"];
32316         var center = rs["center"];
32317         //if(this.hideOnLayout){ // not supported anymore
32318             //c.el.setStyle("display", "none");
32319         //}
32320         if(north && north.isVisible()){
32321             var b = north.getBox();
32322             var m = north.getMargins();
32323             b.width = w - (m.left+m.right);
32324             b.x = m.left;
32325             b.y = m.top;
32326             centerY = b.height + b.y + m.bottom;
32327             centerH -= centerY;
32328             north.updateBox(this.safeBox(b));
32329         }
32330         if(south && south.isVisible()){
32331             var b = south.getBox();
32332             var m = south.getMargins();
32333             b.width = w - (m.left+m.right);
32334             b.x = m.left;
32335             var totalHeight = (b.height + m.top + m.bottom);
32336             b.y = h - totalHeight + m.top;
32337             centerH -= totalHeight;
32338             south.updateBox(this.safeBox(b));
32339         }
32340         if(west && west.isVisible()){
32341             var b = west.getBox();
32342             var m = west.getMargins();
32343             b.height = centerH - (m.top+m.bottom);
32344             b.x = m.left;
32345             b.y = centerY + m.top;
32346             var totalWidth = (b.width + m.left + m.right);
32347             centerX += totalWidth;
32348             centerW -= totalWidth;
32349             west.updateBox(this.safeBox(b));
32350         }
32351         if(east && east.isVisible()){
32352             var b = east.getBox();
32353             var m = east.getMargins();
32354             b.height = centerH - (m.top+m.bottom);
32355             var totalWidth = (b.width + m.left + m.right);
32356             b.x = w - totalWidth + m.left;
32357             b.y = centerY + m.top;
32358             centerW -= totalWidth;
32359             east.updateBox(this.safeBox(b));
32360         }
32361         if(center){
32362             var m = center.getMargins();
32363             var centerBox = {
32364                 x: centerX + m.left,
32365                 y: centerY + m.top,
32366                 width: centerW - (m.left+m.right),
32367                 height: centerH - (m.top+m.bottom)
32368             };
32369             //if(this.hideOnLayout){
32370                 //center.el.setStyle("display", "block");
32371             //}
32372             center.updateBox(this.safeBox(centerBox));
32373         }
32374         this.el.repaint();
32375         this.fireEvent("layout", this);
32376     },
32377
32378     // private
32379     safeBox : function(box){
32380         box.width = Math.max(0, box.width);
32381         box.height = Math.max(0, box.height);
32382         return box;
32383     },
32384
32385     /**
32386      * Adds a ContentPanel (or subclass) to this layout.
32387      * @param {String} target The target region key (north, south, east, west or center).
32388      * @param {Roo.ContentPanel} panel The panel to add
32389      * @return {Roo.ContentPanel} The added panel
32390      */
32391     add : function(target, panel){
32392          
32393         target = target.toLowerCase();
32394         return this.regions[target].add(panel);
32395     },
32396
32397     /**
32398      * Remove a ContentPanel (or subclass) to this layout.
32399      * @param {String} target The target region key (north, south, east, west or center).
32400      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32401      * @return {Roo.ContentPanel} The removed panel
32402      */
32403     remove : function(target, panel){
32404         target = target.toLowerCase();
32405         return this.regions[target].remove(panel);
32406     },
32407
32408     /**
32409      * Searches all regions for a panel with the specified id
32410      * @param {String} panelId
32411      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32412      */
32413     findPanel : function(panelId){
32414         var rs = this.regions;
32415         for(var target in rs){
32416             if(typeof rs[target] != "function"){
32417                 var p = rs[target].getPanel(panelId);
32418                 if(p){
32419                     return p;
32420                 }
32421             }
32422         }
32423         return null;
32424     },
32425
32426     /**
32427      * Searches all regions for a panel with the specified id and activates (shows) it.
32428      * @param {String/ContentPanel} panelId The panels id or the panel itself
32429      * @return {Roo.ContentPanel} The shown panel or null
32430      */
32431     showPanel : function(panelId) {
32432       var rs = this.regions;
32433       for(var target in rs){
32434          var r = rs[target];
32435          if(typeof r != "function"){
32436             if(r.hasPanel(panelId)){
32437                return r.showPanel(panelId);
32438             }
32439          }
32440       }
32441       return null;
32442    },
32443
32444    /**
32445      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32446      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32447      */
32448     restoreState : function(provider){
32449         if(!provider){
32450             provider = Roo.state.Manager;
32451         }
32452         var sm = new Roo.LayoutStateManager();
32453         sm.init(this, provider);
32454     },
32455
32456     /**
32457      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32458      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32459      * a valid ContentPanel config object.  Example:
32460      * <pre><code>
32461 // Create the main layout
32462 var layout = new Roo.BorderLayout('main-ct', {
32463     west: {
32464         split:true,
32465         minSize: 175,
32466         titlebar: true
32467     },
32468     center: {
32469         title:'Components'
32470     }
32471 }, 'main-ct');
32472
32473 // Create and add multiple ContentPanels at once via configs
32474 layout.batchAdd({
32475    west: {
32476        id: 'source-files',
32477        autoCreate:true,
32478        title:'Ext Source Files',
32479        autoScroll:true,
32480        fitToFrame:true
32481    },
32482    center : {
32483        el: cview,
32484        autoScroll:true,
32485        fitToFrame:true,
32486        toolbar: tb,
32487        resizeEl:'cbody'
32488    }
32489 });
32490 </code></pre>
32491      * @param {Object} regions An object containing ContentPanel configs by region name
32492      */
32493     batchAdd : function(regions){
32494         this.beginUpdate();
32495         for(var rname in regions){
32496             var lr = this.regions[rname];
32497             if(lr){
32498                 this.addTypedPanels(lr, regions[rname]);
32499             }
32500         }
32501         this.endUpdate();
32502     },
32503
32504     // private
32505     addTypedPanels : function(lr, ps){
32506         if(typeof ps == 'string'){
32507             lr.add(new Roo.ContentPanel(ps));
32508         }
32509         else if(ps instanceof Array){
32510             for(var i =0, len = ps.length; i < len; i++){
32511                 this.addTypedPanels(lr, ps[i]);
32512             }
32513         }
32514         else if(!ps.events){ // raw config?
32515             var el = ps.el;
32516             delete ps.el; // prevent conflict
32517             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32518         }
32519         else {  // panel object assumed!
32520             lr.add(ps);
32521         }
32522     },
32523     /**
32524      * Adds a xtype elements to the layout.
32525      * <pre><code>
32526
32527 layout.addxtype({
32528        xtype : 'ContentPanel',
32529        region: 'west',
32530        items: [ .... ]
32531    }
32532 );
32533
32534 layout.addxtype({
32535         xtype : 'NestedLayoutPanel',
32536         region: 'west',
32537         layout: {
32538            center: { },
32539            west: { }   
32540         },
32541         items : [ ... list of content panels or nested layout panels.. ]
32542    }
32543 );
32544 </code></pre>
32545      * @param {Object} cfg Xtype definition of item to add.
32546      */
32547     addxtype : function(cfg)
32548     {
32549         // basically accepts a pannel...
32550         // can accept a layout region..!?!?
32551         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32552         
32553         if (!cfg.xtype.match(/Panel$/)) {
32554             return false;
32555         }
32556         var ret = false;
32557         
32558         if (typeof(cfg.region) == 'undefined') {
32559             Roo.log("Failed to add Panel, region was not set");
32560             Roo.log(cfg);
32561             return false;
32562         }
32563         var region = cfg.region;
32564         delete cfg.region;
32565         
32566           
32567         var xitems = [];
32568         if (cfg.items) {
32569             xitems = cfg.items;
32570             delete cfg.items;
32571         }
32572         var nb = false;
32573         
32574         switch(cfg.xtype) 
32575         {
32576             case 'ContentPanel':  // ContentPanel (el, cfg)
32577             case 'ScrollPanel':  // ContentPanel (el, cfg)
32578             case 'ViewPanel': 
32579                 if(cfg.autoCreate) {
32580                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32581                 } else {
32582                     var el = this.el.createChild();
32583                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32584                 }
32585                 
32586                 this.add(region, ret);
32587                 break;
32588             
32589             
32590             case 'TreePanel': // our new panel!
32591                 cfg.el = this.el.createChild();
32592                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32593                 this.add(region, ret);
32594                 break;
32595             
32596             case 'NestedLayoutPanel': 
32597                 // create a new Layout (which is  a Border Layout...
32598                 var el = this.el.createChild();
32599                 var clayout = cfg.layout;
32600                 delete cfg.layout;
32601                 clayout.items   = clayout.items  || [];
32602                 // replace this exitems with the clayout ones..
32603                 xitems = clayout.items;
32604                  
32605                 
32606                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32607                     cfg.background = false;
32608                 }
32609                 var layout = new Roo.BorderLayout(el, clayout);
32610                 
32611                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32612                 //console.log('adding nested layout panel '  + cfg.toSource());
32613                 this.add(region, ret);
32614                 nb = {}; /// find first...
32615                 break;
32616                 
32617             case 'GridPanel': 
32618             
32619                 // needs grid and region
32620                 
32621                 //var el = this.getRegion(region).el.createChild();
32622                 var el = this.el.createChild();
32623                 // create the grid first...
32624                 
32625                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32626                 delete cfg.grid;
32627                 if (region == 'center' && this.active ) {
32628                     cfg.background = false;
32629                 }
32630                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32631                 
32632                 this.add(region, ret);
32633                 if (cfg.background) {
32634                     ret.on('activate', function(gp) {
32635                         if (!gp.grid.rendered) {
32636                             gp.grid.render();
32637                         }
32638                     });
32639                 } else {
32640                     grid.render();
32641                 }
32642                 break;
32643            
32644            
32645            
32646                 
32647                 
32648                 
32649             default:
32650                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32651                     
32652                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32653                     this.add(region, ret);
32654                 } else {
32655                 
32656                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32657                     return null;
32658                 }
32659                 
32660              // GridPanel (grid, cfg)
32661             
32662         }
32663         this.beginUpdate();
32664         // add children..
32665         var region = '';
32666         var abn = {};
32667         Roo.each(xitems, function(i)  {
32668             region = nb && i.region ? i.region : false;
32669             
32670             var add = ret.addxtype(i);
32671            
32672             if (region) {
32673                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32674                 if (!i.background) {
32675                     abn[region] = nb[region] ;
32676                 }
32677             }
32678             
32679         });
32680         this.endUpdate();
32681
32682         // make the last non-background panel active..
32683         //if (nb) { Roo.log(abn); }
32684         if (nb) {
32685             
32686             for(var r in abn) {
32687                 region = this.getRegion(r);
32688                 if (region) {
32689                     // tried using nb[r], but it does not work..
32690                      
32691                     region.showPanel(abn[r]);
32692                    
32693                 }
32694             }
32695         }
32696         return ret;
32697         
32698     }
32699 });
32700
32701 /**
32702  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32703  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32704  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32705  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32706  * <pre><code>
32707 // shorthand
32708 var CP = Roo.ContentPanel;
32709
32710 var layout = Roo.BorderLayout.create({
32711     north: {
32712         initialSize: 25,
32713         titlebar: false,
32714         panels: [new CP("north", "North")]
32715     },
32716     west: {
32717         split:true,
32718         initialSize: 200,
32719         minSize: 175,
32720         maxSize: 400,
32721         titlebar: true,
32722         collapsible: true,
32723         panels: [new CP("west", {title: "West"})]
32724     },
32725     east: {
32726         split:true,
32727         initialSize: 202,
32728         minSize: 175,
32729         maxSize: 400,
32730         titlebar: true,
32731         collapsible: true,
32732         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32733     },
32734     south: {
32735         split:true,
32736         initialSize: 100,
32737         minSize: 100,
32738         maxSize: 200,
32739         titlebar: true,
32740         collapsible: true,
32741         panels: [new CP("south", {title: "South", closable: true})]
32742     },
32743     center: {
32744         titlebar: true,
32745         autoScroll:true,
32746         resizeTabs: true,
32747         minTabWidth: 50,
32748         preferredTabWidth: 150,
32749         panels: [
32750             new CP("center1", {title: "Close Me", closable: true}),
32751             new CP("center2", {title: "Center Panel", closable: false})
32752         ]
32753     }
32754 }, document.body);
32755
32756 layout.getRegion("center").showPanel("center1");
32757 </code></pre>
32758  * @param config
32759  * @param targetEl
32760  */
32761 Roo.BorderLayout.create = function(config, targetEl){
32762     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32763     layout.beginUpdate();
32764     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32765     for(var j = 0, jlen = regions.length; j < jlen; j++){
32766         var lr = regions[j];
32767         if(layout.regions[lr] && config[lr].panels){
32768             var r = layout.regions[lr];
32769             var ps = config[lr].panels;
32770             layout.addTypedPanels(r, ps);
32771         }
32772     }
32773     layout.endUpdate();
32774     return layout;
32775 };
32776
32777 // private
32778 Roo.BorderLayout.RegionFactory = {
32779     // private
32780     validRegions : ["north","south","east","west","center"],
32781
32782     // private
32783     create : function(target, mgr, config){
32784         target = target.toLowerCase();
32785         if(config.lightweight || config.basic){
32786             return new Roo.BasicLayoutRegion(mgr, config, target);
32787         }
32788         switch(target){
32789             case "north":
32790                 return new Roo.NorthLayoutRegion(mgr, config);
32791             case "south":
32792                 return new Roo.SouthLayoutRegion(mgr, config);
32793             case "east":
32794                 return new Roo.EastLayoutRegion(mgr, config);
32795             case "west":
32796                 return new Roo.WestLayoutRegion(mgr, config);
32797             case "center":
32798                 return new Roo.CenterLayoutRegion(mgr, config);
32799         }
32800         throw 'Layout region "'+target+'" not supported.';
32801     }
32802 };/*
32803  * Based on:
32804  * Ext JS Library 1.1.1
32805  * Copyright(c) 2006-2007, Ext JS, LLC.
32806  *
32807  * Originally Released Under LGPL - original licence link has changed is not relivant.
32808  *
32809  * Fork - LGPL
32810  * <script type="text/javascript">
32811  */
32812  
32813 /**
32814  * @class Roo.BasicLayoutRegion
32815  * @extends Roo.util.Observable
32816  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32817  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32818  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32819  */
32820 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32821     this.mgr = mgr;
32822     this.position  = pos;
32823     this.events = {
32824         /**
32825          * @scope Roo.BasicLayoutRegion
32826          */
32827         
32828         /**
32829          * @event beforeremove
32830          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32831          * @param {Roo.LayoutRegion} this
32832          * @param {Roo.ContentPanel} panel The panel
32833          * @param {Object} e The cancel event object
32834          */
32835         "beforeremove" : true,
32836         /**
32837          * @event invalidated
32838          * Fires when the layout for this region is changed.
32839          * @param {Roo.LayoutRegion} this
32840          */
32841         "invalidated" : true,
32842         /**
32843          * @event visibilitychange
32844          * Fires when this region is shown or hidden 
32845          * @param {Roo.LayoutRegion} this
32846          * @param {Boolean} visibility true or false
32847          */
32848         "visibilitychange" : true,
32849         /**
32850          * @event paneladded
32851          * Fires when a panel is added. 
32852          * @param {Roo.LayoutRegion} this
32853          * @param {Roo.ContentPanel} panel The panel
32854          */
32855         "paneladded" : true,
32856         /**
32857          * @event panelremoved
32858          * Fires when a panel is removed. 
32859          * @param {Roo.LayoutRegion} this
32860          * @param {Roo.ContentPanel} panel The panel
32861          */
32862         "panelremoved" : true,
32863         /**
32864          * @event beforecollapse
32865          * Fires when this region before collapse.
32866          * @param {Roo.LayoutRegion} this
32867          */
32868         "beforecollapse" : true,
32869         /**
32870          * @event collapsed
32871          * Fires when this region is collapsed.
32872          * @param {Roo.LayoutRegion} this
32873          */
32874         "collapsed" : true,
32875         /**
32876          * @event expanded
32877          * Fires when this region is expanded.
32878          * @param {Roo.LayoutRegion} this
32879          */
32880         "expanded" : true,
32881         /**
32882          * @event slideshow
32883          * Fires when this region is slid into view.
32884          * @param {Roo.LayoutRegion} this
32885          */
32886         "slideshow" : true,
32887         /**
32888          * @event slidehide
32889          * Fires when this region slides out of view. 
32890          * @param {Roo.LayoutRegion} this
32891          */
32892         "slidehide" : true,
32893         /**
32894          * @event panelactivated
32895          * Fires when a panel is activated. 
32896          * @param {Roo.LayoutRegion} this
32897          * @param {Roo.ContentPanel} panel The activated panel
32898          */
32899         "panelactivated" : true,
32900         /**
32901          * @event resized
32902          * Fires when the user resizes this region. 
32903          * @param {Roo.LayoutRegion} this
32904          * @param {Number} newSize The new size (width for east/west, height for north/south)
32905          */
32906         "resized" : true
32907     };
32908     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32909     this.panels = new Roo.util.MixedCollection();
32910     this.panels.getKey = this.getPanelId.createDelegate(this);
32911     this.box = null;
32912     this.activePanel = null;
32913     // ensure listeners are added...
32914     
32915     if (config.listeners || config.events) {
32916         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
32917             listeners : config.listeners || {},
32918             events : config.events || {}
32919         });
32920     }
32921     
32922     if(skipConfig !== true){
32923         this.applyConfig(config);
32924     }
32925 };
32926
32927 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
32928     getPanelId : function(p){
32929         return p.getId();
32930     },
32931     
32932     applyConfig : function(config){
32933         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32934         this.config = config;
32935         
32936     },
32937     
32938     /**
32939      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32940      * the width, for horizontal (north, south) the height.
32941      * @param {Number} newSize The new width or height
32942      */
32943     resizeTo : function(newSize){
32944         var el = this.el ? this.el :
32945                  (this.activePanel ? this.activePanel.getEl() : null);
32946         if(el){
32947             switch(this.position){
32948                 case "east":
32949                 case "west":
32950                     el.setWidth(newSize);
32951                     this.fireEvent("resized", this, newSize);
32952                 break;
32953                 case "north":
32954                 case "south":
32955                     el.setHeight(newSize);
32956                     this.fireEvent("resized", this, newSize);
32957                 break;                
32958             }
32959         }
32960     },
32961     
32962     getBox : function(){
32963         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32964     },
32965     
32966     getMargins : function(){
32967         return this.margins;
32968     },
32969     
32970     updateBox : function(box){
32971         this.box = box;
32972         var el = this.activePanel.getEl();
32973         el.dom.style.left = box.x + "px";
32974         el.dom.style.top = box.y + "px";
32975         this.activePanel.setSize(box.width, box.height);
32976     },
32977     
32978     /**
32979      * Returns the container element for this region.
32980      * @return {Roo.Element}
32981      */
32982     getEl : function(){
32983         return this.activePanel;
32984     },
32985     
32986     /**
32987      * Returns true if this region is currently visible.
32988      * @return {Boolean}
32989      */
32990     isVisible : function(){
32991         return this.activePanel ? true : false;
32992     },
32993     
32994     setActivePanel : function(panel){
32995         panel = this.getPanel(panel);
32996         if(this.activePanel && this.activePanel != panel){
32997             this.activePanel.setActiveState(false);
32998             this.activePanel.getEl().setLeftTop(-10000,-10000);
32999         }
33000         this.activePanel = panel;
33001         panel.setActiveState(true);
33002         if(this.box){
33003             panel.setSize(this.box.width, this.box.height);
33004         }
33005         this.fireEvent("panelactivated", this, panel);
33006         this.fireEvent("invalidated");
33007     },
33008     
33009     /**
33010      * Show the specified panel.
33011      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33012      * @return {Roo.ContentPanel} The shown panel or null
33013      */
33014     showPanel : function(panel){
33015         if(panel = this.getPanel(panel)){
33016             this.setActivePanel(panel);
33017         }
33018         return panel;
33019     },
33020     
33021     /**
33022      * Get the active panel for this region.
33023      * @return {Roo.ContentPanel} The active panel or null
33024      */
33025     getActivePanel : function(){
33026         return this.activePanel;
33027     },
33028     
33029     /**
33030      * Add the passed ContentPanel(s)
33031      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33032      * @return {Roo.ContentPanel} The panel added (if only one was added)
33033      */
33034     add : function(panel){
33035         if(arguments.length > 1){
33036             for(var i = 0, len = arguments.length; i < len; i++) {
33037                 this.add(arguments[i]);
33038             }
33039             return null;
33040         }
33041         if(this.hasPanel(panel)){
33042             this.showPanel(panel);
33043             return panel;
33044         }
33045         var el = panel.getEl();
33046         if(el.dom.parentNode != this.mgr.el.dom){
33047             this.mgr.el.dom.appendChild(el.dom);
33048         }
33049         if(panel.setRegion){
33050             panel.setRegion(this);
33051         }
33052         this.panels.add(panel);
33053         el.setStyle("position", "absolute");
33054         if(!panel.background){
33055             this.setActivePanel(panel);
33056             if(this.config.initialSize && this.panels.getCount()==1){
33057                 this.resizeTo(this.config.initialSize);
33058             }
33059         }
33060         this.fireEvent("paneladded", this, panel);
33061         return panel;
33062     },
33063     
33064     /**
33065      * Returns true if the panel is in this region.
33066      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33067      * @return {Boolean}
33068      */
33069     hasPanel : function(panel){
33070         if(typeof panel == "object"){ // must be panel obj
33071             panel = panel.getId();
33072         }
33073         return this.getPanel(panel) ? true : false;
33074     },
33075     
33076     /**
33077      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33078      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33079      * @param {Boolean} preservePanel Overrides the config preservePanel option
33080      * @return {Roo.ContentPanel} The panel that was removed
33081      */
33082     remove : function(panel, preservePanel){
33083         panel = this.getPanel(panel);
33084         if(!panel){
33085             return null;
33086         }
33087         var e = {};
33088         this.fireEvent("beforeremove", this, panel, e);
33089         if(e.cancel === true){
33090             return null;
33091         }
33092         var panelId = panel.getId();
33093         this.panels.removeKey(panelId);
33094         return panel;
33095     },
33096     
33097     /**
33098      * Returns the panel specified or null if it's not in this region.
33099      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33100      * @return {Roo.ContentPanel}
33101      */
33102     getPanel : function(id){
33103         if(typeof id == "object"){ // must be panel obj
33104             return id;
33105         }
33106         return this.panels.get(id);
33107     },
33108     
33109     /**
33110      * Returns this regions position (north/south/east/west/center).
33111      * @return {String} 
33112      */
33113     getPosition: function(){
33114         return this.position;    
33115     }
33116 });/*
33117  * Based on:
33118  * Ext JS Library 1.1.1
33119  * Copyright(c) 2006-2007, Ext JS, LLC.
33120  *
33121  * Originally Released Under LGPL - original licence link has changed is not relivant.
33122  *
33123  * Fork - LGPL
33124  * <script type="text/javascript">
33125  */
33126  
33127 /**
33128  * @class Roo.LayoutRegion
33129  * @extends Roo.BasicLayoutRegion
33130  * This class represents a region in a layout manager.
33131  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33132  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33133  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33134  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33135  * @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})
33136  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33137  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33138  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33139  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33140  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33141  * @cfg {String}    title           The title for the region (overrides panel titles)
33142  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33143  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33144  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33145  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33146  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33147  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33148  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33149  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33150  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33151  * @cfg {Boolean}   showPin         True to show a pin button
33152  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33153  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33154  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33155  * @cfg {Number}    width           For East/West panels
33156  * @cfg {Number}    height          For North/South panels
33157  * @cfg {Boolean}   split           To show the splitter
33158  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33159  */
33160 Roo.LayoutRegion = function(mgr, config, pos){
33161     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33162     var dh = Roo.DomHelper;
33163     /** This region's container element 
33164     * @type Roo.Element */
33165     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33166     /** This region's title element 
33167     * @type Roo.Element */
33168
33169     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33170         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33171         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33172     ]}, true);
33173     this.titleEl.enableDisplayMode();
33174     /** This region's title text element 
33175     * @type HTMLElement */
33176     this.titleTextEl = this.titleEl.dom.firstChild;
33177     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33178     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33179     this.closeBtn.enableDisplayMode();
33180     this.closeBtn.on("click", this.closeClicked, this);
33181     this.closeBtn.hide();
33182
33183     this.createBody(config);
33184     this.visible = true;
33185     this.collapsed = false;
33186
33187     if(config.hideWhenEmpty){
33188         this.hide();
33189         this.on("paneladded", this.validateVisibility, this);
33190         this.on("panelremoved", this.validateVisibility, this);
33191     }
33192     this.applyConfig(config);
33193 };
33194
33195 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33196
33197     createBody : function(){
33198         /** This region's body element 
33199         * @type Roo.Element */
33200         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33201     },
33202
33203     applyConfig : function(c){
33204         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33205             var dh = Roo.DomHelper;
33206             if(c.titlebar !== false){
33207                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33208                 this.collapseBtn.on("click", this.collapse, this);
33209                 this.collapseBtn.enableDisplayMode();
33210
33211                 if(c.showPin === true || this.showPin){
33212                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33213                     this.stickBtn.enableDisplayMode();
33214                     this.stickBtn.on("click", this.expand, this);
33215                     this.stickBtn.hide();
33216                 }
33217             }
33218             /** This region's collapsed element
33219             * @type Roo.Element */
33220             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33221                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33222             ]}, true);
33223             if(c.floatable !== false){
33224                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33225                this.collapsedEl.on("click", this.collapseClick, this);
33226             }
33227
33228             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33229                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33230                    id: "message", unselectable: "on", style:{"float":"left"}});
33231                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33232              }
33233             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33234             this.expandBtn.on("click", this.expand, this);
33235         }
33236         if(this.collapseBtn){
33237             this.collapseBtn.setVisible(c.collapsible == true);
33238         }
33239         this.cmargins = c.cmargins || this.cmargins ||
33240                          (this.position == "west" || this.position == "east" ?
33241                              {top: 0, left: 2, right:2, bottom: 0} :
33242                              {top: 2, left: 0, right:0, bottom: 2});
33243         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33244         this.bottomTabs = c.tabPosition != "top";
33245         this.autoScroll = c.autoScroll || false;
33246         if(this.autoScroll){
33247             this.bodyEl.setStyle("overflow", "auto");
33248         }else{
33249             this.bodyEl.setStyle("overflow", "hidden");
33250         }
33251         //if(c.titlebar !== false){
33252             if((!c.titlebar && !c.title) || c.titlebar === false){
33253                 this.titleEl.hide();
33254             }else{
33255                 this.titleEl.show();
33256                 if(c.title){
33257                     this.titleTextEl.innerHTML = c.title;
33258                 }
33259             }
33260         //}
33261         this.duration = c.duration || .30;
33262         this.slideDuration = c.slideDuration || .45;
33263         this.config = c;
33264         if(c.collapsed){
33265             this.collapse(true);
33266         }
33267         if(c.hidden){
33268             this.hide();
33269         }
33270     },
33271     /**
33272      * Returns true if this region is currently visible.
33273      * @return {Boolean}
33274      */
33275     isVisible : function(){
33276         return this.visible;
33277     },
33278
33279     /**
33280      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33281      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33282      */
33283     setCollapsedTitle : function(title){
33284         title = title || "&#160;";
33285         if(this.collapsedTitleTextEl){
33286             this.collapsedTitleTextEl.innerHTML = title;
33287         }
33288     },
33289
33290     getBox : function(){
33291         var b;
33292         if(!this.collapsed){
33293             b = this.el.getBox(false, true);
33294         }else{
33295             b = this.collapsedEl.getBox(false, true);
33296         }
33297         return b;
33298     },
33299
33300     getMargins : function(){
33301         return this.collapsed ? this.cmargins : this.margins;
33302     },
33303
33304     highlight : function(){
33305         this.el.addClass("x-layout-panel-dragover");
33306     },
33307
33308     unhighlight : function(){
33309         this.el.removeClass("x-layout-panel-dragover");
33310     },
33311
33312     updateBox : function(box){
33313         this.box = box;
33314         if(!this.collapsed){
33315             this.el.dom.style.left = box.x + "px";
33316             this.el.dom.style.top = box.y + "px";
33317             this.updateBody(box.width, box.height);
33318         }else{
33319             this.collapsedEl.dom.style.left = box.x + "px";
33320             this.collapsedEl.dom.style.top = box.y + "px";
33321             this.collapsedEl.setSize(box.width, box.height);
33322         }
33323         if(this.tabs){
33324             this.tabs.autoSizeTabs();
33325         }
33326     },
33327
33328     updateBody : function(w, h){
33329         if(w !== null){
33330             this.el.setWidth(w);
33331             w -= this.el.getBorderWidth("rl");
33332             if(this.config.adjustments){
33333                 w += this.config.adjustments[0];
33334             }
33335         }
33336         if(h !== null){
33337             this.el.setHeight(h);
33338             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33339             h -= this.el.getBorderWidth("tb");
33340             if(this.config.adjustments){
33341                 h += this.config.adjustments[1];
33342             }
33343             this.bodyEl.setHeight(h);
33344             if(this.tabs){
33345                 h = this.tabs.syncHeight(h);
33346             }
33347         }
33348         if(this.panelSize){
33349             w = w !== null ? w : this.panelSize.width;
33350             h = h !== null ? h : this.panelSize.height;
33351         }
33352         if(this.activePanel){
33353             var el = this.activePanel.getEl();
33354             w = w !== null ? w : el.getWidth();
33355             h = h !== null ? h : el.getHeight();
33356             this.panelSize = {width: w, height: h};
33357             this.activePanel.setSize(w, h);
33358         }
33359         if(Roo.isIE && this.tabs){
33360             this.tabs.el.repaint();
33361         }
33362     },
33363
33364     /**
33365      * Returns the container element for this region.
33366      * @return {Roo.Element}
33367      */
33368     getEl : function(){
33369         return this.el;
33370     },
33371
33372     /**
33373      * Hides this region.
33374      */
33375     hide : function(){
33376         if(!this.collapsed){
33377             this.el.dom.style.left = "-2000px";
33378             this.el.hide();
33379         }else{
33380             this.collapsedEl.dom.style.left = "-2000px";
33381             this.collapsedEl.hide();
33382         }
33383         this.visible = false;
33384         this.fireEvent("visibilitychange", this, false);
33385     },
33386
33387     /**
33388      * Shows this region if it was previously hidden.
33389      */
33390     show : function(){
33391         if(!this.collapsed){
33392             this.el.show();
33393         }else{
33394             this.collapsedEl.show();
33395         }
33396         this.visible = true;
33397         this.fireEvent("visibilitychange", this, true);
33398     },
33399
33400     closeClicked : function(){
33401         if(this.activePanel){
33402             this.remove(this.activePanel);
33403         }
33404     },
33405
33406     collapseClick : function(e){
33407         if(this.isSlid){
33408            e.stopPropagation();
33409            this.slideIn();
33410         }else{
33411            e.stopPropagation();
33412            this.slideOut();
33413         }
33414     },
33415
33416     /**
33417      * Collapses this region.
33418      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33419      */
33420     collapse : function(skipAnim, skipCheck){
33421         if(this.collapsed) {
33422             return;
33423         }
33424         
33425         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33426             
33427             this.collapsed = true;
33428             if(this.split){
33429                 this.split.el.hide();
33430             }
33431             if(this.config.animate && skipAnim !== true){
33432                 this.fireEvent("invalidated", this);
33433                 this.animateCollapse();
33434             }else{
33435                 this.el.setLocation(-20000,-20000);
33436                 this.el.hide();
33437                 this.collapsedEl.show();
33438                 this.fireEvent("collapsed", this);
33439                 this.fireEvent("invalidated", this);
33440             }
33441         }
33442         
33443     },
33444
33445     animateCollapse : function(){
33446         // overridden
33447     },
33448
33449     /**
33450      * Expands this region if it was previously collapsed.
33451      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33452      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33453      */
33454     expand : function(e, skipAnim){
33455         if(e) {
33456             e.stopPropagation();
33457         }
33458         if(!this.collapsed || this.el.hasActiveFx()) {
33459             return;
33460         }
33461         if(this.isSlid){
33462             this.afterSlideIn();
33463             skipAnim = true;
33464         }
33465         this.collapsed = false;
33466         if(this.config.animate && skipAnim !== true){
33467             this.animateExpand();
33468         }else{
33469             this.el.show();
33470             if(this.split){
33471                 this.split.el.show();
33472             }
33473             this.collapsedEl.setLocation(-2000,-2000);
33474             this.collapsedEl.hide();
33475             this.fireEvent("invalidated", this);
33476             this.fireEvent("expanded", this);
33477         }
33478     },
33479
33480     animateExpand : function(){
33481         // overridden
33482     },
33483
33484     initTabs : function()
33485     {
33486         this.bodyEl.setStyle("overflow", "hidden");
33487         var ts = new Roo.TabPanel(
33488                 this.bodyEl.dom,
33489                 {
33490                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33491                     disableTooltips: this.config.disableTabTips,
33492                     toolbar : this.config.toolbar
33493                 }
33494         );
33495         if(this.config.hideTabs){
33496             ts.stripWrap.setDisplayed(false);
33497         }
33498         this.tabs = ts;
33499         ts.resizeTabs = this.config.resizeTabs === true;
33500         ts.minTabWidth = this.config.minTabWidth || 40;
33501         ts.maxTabWidth = this.config.maxTabWidth || 250;
33502         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33503         ts.monitorResize = false;
33504         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33505         ts.bodyEl.addClass('x-layout-tabs-body');
33506         this.panels.each(this.initPanelAsTab, this);
33507     },
33508
33509     initPanelAsTab : function(panel){
33510         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33511                     this.config.closeOnTab && panel.isClosable());
33512         if(panel.tabTip !== undefined){
33513             ti.setTooltip(panel.tabTip);
33514         }
33515         ti.on("activate", function(){
33516               this.setActivePanel(panel);
33517         }, this);
33518         if(this.config.closeOnTab){
33519             ti.on("beforeclose", function(t, e){
33520                 e.cancel = true;
33521                 this.remove(panel);
33522             }, this);
33523         }
33524         return ti;
33525     },
33526
33527     updatePanelTitle : function(panel, title){
33528         if(this.activePanel == panel){
33529             this.updateTitle(title);
33530         }
33531         if(this.tabs){
33532             var ti = this.tabs.getTab(panel.getEl().id);
33533             ti.setText(title);
33534             if(panel.tabTip !== undefined){
33535                 ti.setTooltip(panel.tabTip);
33536             }
33537         }
33538     },
33539
33540     updateTitle : function(title){
33541         if(this.titleTextEl && !this.config.title){
33542             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33543         }
33544     },
33545
33546     setActivePanel : function(panel){
33547         panel = this.getPanel(panel);
33548         if(this.activePanel && this.activePanel != panel){
33549             this.activePanel.setActiveState(false);
33550         }
33551         this.activePanel = panel;
33552         panel.setActiveState(true);
33553         if(this.panelSize){
33554             panel.setSize(this.panelSize.width, this.panelSize.height);
33555         }
33556         if(this.closeBtn){
33557             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33558         }
33559         this.updateTitle(panel.getTitle());
33560         if(this.tabs){
33561             this.fireEvent("invalidated", this);
33562         }
33563         this.fireEvent("panelactivated", this, panel);
33564     },
33565
33566     /**
33567      * Shows the specified panel.
33568      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33569      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33570      */
33571     showPanel : function(panel)
33572     {
33573         panel = this.getPanel(panel);
33574         if(panel){
33575             if(this.tabs){
33576                 var tab = this.tabs.getTab(panel.getEl().id);
33577                 if(tab.isHidden()){
33578                     this.tabs.unhideTab(tab.id);
33579                 }
33580                 tab.activate();
33581             }else{
33582                 this.setActivePanel(panel);
33583             }
33584         }
33585         return panel;
33586     },
33587
33588     /**
33589      * Get the active panel for this region.
33590      * @return {Roo.ContentPanel} The active panel or null
33591      */
33592     getActivePanel : function(){
33593         return this.activePanel;
33594     },
33595
33596     validateVisibility : function(){
33597         if(this.panels.getCount() < 1){
33598             this.updateTitle("&#160;");
33599             this.closeBtn.hide();
33600             this.hide();
33601         }else{
33602             if(!this.isVisible()){
33603                 this.show();
33604             }
33605         }
33606     },
33607
33608     /**
33609      * Adds the passed ContentPanel(s) to this region.
33610      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33611      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33612      */
33613     add : function(panel){
33614         if(arguments.length > 1){
33615             for(var i = 0, len = arguments.length; i < len; i++) {
33616                 this.add(arguments[i]);
33617             }
33618             return null;
33619         }
33620         if(this.hasPanel(panel)){
33621             this.showPanel(panel);
33622             return panel;
33623         }
33624         panel.setRegion(this);
33625         this.panels.add(panel);
33626         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33627             this.bodyEl.dom.appendChild(panel.getEl().dom);
33628             if(panel.background !== true){
33629                 this.setActivePanel(panel);
33630             }
33631             this.fireEvent("paneladded", this, panel);
33632             return panel;
33633         }
33634         if(!this.tabs){
33635             this.initTabs();
33636         }else{
33637             this.initPanelAsTab(panel);
33638         }
33639         if(panel.background !== true){
33640             this.tabs.activate(panel.getEl().id);
33641         }
33642         this.fireEvent("paneladded", this, panel);
33643         return panel;
33644     },
33645
33646     /**
33647      * Hides the tab for the specified panel.
33648      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33649      */
33650     hidePanel : function(panel){
33651         if(this.tabs && (panel = this.getPanel(panel))){
33652             this.tabs.hideTab(panel.getEl().id);
33653         }
33654     },
33655
33656     /**
33657      * Unhides the tab for a previously hidden panel.
33658      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33659      */
33660     unhidePanel : function(panel){
33661         if(this.tabs && (panel = this.getPanel(panel))){
33662             this.tabs.unhideTab(panel.getEl().id);
33663         }
33664     },
33665
33666     clearPanels : function(){
33667         while(this.panels.getCount() > 0){
33668              this.remove(this.panels.first());
33669         }
33670     },
33671
33672     /**
33673      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33674      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33675      * @param {Boolean} preservePanel Overrides the config preservePanel option
33676      * @return {Roo.ContentPanel} The panel that was removed
33677      */
33678     remove : function(panel, preservePanel){
33679         panel = this.getPanel(panel);
33680         if(!panel){
33681             return null;
33682         }
33683         var e = {};
33684         this.fireEvent("beforeremove", this, panel, e);
33685         if(e.cancel === true){
33686             return null;
33687         }
33688         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33689         var panelId = panel.getId();
33690         this.panels.removeKey(panelId);
33691         if(preservePanel){
33692             document.body.appendChild(panel.getEl().dom);
33693         }
33694         if(this.tabs){
33695             this.tabs.removeTab(panel.getEl().id);
33696         }else if (!preservePanel){
33697             this.bodyEl.dom.removeChild(panel.getEl().dom);
33698         }
33699         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33700             var p = this.panels.first();
33701             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33702             tempEl.appendChild(p.getEl().dom);
33703             this.bodyEl.update("");
33704             this.bodyEl.dom.appendChild(p.getEl().dom);
33705             tempEl = null;
33706             this.updateTitle(p.getTitle());
33707             this.tabs = null;
33708             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33709             this.setActivePanel(p);
33710         }
33711         panel.setRegion(null);
33712         if(this.activePanel == panel){
33713             this.activePanel = null;
33714         }
33715         if(this.config.autoDestroy !== false && preservePanel !== true){
33716             try{panel.destroy();}catch(e){}
33717         }
33718         this.fireEvent("panelremoved", this, panel);
33719         return panel;
33720     },
33721
33722     /**
33723      * Returns the TabPanel component used by this region
33724      * @return {Roo.TabPanel}
33725      */
33726     getTabs : function(){
33727         return this.tabs;
33728     },
33729
33730     createTool : function(parentEl, className){
33731         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33732             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33733         btn.addClassOnOver("x-layout-tools-button-over");
33734         return btn;
33735     }
33736 });/*
33737  * Based on:
33738  * Ext JS Library 1.1.1
33739  * Copyright(c) 2006-2007, Ext JS, LLC.
33740  *
33741  * Originally Released Under LGPL - original licence link has changed is not relivant.
33742  *
33743  * Fork - LGPL
33744  * <script type="text/javascript">
33745  */
33746  
33747
33748
33749 /**
33750  * @class Roo.SplitLayoutRegion
33751  * @extends Roo.LayoutRegion
33752  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33753  */
33754 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33755     this.cursor = cursor;
33756     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33757 };
33758
33759 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33760     splitTip : "Drag to resize.",
33761     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33762     useSplitTips : false,
33763
33764     applyConfig : function(config){
33765         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33766         if(config.split){
33767             if(!this.split){
33768                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33769                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33770                 /** The SplitBar for this region 
33771                 * @type Roo.SplitBar */
33772                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33773                 this.split.on("moved", this.onSplitMove, this);
33774                 this.split.useShim = config.useShim === true;
33775                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33776                 if(this.useSplitTips){
33777                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33778                 }
33779                 if(config.collapsible){
33780                     this.split.el.on("dblclick", this.collapse,  this);
33781                 }
33782             }
33783             if(typeof config.minSize != "undefined"){
33784                 this.split.minSize = config.minSize;
33785             }
33786             if(typeof config.maxSize != "undefined"){
33787                 this.split.maxSize = config.maxSize;
33788             }
33789             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33790                 this.hideSplitter();
33791             }
33792         }
33793     },
33794
33795     getHMaxSize : function(){
33796          var cmax = this.config.maxSize || 10000;
33797          var center = this.mgr.getRegion("center");
33798          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33799     },
33800
33801     getVMaxSize : function(){
33802          var cmax = this.config.maxSize || 10000;
33803          var center = this.mgr.getRegion("center");
33804          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33805     },
33806
33807     onSplitMove : function(split, newSize){
33808         this.fireEvent("resized", this, newSize);
33809     },
33810     
33811     /** 
33812      * Returns the {@link Roo.SplitBar} for this region.
33813      * @return {Roo.SplitBar}
33814      */
33815     getSplitBar : function(){
33816         return this.split;
33817     },
33818     
33819     hide : function(){
33820         this.hideSplitter();
33821         Roo.SplitLayoutRegion.superclass.hide.call(this);
33822     },
33823
33824     hideSplitter : function(){
33825         if(this.split){
33826             this.split.el.setLocation(-2000,-2000);
33827             this.split.el.hide();
33828         }
33829     },
33830
33831     show : function(){
33832         if(this.split){
33833             this.split.el.show();
33834         }
33835         Roo.SplitLayoutRegion.superclass.show.call(this);
33836     },
33837     
33838     beforeSlide: function(){
33839         if(Roo.isGecko){// firefox overflow auto bug workaround
33840             this.bodyEl.clip();
33841             if(this.tabs) {
33842                 this.tabs.bodyEl.clip();
33843             }
33844             if(this.activePanel){
33845                 this.activePanel.getEl().clip();
33846                 
33847                 if(this.activePanel.beforeSlide){
33848                     this.activePanel.beforeSlide();
33849                 }
33850             }
33851         }
33852     },
33853     
33854     afterSlide : function(){
33855         if(Roo.isGecko){// firefox overflow auto bug workaround
33856             this.bodyEl.unclip();
33857             if(this.tabs) {
33858                 this.tabs.bodyEl.unclip();
33859             }
33860             if(this.activePanel){
33861                 this.activePanel.getEl().unclip();
33862                 if(this.activePanel.afterSlide){
33863                     this.activePanel.afterSlide();
33864                 }
33865             }
33866         }
33867     },
33868
33869     initAutoHide : function(){
33870         if(this.autoHide !== false){
33871             if(!this.autoHideHd){
33872                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33873                 this.autoHideHd = {
33874                     "mouseout": function(e){
33875                         if(!e.within(this.el, true)){
33876                             st.delay(500);
33877                         }
33878                     },
33879                     "mouseover" : function(e){
33880                         st.cancel();
33881                     },
33882                     scope : this
33883                 };
33884             }
33885             this.el.on(this.autoHideHd);
33886         }
33887     },
33888
33889     clearAutoHide : function(){
33890         if(this.autoHide !== false){
33891             this.el.un("mouseout", this.autoHideHd.mouseout);
33892             this.el.un("mouseover", this.autoHideHd.mouseover);
33893         }
33894     },
33895
33896     clearMonitor : function(){
33897         Roo.get(document).un("click", this.slideInIf, this);
33898     },
33899
33900     // these names are backwards but not changed for compat
33901     slideOut : function(){
33902         if(this.isSlid || this.el.hasActiveFx()){
33903             return;
33904         }
33905         this.isSlid = true;
33906         if(this.collapseBtn){
33907             this.collapseBtn.hide();
33908         }
33909         this.closeBtnState = this.closeBtn.getStyle('display');
33910         this.closeBtn.hide();
33911         if(this.stickBtn){
33912             this.stickBtn.show();
33913         }
33914         this.el.show();
33915         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33916         this.beforeSlide();
33917         this.el.setStyle("z-index", 10001);
33918         this.el.slideIn(this.getSlideAnchor(), {
33919             callback: function(){
33920                 this.afterSlide();
33921                 this.initAutoHide();
33922                 Roo.get(document).on("click", this.slideInIf, this);
33923                 this.fireEvent("slideshow", this);
33924             },
33925             scope: this,
33926             block: true
33927         });
33928     },
33929
33930     afterSlideIn : function(){
33931         this.clearAutoHide();
33932         this.isSlid = false;
33933         this.clearMonitor();
33934         this.el.setStyle("z-index", "");
33935         if(this.collapseBtn){
33936             this.collapseBtn.show();
33937         }
33938         this.closeBtn.setStyle('display', this.closeBtnState);
33939         if(this.stickBtn){
33940             this.stickBtn.hide();
33941         }
33942         this.fireEvent("slidehide", this);
33943     },
33944
33945     slideIn : function(cb){
33946         if(!this.isSlid || this.el.hasActiveFx()){
33947             Roo.callback(cb);
33948             return;
33949         }
33950         this.isSlid = false;
33951         this.beforeSlide();
33952         this.el.slideOut(this.getSlideAnchor(), {
33953             callback: function(){
33954                 this.el.setLeftTop(-10000, -10000);
33955                 this.afterSlide();
33956                 this.afterSlideIn();
33957                 Roo.callback(cb);
33958             },
33959             scope: this,
33960             block: true
33961         });
33962     },
33963     
33964     slideInIf : function(e){
33965         if(!e.within(this.el)){
33966             this.slideIn();
33967         }
33968     },
33969
33970     animateCollapse : function(){
33971         this.beforeSlide();
33972         this.el.setStyle("z-index", 20000);
33973         var anchor = this.getSlideAnchor();
33974         this.el.slideOut(anchor, {
33975             callback : function(){
33976                 this.el.setStyle("z-index", "");
33977                 this.collapsedEl.slideIn(anchor, {duration:.3});
33978                 this.afterSlide();
33979                 this.el.setLocation(-10000,-10000);
33980                 this.el.hide();
33981                 this.fireEvent("collapsed", this);
33982             },
33983             scope: this,
33984             block: true
33985         });
33986     },
33987
33988     animateExpand : function(){
33989         this.beforeSlide();
33990         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33991         this.el.setStyle("z-index", 20000);
33992         this.collapsedEl.hide({
33993             duration:.1
33994         });
33995         this.el.slideIn(this.getSlideAnchor(), {
33996             callback : function(){
33997                 this.el.setStyle("z-index", "");
33998                 this.afterSlide();
33999                 if(this.split){
34000                     this.split.el.show();
34001                 }
34002                 this.fireEvent("invalidated", this);
34003                 this.fireEvent("expanded", this);
34004             },
34005             scope: this,
34006             block: true
34007         });
34008     },
34009
34010     anchors : {
34011         "west" : "left",
34012         "east" : "right",
34013         "north" : "top",
34014         "south" : "bottom"
34015     },
34016
34017     sanchors : {
34018         "west" : "l",
34019         "east" : "r",
34020         "north" : "t",
34021         "south" : "b"
34022     },
34023
34024     canchors : {
34025         "west" : "tl-tr",
34026         "east" : "tr-tl",
34027         "north" : "tl-bl",
34028         "south" : "bl-tl"
34029     },
34030
34031     getAnchor : function(){
34032         return this.anchors[this.position];
34033     },
34034
34035     getCollapseAnchor : function(){
34036         return this.canchors[this.position];
34037     },
34038
34039     getSlideAnchor : function(){
34040         return this.sanchors[this.position];
34041     },
34042
34043     getAlignAdj : function(){
34044         var cm = this.cmargins;
34045         switch(this.position){
34046             case "west":
34047                 return [0, 0];
34048             break;
34049             case "east":
34050                 return [0, 0];
34051             break;
34052             case "north":
34053                 return [0, 0];
34054             break;
34055             case "south":
34056                 return [0, 0];
34057             break;
34058         }
34059     },
34060
34061     getExpandAdj : function(){
34062         var c = this.collapsedEl, cm = this.cmargins;
34063         switch(this.position){
34064             case "west":
34065                 return [-(cm.right+c.getWidth()+cm.left), 0];
34066             break;
34067             case "east":
34068                 return [cm.right+c.getWidth()+cm.left, 0];
34069             break;
34070             case "north":
34071                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34072             break;
34073             case "south":
34074                 return [0, cm.top+cm.bottom+c.getHeight()];
34075             break;
34076         }
34077     }
34078 });/*
34079  * Based on:
34080  * Ext JS Library 1.1.1
34081  * Copyright(c) 2006-2007, Ext JS, LLC.
34082  *
34083  * Originally Released Under LGPL - original licence link has changed is not relivant.
34084  *
34085  * Fork - LGPL
34086  * <script type="text/javascript">
34087  */
34088 /*
34089  * These classes are private internal classes
34090  */
34091 Roo.CenterLayoutRegion = function(mgr, config){
34092     Roo.LayoutRegion.call(this, mgr, config, "center");
34093     this.visible = true;
34094     this.minWidth = config.minWidth || 20;
34095     this.minHeight = config.minHeight || 20;
34096 };
34097
34098 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34099     hide : function(){
34100         // center panel can't be hidden
34101     },
34102     
34103     show : function(){
34104         // center panel can't be hidden
34105     },
34106     
34107     getMinWidth: function(){
34108         return this.minWidth;
34109     },
34110     
34111     getMinHeight: function(){
34112         return this.minHeight;
34113     }
34114 });
34115
34116
34117 Roo.NorthLayoutRegion = function(mgr, config){
34118     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34119     if(this.split){
34120         this.split.placement = Roo.SplitBar.TOP;
34121         this.split.orientation = Roo.SplitBar.VERTICAL;
34122         this.split.el.addClass("x-layout-split-v");
34123     }
34124     var size = config.initialSize || config.height;
34125     if(typeof size != "undefined"){
34126         this.el.setHeight(size);
34127     }
34128 };
34129 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34130     orientation: Roo.SplitBar.VERTICAL,
34131     getBox : function(){
34132         if(this.collapsed){
34133             return this.collapsedEl.getBox();
34134         }
34135         var box = this.el.getBox();
34136         if(this.split){
34137             box.height += this.split.el.getHeight();
34138         }
34139         return box;
34140     },
34141     
34142     updateBox : function(box){
34143         if(this.split && !this.collapsed){
34144             box.height -= this.split.el.getHeight();
34145             this.split.el.setLeft(box.x);
34146             this.split.el.setTop(box.y+box.height);
34147             this.split.el.setWidth(box.width);
34148         }
34149         if(this.collapsed){
34150             this.updateBody(box.width, null);
34151         }
34152         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34153     }
34154 });
34155
34156 Roo.SouthLayoutRegion = function(mgr, config){
34157     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34158     if(this.split){
34159         this.split.placement = Roo.SplitBar.BOTTOM;
34160         this.split.orientation = Roo.SplitBar.VERTICAL;
34161         this.split.el.addClass("x-layout-split-v");
34162     }
34163     var size = config.initialSize || config.height;
34164     if(typeof size != "undefined"){
34165         this.el.setHeight(size);
34166     }
34167 };
34168 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34169     orientation: Roo.SplitBar.VERTICAL,
34170     getBox : function(){
34171         if(this.collapsed){
34172             return this.collapsedEl.getBox();
34173         }
34174         var box = this.el.getBox();
34175         if(this.split){
34176             var sh = this.split.el.getHeight();
34177             box.height += sh;
34178             box.y -= sh;
34179         }
34180         return box;
34181     },
34182     
34183     updateBox : function(box){
34184         if(this.split && !this.collapsed){
34185             var sh = this.split.el.getHeight();
34186             box.height -= sh;
34187             box.y += sh;
34188             this.split.el.setLeft(box.x);
34189             this.split.el.setTop(box.y-sh);
34190             this.split.el.setWidth(box.width);
34191         }
34192         if(this.collapsed){
34193             this.updateBody(box.width, null);
34194         }
34195         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34196     }
34197 });
34198
34199 Roo.EastLayoutRegion = function(mgr, config){
34200     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34201     if(this.split){
34202         this.split.placement = Roo.SplitBar.RIGHT;
34203         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34204         this.split.el.addClass("x-layout-split-h");
34205     }
34206     var size = config.initialSize || config.width;
34207     if(typeof size != "undefined"){
34208         this.el.setWidth(size);
34209     }
34210 };
34211 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34212     orientation: Roo.SplitBar.HORIZONTAL,
34213     getBox : function(){
34214         if(this.collapsed){
34215             return this.collapsedEl.getBox();
34216         }
34217         var box = this.el.getBox();
34218         if(this.split){
34219             var sw = this.split.el.getWidth();
34220             box.width += sw;
34221             box.x -= sw;
34222         }
34223         return box;
34224     },
34225
34226     updateBox : function(box){
34227         if(this.split && !this.collapsed){
34228             var sw = this.split.el.getWidth();
34229             box.width -= sw;
34230             this.split.el.setLeft(box.x);
34231             this.split.el.setTop(box.y);
34232             this.split.el.setHeight(box.height);
34233             box.x += sw;
34234         }
34235         if(this.collapsed){
34236             this.updateBody(null, box.height);
34237         }
34238         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34239     }
34240 });
34241
34242 Roo.WestLayoutRegion = function(mgr, config){
34243     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34244     if(this.split){
34245         this.split.placement = Roo.SplitBar.LEFT;
34246         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34247         this.split.el.addClass("x-layout-split-h");
34248     }
34249     var size = config.initialSize || config.width;
34250     if(typeof size != "undefined"){
34251         this.el.setWidth(size);
34252     }
34253 };
34254 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34255     orientation: Roo.SplitBar.HORIZONTAL,
34256     getBox : function(){
34257         if(this.collapsed){
34258             return this.collapsedEl.getBox();
34259         }
34260         var box = this.el.getBox();
34261         if(this.split){
34262             box.width += this.split.el.getWidth();
34263         }
34264         return box;
34265     },
34266     
34267     updateBox : function(box){
34268         if(this.split && !this.collapsed){
34269             var sw = this.split.el.getWidth();
34270             box.width -= sw;
34271             this.split.el.setLeft(box.x+box.width);
34272             this.split.el.setTop(box.y);
34273             this.split.el.setHeight(box.height);
34274         }
34275         if(this.collapsed){
34276             this.updateBody(null, box.height);
34277         }
34278         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34279     }
34280 });
34281 /*
34282  * Based on:
34283  * Ext JS Library 1.1.1
34284  * Copyright(c) 2006-2007, Ext JS, LLC.
34285  *
34286  * Originally Released Under LGPL - original licence link has changed is not relivant.
34287  *
34288  * Fork - LGPL
34289  * <script type="text/javascript">
34290  */
34291  
34292  
34293 /*
34294  * Private internal class for reading and applying state
34295  */
34296 Roo.LayoutStateManager = function(layout){
34297      // default empty state
34298      this.state = {
34299         north: {},
34300         south: {},
34301         east: {},
34302         west: {}       
34303     };
34304 };
34305
34306 Roo.LayoutStateManager.prototype = {
34307     init : function(layout, provider){
34308         this.provider = provider;
34309         var state = provider.get(layout.id+"-layout-state");
34310         if(state){
34311             var wasUpdating = layout.isUpdating();
34312             if(!wasUpdating){
34313                 layout.beginUpdate();
34314             }
34315             for(var key in state){
34316                 if(typeof state[key] != "function"){
34317                     var rstate = state[key];
34318                     var r = layout.getRegion(key);
34319                     if(r && rstate){
34320                         if(rstate.size){
34321                             r.resizeTo(rstate.size);
34322                         }
34323                         if(rstate.collapsed == true){
34324                             r.collapse(true);
34325                         }else{
34326                             r.expand(null, true);
34327                         }
34328                     }
34329                 }
34330             }
34331             if(!wasUpdating){
34332                 layout.endUpdate();
34333             }
34334             this.state = state; 
34335         }
34336         this.layout = layout;
34337         layout.on("regionresized", this.onRegionResized, this);
34338         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34339         layout.on("regionexpanded", this.onRegionExpanded, this);
34340     },
34341     
34342     storeState : function(){
34343         this.provider.set(this.layout.id+"-layout-state", this.state);
34344     },
34345     
34346     onRegionResized : function(region, newSize){
34347         this.state[region.getPosition()].size = newSize;
34348         this.storeState();
34349     },
34350     
34351     onRegionCollapsed : function(region){
34352         this.state[region.getPosition()].collapsed = true;
34353         this.storeState();
34354     },
34355     
34356     onRegionExpanded : function(region){
34357         this.state[region.getPosition()].collapsed = false;
34358         this.storeState();
34359     }
34360 };/*
34361  * Based on:
34362  * Ext JS Library 1.1.1
34363  * Copyright(c) 2006-2007, Ext JS, LLC.
34364  *
34365  * Originally Released Under LGPL - original licence link has changed is not relivant.
34366  *
34367  * Fork - LGPL
34368  * <script type="text/javascript">
34369  */
34370 /**
34371  * @class Roo.ContentPanel
34372  * @extends Roo.util.Observable
34373  * @children Roo.form.Form Roo.JsonView Roo.View
34374  * @parent Roo.BorderLayout Roo.LayoutDialog builder
34375  * A basic ContentPanel element.
34376  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34377  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34378  * @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
34379  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34380  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34381  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34382  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
34383  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34384  * @cfg {String} title          The title for this panel
34385  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34386  * @cfg {String} url            Calls {@link #setUrl} with this value
34387  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
34388  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34389  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34390  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34391  * @cfg {String}    style  Extra style to add to the content panel
34392  * @cfg {Roo.menu.Menu} menu  popup menu
34393
34394  * @constructor
34395  * Create a new ContentPanel.
34396  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34397  * @param {String/Object} config A string to set only the title or a config object
34398  * @param {String} content (optional) Set the HTML content for this panel
34399  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34400  */
34401 Roo.ContentPanel = function(el, config, content){
34402     
34403      
34404     /*
34405     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34406         config = el;
34407         el = Roo.id();
34408     }
34409     if (config && config.parentLayout) { 
34410         el = config.parentLayout.el.createChild(); 
34411     }
34412     */
34413     if(el.autoCreate){ // xtype is available if this is called from factory
34414         config = el;
34415         el = Roo.id();
34416     }
34417     this.el = Roo.get(el);
34418     if(!this.el && config && config.autoCreate){
34419         if(typeof config.autoCreate == "object"){
34420             if(!config.autoCreate.id){
34421                 config.autoCreate.id = config.id||el;
34422             }
34423             this.el = Roo.DomHelper.append(document.body,
34424                         config.autoCreate, true);
34425         }else{
34426             this.el = Roo.DomHelper.append(document.body,
34427                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34428         }
34429     }
34430     
34431     
34432     this.closable = false;
34433     this.loaded = false;
34434     this.active = false;
34435     if(typeof config == "string"){
34436         this.title = config;
34437     }else{
34438         Roo.apply(this, config);
34439     }
34440     
34441     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34442         this.wrapEl = this.el.wrap();
34443         this.toolbar.container = this.el.insertSibling(false, 'before');
34444         this.toolbar = new Roo.Toolbar(this.toolbar);
34445     }
34446     
34447     // xtype created footer. - not sure if will work as we normally have to render first..
34448     if (this.footer && !this.footer.el && this.footer.xtype) {
34449         if (!this.wrapEl) {
34450             this.wrapEl = this.el.wrap();
34451         }
34452     
34453         this.footer.container = this.wrapEl.createChild();
34454          
34455         this.footer = Roo.factory(this.footer, Roo);
34456         
34457     }
34458     
34459     if(this.resizeEl){
34460         this.resizeEl = Roo.get(this.resizeEl, true);
34461     }else{
34462         this.resizeEl = this.el;
34463     }
34464     // handle view.xtype
34465     
34466  
34467     
34468     
34469     this.addEvents({
34470         /**
34471          * @event activate
34472          * Fires when this panel is activated. 
34473          * @param {Roo.ContentPanel} this
34474          */
34475         "activate" : true,
34476         /**
34477          * @event deactivate
34478          * Fires when this panel is activated. 
34479          * @param {Roo.ContentPanel} this
34480          */
34481         "deactivate" : true,
34482
34483         /**
34484          * @event resize
34485          * Fires when this panel is resized if fitToFrame is true.
34486          * @param {Roo.ContentPanel} this
34487          * @param {Number} width The width after any component adjustments
34488          * @param {Number} height The height after any component adjustments
34489          */
34490         "resize" : true,
34491         
34492          /**
34493          * @event render
34494          * Fires when this tab is created
34495          * @param {Roo.ContentPanel} this
34496          */
34497         "render" : true
34498          
34499         
34500     });
34501     
34502
34503     
34504     
34505     if(this.autoScroll){
34506         this.resizeEl.setStyle("overflow", "auto");
34507     } else {
34508         // fix randome scrolling
34509         this.el.on('scroll', function() {
34510             Roo.log('fix random scolling');
34511             this.scrollTo('top',0); 
34512         });
34513     }
34514     content = content || this.content;
34515     if(content){
34516         this.setContent(content);
34517     }
34518     if(config && config.url){
34519         this.setUrl(this.url, this.params, this.loadOnce);
34520     }
34521     
34522     
34523     
34524     Roo.ContentPanel.superclass.constructor.call(this);
34525     
34526     if (this.view && typeof(this.view.xtype) != 'undefined') {
34527         this.view.el = this.el.appendChild(document.createElement("div"));
34528         this.view = Roo.factory(this.view); 
34529         this.view.render  &&  this.view.render(false, '');  
34530     }
34531     
34532     
34533     this.fireEvent('render', this);
34534 };
34535
34536 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34537     tabTip:'',
34538     setRegion : function(region){
34539         this.region = region;
34540         if(region){
34541            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34542         }else{
34543            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34544         } 
34545     },
34546     
34547     /**
34548      * Returns the toolbar for this Panel if one was configured. 
34549      * @return {Roo.Toolbar} 
34550      */
34551     getToolbar : function(){
34552         return this.toolbar;
34553     },
34554     
34555     setActiveState : function(active){
34556         this.active = active;
34557         if(!active){
34558             this.fireEvent("deactivate", this);
34559         }else{
34560             this.fireEvent("activate", this);
34561         }
34562     },
34563     /**
34564      * Updates this panel's element
34565      * @param {String} content The new content
34566      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34567     */
34568     setContent : function(content, loadScripts){
34569         this.el.update(content, loadScripts);
34570     },
34571
34572     ignoreResize : function(w, h){
34573         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34574             return true;
34575         }else{
34576             this.lastSize = {width: w, height: h};
34577             return false;
34578         }
34579     },
34580     /**
34581      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34582      * @return {Roo.UpdateManager} The UpdateManager
34583      */
34584     getUpdateManager : function(){
34585         return this.el.getUpdateManager();
34586     },
34587      /**
34588      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34589      * @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:
34590 <pre><code>
34591 panel.load({
34592     url: "your-url.php",
34593     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34594     callback: yourFunction,
34595     scope: yourObject, //(optional scope)
34596     discardUrl: false,
34597     nocache: false,
34598     text: "Loading...",
34599     timeout: 30,
34600     scripts: false
34601 });
34602 </code></pre>
34603      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34604      * 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.
34605      * @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}
34606      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34607      * @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.
34608      * @return {Roo.ContentPanel} this
34609      */
34610     load : function(){
34611         var um = this.el.getUpdateManager();
34612         um.update.apply(um, arguments);
34613         return this;
34614     },
34615
34616
34617     /**
34618      * 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.
34619      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34620      * @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)
34621      * @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)
34622      * @return {Roo.UpdateManager} The UpdateManager
34623      */
34624     setUrl : function(url, params, loadOnce){
34625         if(this.refreshDelegate){
34626             this.removeListener("activate", this.refreshDelegate);
34627         }
34628         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34629         this.on("activate", this.refreshDelegate);
34630         return this.el.getUpdateManager();
34631     },
34632     
34633     _handleRefresh : function(url, params, loadOnce){
34634         if(!loadOnce || !this.loaded){
34635             var updater = this.el.getUpdateManager();
34636             updater.update(url, params, this._setLoaded.createDelegate(this));
34637         }
34638     },
34639     
34640     _setLoaded : function(){
34641         this.loaded = true;
34642     }, 
34643     
34644     /**
34645      * Returns this panel's id
34646      * @return {String} 
34647      */
34648     getId : function(){
34649         return this.el.id;
34650     },
34651     
34652     /** 
34653      * Returns this panel's element - used by regiosn to add.
34654      * @return {Roo.Element} 
34655      */
34656     getEl : function(){
34657         return this.wrapEl || this.el;
34658     },
34659     
34660     adjustForComponents : function(width, height)
34661     {
34662         //Roo.log('adjustForComponents ');
34663         if(this.resizeEl != this.el){
34664             width -= this.el.getFrameWidth('lr');
34665             height -= this.el.getFrameWidth('tb');
34666         }
34667         if(this.toolbar){
34668             var te = this.toolbar.getEl();
34669             height -= te.getHeight();
34670             te.setWidth(width);
34671         }
34672         if(this.footer){
34673             var te = this.footer.getEl();
34674             //Roo.log("footer:" + te.getHeight());
34675             
34676             height -= te.getHeight();
34677             te.setWidth(width);
34678         }
34679         
34680         
34681         if(this.adjustments){
34682             width += this.adjustments[0];
34683             height += this.adjustments[1];
34684         }
34685         return {"width": width, "height": height};
34686     },
34687     
34688     setSize : function(width, height){
34689         if(this.fitToFrame && !this.ignoreResize(width, height)){
34690             if(this.fitContainer && this.resizeEl != this.el){
34691                 this.el.setSize(width, height);
34692             }
34693             var size = this.adjustForComponents(width, height);
34694             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34695             this.fireEvent('resize', this, size.width, size.height);
34696         }
34697     },
34698     
34699     /**
34700      * Returns this panel's title
34701      * @return {String} 
34702      */
34703     getTitle : function(){
34704         return this.title;
34705     },
34706     
34707     /**
34708      * Set this panel's title
34709      * @param {String} title
34710      */
34711     setTitle : function(title){
34712         this.title = title;
34713         if(this.region){
34714             this.region.updatePanelTitle(this, title);
34715         }
34716     },
34717     
34718     /**
34719      * Returns true is this panel was configured to be closable
34720      * @return {Boolean} 
34721      */
34722     isClosable : function(){
34723         return this.closable;
34724     },
34725     
34726     beforeSlide : function(){
34727         this.el.clip();
34728         this.resizeEl.clip();
34729     },
34730     
34731     afterSlide : function(){
34732         this.el.unclip();
34733         this.resizeEl.unclip();
34734     },
34735     
34736     /**
34737      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34738      *   Will fail silently if the {@link #setUrl} method has not been called.
34739      *   This does not activate the panel, just updates its content.
34740      */
34741     refresh : function(){
34742         if(this.refreshDelegate){
34743            this.loaded = false;
34744            this.refreshDelegate();
34745         }
34746     },
34747     
34748     /**
34749      * Destroys this panel
34750      */
34751     destroy : function(){
34752         this.el.removeAllListeners();
34753         var tempEl = document.createElement("span");
34754         tempEl.appendChild(this.el.dom);
34755         tempEl.innerHTML = "";
34756         this.el.remove();
34757         this.el = null;
34758     },
34759     
34760     /**
34761      * form - if the content panel contains a form - this is a reference to it.
34762      * @type {Roo.form.Form}
34763      */
34764     form : false,
34765     /**
34766      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34767      *    This contains a reference to it.
34768      * @type {Roo.View}
34769      */
34770     view : false,
34771     
34772       /**
34773      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34774      * <pre><code>
34775
34776 layout.addxtype({
34777        xtype : 'Form',
34778        items: [ .... ]
34779    }
34780 );
34781
34782 </code></pre>
34783      * @param {Object} cfg Xtype definition of item to add.
34784      */
34785     
34786     addxtype : function(cfg) {
34787         // add form..
34788         if (cfg.xtype.match(/^Form$/)) {
34789             
34790             var el;
34791             //if (this.footer) {
34792             //    el = this.footer.container.insertSibling(false, 'before');
34793             //} else {
34794                 el = this.el.createChild();
34795             //}
34796
34797             this.form = new  Roo.form.Form(cfg);
34798             
34799             
34800             if ( this.form.allItems.length) {
34801                 this.form.render(el.dom);
34802             }
34803             return this.form;
34804         }
34805         // should only have one of theses..
34806         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34807             // views.. should not be just added - used named prop 'view''
34808             
34809             cfg.el = this.el.appendChild(document.createElement("div"));
34810             // factory?
34811             
34812             var ret = new Roo.factory(cfg);
34813              
34814              ret.render && ret.render(false, ''); // render blank..
34815             this.view = ret;
34816             return ret;
34817         }
34818         return false;
34819     }
34820 });
34821
34822 /**
34823  * @class Roo.GridPanel
34824  * @extends Roo.ContentPanel
34825  * @parent Roo.BorderLayout Roo.LayoutDialog builder
34826  * @constructor
34827  * Create a new GridPanel.
34828  * @cfg {Roo.grid.Grid} grid The grid for this panel
34829  */
34830 Roo.GridPanel = function(grid, config){
34831     
34832     // universal ctor...
34833     if (typeof(grid.grid) != 'undefined') {
34834         config = grid;
34835         grid = config.grid;
34836     }
34837     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34838         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34839         
34840     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34841     
34842     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34843     
34844     if(this.toolbar){
34845         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34846     }
34847     // xtype created footer. - not sure if will work as we normally have to render first..
34848     if (this.footer && !this.footer.el && this.footer.xtype) {
34849         
34850         this.footer.container = this.grid.getView().getFooterPanel(true);
34851         this.footer.dataSource = this.grid.dataSource;
34852         this.footer = Roo.factory(this.footer, Roo);
34853         
34854     }
34855     
34856     grid.monitorWindowResize = false; // turn off autosizing
34857     grid.autoHeight = false;
34858     grid.autoWidth = false;
34859     this.grid = grid;
34860     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34861 };
34862
34863 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34864     getId : function(){
34865         return this.grid.id;
34866     },
34867     
34868     /**
34869      * Returns the grid for this panel
34870      * @return {Roo.grid.Grid} 
34871      */
34872     getGrid : function(){
34873         return this.grid;    
34874     },
34875     
34876     setSize : function(width, height){
34877         if(!this.ignoreResize(width, height)){
34878             var grid = this.grid;
34879             var size = this.adjustForComponents(width, height);
34880             grid.getGridEl().setSize(size.width, size.height);
34881             grid.autoSize();
34882         }
34883     },
34884     
34885     beforeSlide : function(){
34886         this.grid.getView().scroller.clip();
34887     },
34888     
34889     afterSlide : function(){
34890         this.grid.getView().scroller.unclip();
34891     },
34892     
34893     destroy : function(){
34894         this.grid.destroy();
34895         delete this.grid;
34896         Roo.GridPanel.superclass.destroy.call(this); 
34897     }
34898 });
34899
34900
34901 /**
34902  * @class Roo.NestedLayoutPanel
34903  * @extends Roo.ContentPanel
34904  * @parent Roo.BorderLayout Roo.LayoutDialog builder
34905  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
34906  *
34907  * 
34908  * @constructor
34909  * Create a new NestedLayoutPanel.
34910  * 
34911  * 
34912  * @param {Roo.BorderLayout} layout [required] The layout for this panel
34913  * @param {String/Object} config A string to set only the title or a config object
34914  */
34915 Roo.NestedLayoutPanel = function(layout, config)
34916 {
34917     // construct with only one argument..
34918     /* FIXME - implement nicer consturctors
34919     if (layout.layout) {
34920         config = layout;
34921         layout = config.layout;
34922         delete config.layout;
34923     }
34924     if (layout.xtype && !layout.getEl) {
34925         // then layout needs constructing..
34926         layout = Roo.factory(layout, Roo);
34927     }
34928     */
34929     
34930     
34931     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
34932     
34933     layout.monitorWindowResize = false; // turn off autosizing
34934     this.layout = layout;
34935     this.layout.getEl().addClass("x-layout-nested-layout");
34936     
34937     
34938     
34939     
34940 };
34941
34942 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
34943
34944     setSize : function(width, height){
34945         if(!this.ignoreResize(width, height)){
34946             var size = this.adjustForComponents(width, height);
34947             var el = this.layout.getEl();
34948             el.setSize(size.width, size.height);
34949             var touch = el.dom.offsetWidth;
34950             this.layout.layout();
34951             // ie requires a double layout on the first pass
34952             if(Roo.isIE && !this.initialized){
34953                 this.initialized = true;
34954                 this.layout.layout();
34955             }
34956         }
34957     },
34958     
34959     // activate all subpanels if not currently active..
34960     
34961     setActiveState : function(active){
34962         this.active = active;
34963         if(!active){
34964             this.fireEvent("deactivate", this);
34965             return;
34966         }
34967         
34968         this.fireEvent("activate", this);
34969         // not sure if this should happen before or after..
34970         if (!this.layout) {
34971             return; // should not happen..
34972         }
34973         var reg = false;
34974         for (var r in this.layout.regions) {
34975             reg = this.layout.getRegion(r);
34976             if (reg.getActivePanel()) {
34977                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34978                 reg.setActivePanel(reg.getActivePanel());
34979                 continue;
34980             }
34981             if (!reg.panels.length) {
34982                 continue;
34983             }
34984             reg.showPanel(reg.getPanel(0));
34985         }
34986         
34987         
34988         
34989         
34990     },
34991     
34992     /**
34993      * Returns the nested BorderLayout for this panel
34994      * @return {Roo.BorderLayout} 
34995      */
34996     getLayout : function(){
34997         return this.layout;
34998     },
34999     
35000      /**
35001      * Adds a xtype elements to the layout of the nested panel
35002      * <pre><code>
35003
35004 panel.addxtype({
35005        xtype : 'ContentPanel',
35006        region: 'west',
35007        items: [ .... ]
35008    }
35009 );
35010
35011 panel.addxtype({
35012         xtype : 'NestedLayoutPanel',
35013         region: 'west',
35014         layout: {
35015            center: { },
35016            west: { }   
35017         },
35018         items : [ ... list of content panels or nested layout panels.. ]
35019    }
35020 );
35021 </code></pre>
35022      * @param {Object} cfg Xtype definition of item to add.
35023      */
35024     addxtype : function(cfg) {
35025         return this.layout.addxtype(cfg);
35026     
35027     }
35028 });
35029
35030 Roo.ScrollPanel = function(el, config, content){
35031     config = config || {};
35032     config.fitToFrame = true;
35033     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35034     
35035     this.el.dom.style.overflow = "hidden";
35036     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35037     this.el.removeClass("x-layout-inactive-content");
35038     this.el.on("mousewheel", this.onWheel, this);
35039
35040     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35041     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35042     up.unselectable(); down.unselectable();
35043     up.on("click", this.scrollUp, this);
35044     down.on("click", this.scrollDown, this);
35045     up.addClassOnOver("x-scroller-btn-over");
35046     down.addClassOnOver("x-scroller-btn-over");
35047     up.addClassOnClick("x-scroller-btn-click");
35048     down.addClassOnClick("x-scroller-btn-click");
35049     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35050
35051     this.resizeEl = this.el;
35052     this.el = wrap; this.up = up; this.down = down;
35053 };
35054
35055 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35056     increment : 100,
35057     wheelIncrement : 5,
35058     scrollUp : function(){
35059         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35060     },
35061
35062     scrollDown : function(){
35063         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35064     },
35065
35066     afterScroll : function(){
35067         var el = this.resizeEl;
35068         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35069         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35070         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35071     },
35072
35073     setSize : function(){
35074         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35075         this.afterScroll();
35076     },
35077
35078     onWheel : function(e){
35079         var d = e.getWheelDelta();
35080         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35081         this.afterScroll();
35082         e.stopEvent();
35083     },
35084
35085     setContent : function(content, loadScripts){
35086         this.resizeEl.update(content, loadScripts);
35087     }
35088
35089 });
35090
35091
35092
35093 /**
35094  * @class Roo.TreePanel
35095  * @extends Roo.ContentPanel
35096  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35097  * Treepanel component
35098  * 
35099  * @constructor
35100  * Create a new TreePanel. - defaults to fit/scoll contents.
35101  * @param {String/Object} config A string to set only the panel's title, or a config object
35102  */
35103 Roo.TreePanel = function(config){
35104     var el = config.el;
35105     var tree = config.tree;
35106     delete config.tree; 
35107     delete config.el; // hopefull!
35108     
35109     // wrapper for IE7 strict & safari scroll issue
35110     
35111     var treeEl = el.createChild();
35112     config.resizeEl = treeEl;
35113     
35114     
35115     
35116     Roo.TreePanel.superclass.constructor.call(this, el, config);
35117  
35118  
35119     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35120     //console.log(tree);
35121     this.on('activate', function()
35122     {
35123         if (this.tree.rendered) {
35124             return;
35125         }
35126         //console.log('render tree');
35127         this.tree.render();
35128     });
35129     // this should not be needed.. - it's actually the 'el' that resizes?
35130     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35131     
35132     //this.on('resize',  function (cp, w, h) {
35133     //        this.tree.innerCt.setWidth(w);
35134     //        this.tree.innerCt.setHeight(h);
35135     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35136     //});
35137
35138         
35139     
35140 };
35141
35142 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35143     fitToFrame : true,
35144     autoScroll : true,
35145     /*
35146      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
35147      */
35148     tree : false
35149
35150 });
35151
35152
35153
35154
35155
35156
35157
35158
35159
35160
35161
35162 /*
35163  * Based on:
35164  * Ext JS Library 1.1.1
35165  * Copyright(c) 2006-2007, Ext JS, LLC.
35166  *
35167  * Originally Released Under LGPL - original licence link has changed is not relivant.
35168  *
35169  * Fork - LGPL
35170  * <script type="text/javascript">
35171  */
35172  
35173
35174 /**
35175  * @class Roo.ReaderLayout
35176  * @extends Roo.BorderLayout
35177  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35178  * center region containing two nested regions (a top one for a list view and one for item preview below),
35179  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35180  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35181  * expedites the setup of the overall layout and regions for this common application style.
35182  * Example:
35183  <pre><code>
35184 var reader = new Roo.ReaderLayout();
35185 var CP = Roo.ContentPanel;  // shortcut for adding
35186
35187 reader.beginUpdate();
35188 reader.add("north", new CP("north", "North"));
35189 reader.add("west", new CP("west", {title: "West"}));
35190 reader.add("east", new CP("east", {title: "East"}));
35191
35192 reader.regions.listView.add(new CP("listView", "List"));
35193 reader.regions.preview.add(new CP("preview", "Preview"));
35194 reader.endUpdate();
35195 </code></pre>
35196 * @constructor
35197 * Create a new ReaderLayout
35198 * @param {Object} config Configuration options
35199 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35200 * document.body if omitted)
35201 */
35202 Roo.ReaderLayout = function(config, renderTo){
35203     var c = config || {size:{}};
35204     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35205         north: c.north !== false ? Roo.apply({
35206             split:false,
35207             initialSize: 32,
35208             titlebar: false
35209         }, c.north) : false,
35210         west: c.west !== false ? Roo.apply({
35211             split:true,
35212             initialSize: 200,
35213             minSize: 175,
35214             maxSize: 400,
35215             titlebar: true,
35216             collapsible: true,
35217             animate: true,
35218             margins:{left:5,right:0,bottom:5,top:5},
35219             cmargins:{left:5,right:5,bottom:5,top:5}
35220         }, c.west) : false,
35221         east: c.east !== false ? Roo.apply({
35222             split:true,
35223             initialSize: 200,
35224             minSize: 175,
35225             maxSize: 400,
35226             titlebar: true,
35227             collapsible: true,
35228             animate: true,
35229             margins:{left:0,right:5,bottom:5,top:5},
35230             cmargins:{left:5,right:5,bottom:5,top:5}
35231         }, c.east) : false,
35232         center: Roo.apply({
35233             tabPosition: 'top',
35234             autoScroll:false,
35235             closeOnTab: true,
35236             titlebar:false,
35237             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35238         }, c.center)
35239     });
35240
35241     this.el.addClass('x-reader');
35242
35243     this.beginUpdate();
35244
35245     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35246         south: c.preview !== false ? Roo.apply({
35247             split:true,
35248             initialSize: 200,
35249             minSize: 100,
35250             autoScroll:true,
35251             collapsible:true,
35252             titlebar: true,
35253             cmargins:{top:5,left:0, right:0, bottom:0}
35254         }, c.preview) : false,
35255         center: Roo.apply({
35256             autoScroll:false,
35257             titlebar:false,
35258             minHeight:200
35259         }, c.listView)
35260     });
35261     this.add('center', new Roo.NestedLayoutPanel(inner,
35262             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35263
35264     this.endUpdate();
35265
35266     this.regions.preview = inner.getRegion('south');
35267     this.regions.listView = inner.getRegion('center');
35268 };
35269
35270 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35271  * Based on:
35272  * Ext JS Library 1.1.1
35273  * Copyright(c) 2006-2007, Ext JS, LLC.
35274  *
35275  * Originally Released Under LGPL - original licence link has changed is not relivant.
35276  *
35277  * Fork - LGPL
35278  * <script type="text/javascript">
35279  */
35280  
35281 /**
35282  * @class Roo.grid.Grid
35283  * @extends Roo.util.Observable
35284  * This class represents the primary interface of a component based grid control.
35285  * <br><br>Usage:<pre><code>
35286  var grid = new Roo.grid.Grid("my-container-id", {
35287      ds: myDataStore,
35288      cm: myColModel,
35289      selModel: mySelectionModel,
35290      autoSizeColumns: true,
35291      monitorWindowResize: false,
35292      trackMouseOver: true
35293  });
35294  // set any options
35295  grid.render();
35296  * </code></pre>
35297  * <b>Common Problems:</b><br/>
35298  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35299  * element will correct this<br/>
35300  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35301  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35302  * are unpredictable.<br/>
35303  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35304  * grid to calculate dimensions/offsets.<br/>
35305   * @constructor
35306  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35307  * The container MUST have some type of size defined for the grid to fill. The container will be
35308  * automatically set to position relative if it isn't already.
35309  * @param {Object} config A config object that sets properties on this grid.
35310  */
35311 Roo.grid.Grid = function(container, config){
35312         // initialize the container
35313         this.container = Roo.get(container);
35314         this.container.update("");
35315         this.container.setStyle("overflow", "hidden");
35316     this.container.addClass('x-grid-container');
35317
35318     this.id = this.container.id;
35319
35320     Roo.apply(this, config);
35321     // check and correct shorthanded configs
35322     if(this.ds){
35323         this.dataSource = this.ds;
35324         delete this.ds;
35325     }
35326     if(this.cm){
35327         this.colModel = this.cm;
35328         delete this.cm;
35329     }
35330     if(this.sm){
35331         this.selModel = this.sm;
35332         delete this.sm;
35333     }
35334
35335     if (this.selModel) {
35336         this.selModel = Roo.factory(this.selModel, Roo.grid);
35337         this.sm = this.selModel;
35338         this.sm.xmodule = this.xmodule || false;
35339     }
35340     if (typeof(this.colModel.config) == 'undefined') {
35341         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35342         this.cm = this.colModel;
35343         this.cm.xmodule = this.xmodule || false;
35344     }
35345     if (this.dataSource) {
35346         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35347         this.ds = this.dataSource;
35348         this.ds.xmodule = this.xmodule || false;
35349          
35350     }
35351     
35352     
35353     
35354     if(this.width){
35355         this.container.setWidth(this.width);
35356     }
35357
35358     if(this.height){
35359         this.container.setHeight(this.height);
35360     }
35361     /** @private */
35362         this.addEvents({
35363         // raw events
35364         /**
35365          * @event click
35366          * The raw click event for the entire grid.
35367          * @param {Roo.EventObject} e
35368          */
35369         "click" : true,
35370         /**
35371          * @event dblclick
35372          * The raw dblclick event for the entire grid.
35373          * @param {Roo.EventObject} e
35374          */
35375         "dblclick" : true,
35376         /**
35377          * @event contextmenu
35378          * The raw contextmenu event for the entire grid.
35379          * @param {Roo.EventObject} e
35380          */
35381         "contextmenu" : true,
35382         /**
35383          * @event mousedown
35384          * The raw mousedown event for the entire grid.
35385          * @param {Roo.EventObject} e
35386          */
35387         "mousedown" : true,
35388         /**
35389          * @event mouseup
35390          * The raw mouseup event for the entire grid.
35391          * @param {Roo.EventObject} e
35392          */
35393         "mouseup" : true,
35394         /**
35395          * @event mouseover
35396          * The raw mouseover event for the entire grid.
35397          * @param {Roo.EventObject} e
35398          */
35399         "mouseover" : true,
35400         /**
35401          * @event mouseout
35402          * The raw mouseout event for the entire grid.
35403          * @param {Roo.EventObject} e
35404          */
35405         "mouseout" : true,
35406         /**
35407          * @event keypress
35408          * The raw keypress event for the entire grid.
35409          * @param {Roo.EventObject} e
35410          */
35411         "keypress" : true,
35412         /**
35413          * @event keydown
35414          * The raw keydown event for the entire grid.
35415          * @param {Roo.EventObject} e
35416          */
35417         "keydown" : true,
35418
35419         // custom events
35420
35421         /**
35422          * @event cellclick
35423          * Fires when a cell is clicked
35424          * @param {Grid} this
35425          * @param {Number} rowIndex
35426          * @param {Number} columnIndex
35427          * @param {Roo.EventObject} e
35428          */
35429         "cellclick" : true,
35430         /**
35431          * @event celldblclick
35432          * Fires when a cell is double clicked
35433          * @param {Grid} this
35434          * @param {Number} rowIndex
35435          * @param {Number} columnIndex
35436          * @param {Roo.EventObject} e
35437          */
35438         "celldblclick" : true,
35439         /**
35440          * @event rowclick
35441          * Fires when a row is clicked
35442          * @param {Grid} this
35443          * @param {Number} rowIndex
35444          * @param {Roo.EventObject} e
35445          */
35446         "rowclick" : true,
35447         /**
35448          * @event rowdblclick
35449          * Fires when a row is double clicked
35450          * @param {Grid} this
35451          * @param {Number} rowIndex
35452          * @param {Roo.EventObject} e
35453          */
35454         "rowdblclick" : true,
35455         /**
35456          * @event headerclick
35457          * Fires when a header is clicked
35458          * @param {Grid} this
35459          * @param {Number} columnIndex
35460          * @param {Roo.EventObject} e
35461          */
35462         "headerclick" : true,
35463         /**
35464          * @event headerdblclick
35465          * Fires when a header cell is double clicked
35466          * @param {Grid} this
35467          * @param {Number} columnIndex
35468          * @param {Roo.EventObject} e
35469          */
35470         "headerdblclick" : true,
35471         /**
35472          * @event rowcontextmenu
35473          * Fires when a row is right clicked
35474          * @param {Grid} this
35475          * @param {Number} rowIndex
35476          * @param {Roo.EventObject} e
35477          */
35478         "rowcontextmenu" : true,
35479         /**
35480          * @event cellcontextmenu
35481          * Fires when a cell is right clicked
35482          * @param {Grid} this
35483          * @param {Number} rowIndex
35484          * @param {Number} cellIndex
35485          * @param {Roo.EventObject} e
35486          */
35487          "cellcontextmenu" : true,
35488         /**
35489          * @event headercontextmenu
35490          * Fires when a header is right clicked
35491          * @param {Grid} this
35492          * @param {Number} columnIndex
35493          * @param {Roo.EventObject} e
35494          */
35495         "headercontextmenu" : true,
35496         /**
35497          * @event bodyscroll
35498          * Fires when the body element is scrolled
35499          * @param {Number} scrollLeft
35500          * @param {Number} scrollTop
35501          */
35502         "bodyscroll" : true,
35503         /**
35504          * @event columnresize
35505          * Fires when the user resizes a column
35506          * @param {Number} columnIndex
35507          * @param {Number} newSize
35508          */
35509         "columnresize" : true,
35510         /**
35511          * @event columnmove
35512          * Fires when the user moves a column
35513          * @param {Number} oldIndex
35514          * @param {Number} newIndex
35515          */
35516         "columnmove" : true,
35517         /**
35518          * @event startdrag
35519          * Fires when row(s) start being dragged
35520          * @param {Grid} this
35521          * @param {Roo.GridDD} dd The drag drop object
35522          * @param {event} e The raw browser event
35523          */
35524         "startdrag" : true,
35525         /**
35526          * @event enddrag
35527          * Fires when a drag operation is complete
35528          * @param {Grid} this
35529          * @param {Roo.GridDD} dd The drag drop object
35530          * @param {event} e The raw browser event
35531          */
35532         "enddrag" : true,
35533         /**
35534          * @event dragdrop
35535          * Fires when dragged row(s) are dropped on a valid DD target
35536          * @param {Grid} this
35537          * @param {Roo.GridDD} dd The drag drop object
35538          * @param {String} targetId The target drag drop object
35539          * @param {event} e The raw browser event
35540          */
35541         "dragdrop" : true,
35542         /**
35543          * @event dragover
35544          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35545          * @param {Grid} this
35546          * @param {Roo.GridDD} dd The drag drop object
35547          * @param {String} targetId The target drag drop object
35548          * @param {event} e The raw browser event
35549          */
35550         "dragover" : true,
35551         /**
35552          * @event dragenter
35553          *  Fires when the dragged row(s) first cross another DD target while being dragged
35554          * @param {Grid} this
35555          * @param {Roo.GridDD} dd The drag drop object
35556          * @param {String} targetId The target drag drop object
35557          * @param {event} e The raw browser event
35558          */
35559         "dragenter" : true,
35560         /**
35561          * @event dragout
35562          * Fires when the dragged row(s) leave another DD target while being dragged
35563          * @param {Grid} this
35564          * @param {Roo.GridDD} dd The drag drop object
35565          * @param {String} targetId The target drag drop object
35566          * @param {event} e The raw browser event
35567          */
35568         "dragout" : true,
35569         /**
35570          * @event rowclass
35571          * Fires when a row is rendered, so you can change add a style to it.
35572          * @param {GridView} gridview   The grid view
35573          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35574          */
35575         'rowclass' : true,
35576
35577         /**
35578          * @event render
35579          * Fires when the grid is rendered
35580          * @param {Grid} grid
35581          */
35582         'render' : true
35583     });
35584
35585     Roo.grid.Grid.superclass.constructor.call(this);
35586 };
35587 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35588     
35589     /**
35590          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
35591          */
35592         /**
35593          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
35594          */
35595         /**
35596          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
35597          */
35598         /**
35599          * @cfg {Roo.grid.Store} ds The data store for the grid
35600          */
35601         /**
35602          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
35603          */
35604         /**
35605      * @cfg {String} ddGroup - drag drop group.
35606      */
35607       /**
35608      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
35609      */
35610
35611     /**
35612      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35613      */
35614     minColumnWidth : 25,
35615
35616     /**
35617      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35618      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35619      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35620      */
35621     autoSizeColumns : false,
35622
35623     /**
35624      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35625      */
35626     autoSizeHeaders : true,
35627
35628     /**
35629      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35630      */
35631     monitorWindowResize : true,
35632
35633     /**
35634      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35635      * rows measured to get a columns size. Default is 0 (all rows).
35636      */
35637     maxRowsToMeasure : 0,
35638
35639     /**
35640      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35641      */
35642     trackMouseOver : true,
35643
35644     /**
35645     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35646     */
35647       /**
35648     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
35649     */
35650     
35651     /**
35652     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35653     */
35654     enableDragDrop : false,
35655     
35656     /**
35657     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35658     */
35659     enableColumnMove : true,
35660     
35661     /**
35662     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35663     */
35664     enableColumnHide : true,
35665     
35666     /**
35667     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35668     */
35669     enableRowHeightSync : false,
35670     
35671     /**
35672     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35673     */
35674     stripeRows : true,
35675     
35676     /**
35677     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35678     */
35679     autoHeight : false,
35680
35681     /**
35682      * @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.
35683      */
35684     autoExpandColumn : false,
35685
35686     /**
35687     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35688     * Default is 50.
35689     */
35690     autoExpandMin : 50,
35691
35692     /**
35693     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35694     */
35695     autoExpandMax : 1000,
35696
35697     /**
35698     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35699     */
35700     view : null,
35701
35702     /**
35703     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35704     */
35705     loadMask : false,
35706     /**
35707     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35708     */
35709     dropTarget: false,
35710      /**
35711     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
35712     */ 
35713     sortColMenu : false,
35714     
35715     // private
35716     rendered : false,
35717
35718     /**
35719     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35720     * of a fixed width. Default is false.
35721     */
35722     /**
35723     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35724     */
35725     
35726     
35727     /**
35728     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
35729     * %0 is replaced with the number of selected rows.
35730     */
35731     ddText : "{0} selected row{1}",
35732     
35733     
35734     /**
35735      * Called once after all setup has been completed and the grid is ready to be rendered.
35736      * @return {Roo.grid.Grid} this
35737      */
35738     render : function()
35739     {
35740         var c = this.container;
35741         // try to detect autoHeight/width mode
35742         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35743             this.autoHeight = true;
35744         }
35745         var view = this.getView();
35746         view.init(this);
35747
35748         c.on("click", this.onClick, this);
35749         c.on("dblclick", this.onDblClick, this);
35750         c.on("contextmenu", this.onContextMenu, this);
35751         c.on("keydown", this.onKeyDown, this);
35752         if (Roo.isTouch) {
35753             c.on("touchstart", this.onTouchStart, this);
35754         }
35755
35756         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35757
35758         this.getSelectionModel().init(this);
35759
35760         view.render();
35761
35762         if(this.loadMask){
35763             this.loadMask = new Roo.LoadMask(this.container,
35764                     Roo.apply({store:this.dataSource}, this.loadMask));
35765         }
35766         
35767         
35768         if (this.toolbar && this.toolbar.xtype) {
35769             this.toolbar.container = this.getView().getHeaderPanel(true);
35770             this.toolbar = new Roo.Toolbar(this.toolbar);
35771         }
35772         if (this.footer && this.footer.xtype) {
35773             this.footer.dataSource = this.getDataSource();
35774             this.footer.container = this.getView().getFooterPanel(true);
35775             this.footer = Roo.factory(this.footer, Roo);
35776         }
35777         if (this.dropTarget && this.dropTarget.xtype) {
35778             delete this.dropTarget.xtype;
35779             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35780         }
35781         
35782         
35783         this.rendered = true;
35784         this.fireEvent('render', this);
35785         return this;
35786     },
35787
35788     /**
35789      * Reconfigures the grid to use a different Store and Column Model.
35790      * The View will be bound to the new objects and refreshed.
35791      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35792      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35793      */
35794     reconfigure : function(dataSource, colModel){
35795         if(this.loadMask){
35796             this.loadMask.destroy();
35797             this.loadMask = new Roo.LoadMask(this.container,
35798                     Roo.apply({store:dataSource}, this.loadMask));
35799         }
35800         this.view.bind(dataSource, colModel);
35801         this.dataSource = dataSource;
35802         this.colModel = colModel;
35803         this.view.refresh(true);
35804     },
35805     /**
35806      * addColumns
35807      * Add's a column, default at the end..
35808      
35809      * @param {int} position to add (default end)
35810      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
35811      */
35812     addColumns : function(pos, ar)
35813     {
35814         
35815         for (var i =0;i< ar.length;i++) {
35816             var cfg = ar[i];
35817             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
35818             this.cm.lookup[cfg.id] = cfg;
35819         }
35820         
35821         
35822         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
35823             pos = this.cm.config.length; //this.cm.config.push(cfg);
35824         } 
35825         pos = Math.max(0,pos);
35826         ar.unshift(0);
35827         ar.unshift(pos);
35828         this.cm.config.splice.apply(this.cm.config, ar);
35829         
35830         
35831         
35832         this.view.generateRules(this.cm);
35833         this.view.refresh(true);
35834         
35835     },
35836     
35837     
35838     
35839     
35840     // private
35841     onKeyDown : function(e){
35842         this.fireEvent("keydown", e);
35843     },
35844
35845     /**
35846      * Destroy this grid.
35847      * @param {Boolean} removeEl True to remove the element
35848      */
35849     destroy : function(removeEl, keepListeners){
35850         if(this.loadMask){
35851             this.loadMask.destroy();
35852         }
35853         var c = this.container;
35854         c.removeAllListeners();
35855         this.view.destroy();
35856         this.colModel.purgeListeners();
35857         if(!keepListeners){
35858             this.purgeListeners();
35859         }
35860         c.update("");
35861         if(removeEl === true){
35862             c.remove();
35863         }
35864     },
35865
35866     // private
35867     processEvent : function(name, e){
35868         // does this fire select???
35869         //Roo.log('grid:processEvent '  + name);
35870         
35871         if (name != 'touchstart' ) {
35872             this.fireEvent(name, e);    
35873         }
35874         
35875         var t = e.getTarget();
35876         var v = this.view;
35877         var header = v.findHeaderIndex(t);
35878         if(header !== false){
35879             var ename = name == 'touchstart' ? 'click' : name;
35880              
35881             this.fireEvent("header" + ename, this, header, e);
35882         }else{
35883             var row = v.findRowIndex(t);
35884             var cell = v.findCellIndex(t);
35885             if (name == 'touchstart') {
35886                 // first touch is always a click.
35887                 // hopefull this happens after selection is updated.?
35888                 name = false;
35889                 
35890                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35891                     var cs = this.selModel.getSelectedCell();
35892                     if (row == cs[0] && cell == cs[1]){
35893                         name = 'dblclick';
35894                     }
35895                 }
35896                 if (typeof(this.selModel.getSelections) != 'undefined') {
35897                     var cs = this.selModel.getSelections();
35898                     var ds = this.dataSource;
35899                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35900                         name = 'dblclick';
35901                     }
35902                 }
35903                 if (!name) {
35904                     return;
35905                 }
35906             }
35907             
35908             
35909             if(row !== false){
35910                 this.fireEvent("row" + name, this, row, e);
35911                 if(cell !== false){
35912                     this.fireEvent("cell" + name, this, row, cell, e);
35913                 }
35914             }
35915         }
35916     },
35917
35918     // private
35919     onClick : function(e){
35920         this.processEvent("click", e);
35921     },
35922    // private
35923     onTouchStart : function(e){
35924         this.processEvent("touchstart", e);
35925     },
35926
35927     // private
35928     onContextMenu : function(e, t){
35929         this.processEvent("contextmenu", e);
35930     },
35931
35932     // private
35933     onDblClick : function(e){
35934         this.processEvent("dblclick", e);
35935     },
35936
35937     // private
35938     walkCells : function(row, col, step, fn, scope){
35939         var cm = this.colModel, clen = cm.getColumnCount();
35940         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35941         if(step < 0){
35942             if(col < 0){
35943                 row--;
35944                 first = false;
35945             }
35946             while(row >= 0){
35947                 if(!first){
35948                     col = clen-1;
35949                 }
35950                 first = false;
35951                 while(col >= 0){
35952                     if(fn.call(scope || this, row, col, cm) === true){
35953                         return [row, col];
35954                     }
35955                     col--;
35956                 }
35957                 row--;
35958             }
35959         } else {
35960             if(col >= clen){
35961                 row++;
35962                 first = false;
35963             }
35964             while(row < rlen){
35965                 if(!first){
35966                     col = 0;
35967                 }
35968                 first = false;
35969                 while(col < clen){
35970                     if(fn.call(scope || this, row, col, cm) === true){
35971                         return [row, col];
35972                     }
35973                     col++;
35974                 }
35975                 row++;
35976             }
35977         }
35978         return null;
35979     },
35980
35981     // private
35982     getSelections : function(){
35983         return this.selModel.getSelections();
35984     },
35985
35986     /**
35987      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35988      * but if manual update is required this method will initiate it.
35989      */
35990     autoSize : function(){
35991         if(this.rendered){
35992             this.view.layout();
35993             if(this.view.adjustForScroll){
35994                 this.view.adjustForScroll();
35995             }
35996         }
35997     },
35998
35999     /**
36000      * Returns the grid's underlying element.
36001      * @return {Element} The element
36002      */
36003     getGridEl : function(){
36004         return this.container;
36005     },
36006
36007     // private for compatibility, overridden by editor grid
36008     stopEditing : function(){},
36009
36010     /**
36011      * Returns the grid's SelectionModel.
36012      * @return {SelectionModel}
36013      */
36014     getSelectionModel : function(){
36015         if(!this.selModel){
36016             this.selModel = new Roo.grid.RowSelectionModel();
36017         }
36018         return this.selModel;
36019     },
36020
36021     /**
36022      * Returns the grid's DataSource.
36023      * @return {DataSource}
36024      */
36025     getDataSource : function(){
36026         return this.dataSource;
36027     },
36028
36029     /**
36030      * Returns the grid's ColumnModel.
36031      * @return {ColumnModel}
36032      */
36033     getColumnModel : function(){
36034         return this.colModel;
36035     },
36036
36037     /**
36038      * Returns the grid's GridView object.
36039      * @return {GridView}
36040      */
36041     getView : function(){
36042         if(!this.view){
36043             this.view = new Roo.grid.GridView(this.viewConfig);
36044             this.relayEvents(this.view, [
36045                 "beforerowremoved", "beforerowsinserted",
36046                 "beforerefresh", "rowremoved",
36047                 "rowsinserted", "rowupdated" ,"refresh"
36048             ]);
36049         }
36050         return this.view;
36051     },
36052     /**
36053      * Called to get grid's drag proxy text, by default returns this.ddText.
36054      * Override this to put something different in the dragged text.
36055      * @return {String}
36056      */
36057     getDragDropText : function(){
36058         var count = this.selModel.getCount();
36059         return String.format(this.ddText, count, count == 1 ? '' : 's');
36060     }
36061 });
36062 /*
36063  * Based on:
36064  * Ext JS Library 1.1.1
36065  * Copyright(c) 2006-2007, Ext JS, LLC.
36066  *
36067  * Originally Released Under LGPL - original licence link has changed is not relivant.
36068  *
36069  * Fork - LGPL
36070  * <script type="text/javascript">
36071  */
36072  /**
36073  * @class Roo.grid.AbstractGridView
36074  * @extends Roo.util.Observable
36075  * @abstract
36076  * Abstract base class for grid Views
36077  * @constructor
36078  */
36079 Roo.grid.AbstractGridView = function(){
36080         this.grid = null;
36081         
36082         this.events = {
36083             "beforerowremoved" : true,
36084             "beforerowsinserted" : true,
36085             "beforerefresh" : true,
36086             "rowremoved" : true,
36087             "rowsinserted" : true,
36088             "rowupdated" : true,
36089             "refresh" : true
36090         };
36091     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36092 };
36093
36094 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36095     rowClass : "x-grid-row",
36096     cellClass : "x-grid-cell",
36097     tdClass : "x-grid-td",
36098     hdClass : "x-grid-hd",
36099     splitClass : "x-grid-hd-split",
36100     
36101     init: function(grid){
36102         this.grid = grid;
36103                 var cid = this.grid.getGridEl().id;
36104         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36105         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36106         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36107         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36108         },
36109         
36110     getColumnRenderers : function(){
36111         var renderers = [];
36112         var cm = this.grid.colModel;
36113         var colCount = cm.getColumnCount();
36114         for(var i = 0; i < colCount; i++){
36115             renderers[i] = cm.getRenderer(i);
36116         }
36117         return renderers;
36118     },
36119     
36120     getColumnIds : function(){
36121         var ids = [];
36122         var cm = this.grid.colModel;
36123         var colCount = cm.getColumnCount();
36124         for(var i = 0; i < colCount; i++){
36125             ids[i] = cm.getColumnId(i);
36126         }
36127         return ids;
36128     },
36129     
36130     getDataIndexes : function(){
36131         if(!this.indexMap){
36132             this.indexMap = this.buildIndexMap();
36133         }
36134         return this.indexMap.colToData;
36135     },
36136     
36137     getColumnIndexByDataIndex : function(dataIndex){
36138         if(!this.indexMap){
36139             this.indexMap = this.buildIndexMap();
36140         }
36141         return this.indexMap.dataToCol[dataIndex];
36142     },
36143     
36144     /**
36145      * Set a css style for a column dynamically. 
36146      * @param {Number} colIndex The index of the column
36147      * @param {String} name The css property name
36148      * @param {String} value The css value
36149      */
36150     setCSSStyle : function(colIndex, name, value){
36151         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36152         Roo.util.CSS.updateRule(selector, name, value);
36153     },
36154     
36155     generateRules : function(cm){
36156         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36157         Roo.util.CSS.removeStyleSheet(rulesId);
36158         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36159             var cid = cm.getColumnId(i);
36160             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36161                          this.tdSelector, cid, " {\n}\n",
36162                          this.hdSelector, cid, " {\n}\n",
36163                          this.splitSelector, cid, " {\n}\n");
36164         }
36165         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36166     }
36167 });/*
36168  * Based on:
36169  * Ext JS Library 1.1.1
36170  * Copyright(c) 2006-2007, Ext JS, LLC.
36171  *
36172  * Originally Released Under LGPL - original licence link has changed is not relivant.
36173  *
36174  * Fork - LGPL
36175  * <script type="text/javascript">
36176  */
36177
36178 // private
36179 // This is a support class used internally by the Grid components
36180 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36181     this.grid = grid;
36182     this.view = grid.getView();
36183     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36184     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36185     if(hd2){
36186         this.setHandleElId(Roo.id(hd));
36187         this.setOuterHandleElId(Roo.id(hd2));
36188     }
36189     this.scroll = false;
36190 };
36191 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36192     maxDragWidth: 120,
36193     getDragData : function(e){
36194         var t = Roo.lib.Event.getTarget(e);
36195         var h = this.view.findHeaderCell(t);
36196         if(h){
36197             return {ddel: h.firstChild, header:h};
36198         }
36199         return false;
36200     },
36201
36202     onInitDrag : function(e){
36203         this.view.headersDisabled = true;
36204         var clone = this.dragData.ddel.cloneNode(true);
36205         clone.id = Roo.id();
36206         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36207         this.proxy.update(clone);
36208         return true;
36209     },
36210
36211     afterValidDrop : function(){
36212         var v = this.view;
36213         setTimeout(function(){
36214             v.headersDisabled = false;
36215         }, 50);
36216     },
36217
36218     afterInvalidDrop : function(){
36219         var v = this.view;
36220         setTimeout(function(){
36221             v.headersDisabled = false;
36222         }, 50);
36223     }
36224 });
36225 /*
36226  * Based on:
36227  * Ext JS Library 1.1.1
36228  * Copyright(c) 2006-2007, Ext JS, LLC.
36229  *
36230  * Originally Released Under LGPL - original licence link has changed is not relivant.
36231  *
36232  * Fork - LGPL
36233  * <script type="text/javascript">
36234  */
36235 // private
36236 // This is a support class used internally by the Grid components
36237 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36238     this.grid = grid;
36239     this.view = grid.getView();
36240     // split the proxies so they don't interfere with mouse events
36241     this.proxyTop = Roo.DomHelper.append(document.body, {
36242         cls:"col-move-top", html:"&#160;"
36243     }, true);
36244     this.proxyBottom = Roo.DomHelper.append(document.body, {
36245         cls:"col-move-bottom", html:"&#160;"
36246     }, true);
36247     this.proxyTop.hide = this.proxyBottom.hide = function(){
36248         this.setLeftTop(-100,-100);
36249         this.setStyle("visibility", "hidden");
36250     };
36251     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36252     // temporarily disabled
36253     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36254     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36255 };
36256 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36257     proxyOffsets : [-4, -9],
36258     fly: Roo.Element.fly,
36259
36260     getTargetFromEvent : function(e){
36261         var t = Roo.lib.Event.getTarget(e);
36262         var cindex = this.view.findCellIndex(t);
36263         if(cindex !== false){
36264             return this.view.getHeaderCell(cindex);
36265         }
36266         return null;
36267     },
36268
36269     nextVisible : function(h){
36270         var v = this.view, cm = this.grid.colModel;
36271         h = h.nextSibling;
36272         while(h){
36273             if(!cm.isHidden(v.getCellIndex(h))){
36274                 return h;
36275             }
36276             h = h.nextSibling;
36277         }
36278         return null;
36279     },
36280
36281     prevVisible : function(h){
36282         var v = this.view, cm = this.grid.colModel;
36283         h = h.prevSibling;
36284         while(h){
36285             if(!cm.isHidden(v.getCellIndex(h))){
36286                 return h;
36287             }
36288             h = h.prevSibling;
36289         }
36290         return null;
36291     },
36292
36293     positionIndicator : function(h, n, e){
36294         var x = Roo.lib.Event.getPageX(e);
36295         var r = Roo.lib.Dom.getRegion(n.firstChild);
36296         var px, pt, py = r.top + this.proxyOffsets[1];
36297         if((r.right - x) <= (r.right-r.left)/2){
36298             px = r.right+this.view.borderWidth;
36299             pt = "after";
36300         }else{
36301             px = r.left;
36302             pt = "before";
36303         }
36304         var oldIndex = this.view.getCellIndex(h);
36305         var newIndex = this.view.getCellIndex(n);
36306
36307         if(this.grid.colModel.isFixed(newIndex)){
36308             return false;
36309         }
36310
36311         var locked = this.grid.colModel.isLocked(newIndex);
36312
36313         if(pt == "after"){
36314             newIndex++;
36315         }
36316         if(oldIndex < newIndex){
36317             newIndex--;
36318         }
36319         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36320             return false;
36321         }
36322         px +=  this.proxyOffsets[0];
36323         this.proxyTop.setLeftTop(px, py);
36324         this.proxyTop.show();
36325         if(!this.bottomOffset){
36326             this.bottomOffset = this.view.mainHd.getHeight();
36327         }
36328         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36329         this.proxyBottom.show();
36330         return pt;
36331     },
36332
36333     onNodeEnter : function(n, dd, e, data){
36334         if(data.header != n){
36335             this.positionIndicator(data.header, n, e);
36336         }
36337     },
36338
36339     onNodeOver : function(n, dd, e, data){
36340         var result = false;
36341         if(data.header != n){
36342             result = this.positionIndicator(data.header, n, e);
36343         }
36344         if(!result){
36345             this.proxyTop.hide();
36346             this.proxyBottom.hide();
36347         }
36348         return result ? this.dropAllowed : this.dropNotAllowed;
36349     },
36350
36351     onNodeOut : function(n, dd, e, data){
36352         this.proxyTop.hide();
36353         this.proxyBottom.hide();
36354     },
36355
36356     onNodeDrop : function(n, dd, e, data){
36357         var h = data.header;
36358         if(h != n){
36359             var cm = this.grid.colModel;
36360             var x = Roo.lib.Event.getPageX(e);
36361             var r = Roo.lib.Dom.getRegion(n.firstChild);
36362             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36363             var oldIndex = this.view.getCellIndex(h);
36364             var newIndex = this.view.getCellIndex(n);
36365             var locked = cm.isLocked(newIndex);
36366             if(pt == "after"){
36367                 newIndex++;
36368             }
36369             if(oldIndex < newIndex){
36370                 newIndex--;
36371             }
36372             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36373                 return false;
36374             }
36375             cm.setLocked(oldIndex, locked, true);
36376             cm.moveColumn(oldIndex, newIndex);
36377             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36378             return true;
36379         }
36380         return false;
36381     }
36382 });
36383 /*
36384  * Based on:
36385  * Ext JS Library 1.1.1
36386  * Copyright(c) 2006-2007, Ext JS, LLC.
36387  *
36388  * Originally Released Under LGPL - original licence link has changed is not relivant.
36389  *
36390  * Fork - LGPL
36391  * <script type="text/javascript">
36392  */
36393   
36394 /**
36395  * @class Roo.grid.GridView
36396  * @extends Roo.util.Observable
36397  *
36398  * @constructor
36399  * @param {Object} config
36400  */
36401 Roo.grid.GridView = function(config){
36402     Roo.grid.GridView.superclass.constructor.call(this);
36403     this.el = null;
36404
36405     Roo.apply(this, config);
36406 };
36407
36408 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36409
36410     unselectable :  'unselectable="on"',
36411     unselectableCls :  'x-unselectable',
36412     
36413     
36414     rowClass : "x-grid-row",
36415
36416     cellClass : "x-grid-col",
36417
36418     tdClass : "x-grid-td",
36419
36420     hdClass : "x-grid-hd",
36421
36422     splitClass : "x-grid-split",
36423
36424     sortClasses : ["sort-asc", "sort-desc"],
36425
36426     enableMoveAnim : false,
36427
36428     hlColor: "C3DAF9",
36429
36430     dh : Roo.DomHelper,
36431
36432     fly : Roo.Element.fly,
36433
36434     css : Roo.util.CSS,
36435
36436     borderWidth: 1,
36437
36438     splitOffset: 3,
36439
36440     scrollIncrement : 22,
36441
36442     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36443
36444     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36445
36446     bind : function(ds, cm){
36447         if(this.ds){
36448             this.ds.un("load", this.onLoad, this);
36449             this.ds.un("datachanged", this.onDataChange, this);
36450             this.ds.un("add", this.onAdd, this);
36451             this.ds.un("remove", this.onRemove, this);
36452             this.ds.un("update", this.onUpdate, this);
36453             this.ds.un("clear", this.onClear, this);
36454         }
36455         if(ds){
36456             ds.on("load", this.onLoad, this);
36457             ds.on("datachanged", this.onDataChange, this);
36458             ds.on("add", this.onAdd, this);
36459             ds.on("remove", this.onRemove, this);
36460             ds.on("update", this.onUpdate, this);
36461             ds.on("clear", this.onClear, this);
36462         }
36463         this.ds = ds;
36464
36465         if(this.cm){
36466             this.cm.un("widthchange", this.onColWidthChange, this);
36467             this.cm.un("headerchange", this.onHeaderChange, this);
36468             this.cm.un("hiddenchange", this.onHiddenChange, this);
36469             this.cm.un("columnmoved", this.onColumnMove, this);
36470             this.cm.un("columnlockchange", this.onColumnLock, this);
36471         }
36472         if(cm){
36473             this.generateRules(cm);
36474             cm.on("widthchange", this.onColWidthChange, this);
36475             cm.on("headerchange", this.onHeaderChange, this);
36476             cm.on("hiddenchange", this.onHiddenChange, this);
36477             cm.on("columnmoved", this.onColumnMove, this);
36478             cm.on("columnlockchange", this.onColumnLock, this);
36479         }
36480         this.cm = cm;
36481     },
36482
36483     init: function(grid){
36484         Roo.grid.GridView.superclass.init.call(this, grid);
36485
36486         this.bind(grid.dataSource, grid.colModel);
36487
36488         grid.on("headerclick", this.handleHeaderClick, this);
36489
36490         if(grid.trackMouseOver){
36491             grid.on("mouseover", this.onRowOver, this);
36492             grid.on("mouseout", this.onRowOut, this);
36493         }
36494         grid.cancelTextSelection = function(){};
36495         this.gridId = grid.id;
36496
36497         var tpls = this.templates || {};
36498
36499         if(!tpls.master){
36500             tpls.master = new Roo.Template(
36501                '<div class="x-grid" hidefocus="true">',
36502                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36503                   '<div class="x-grid-topbar"></div>',
36504                   '<div class="x-grid-scroller"><div></div></div>',
36505                   '<div class="x-grid-locked">',
36506                       '<div class="x-grid-header">{lockedHeader}</div>',
36507                       '<div class="x-grid-body">{lockedBody}</div>',
36508                   "</div>",
36509                   '<div class="x-grid-viewport">',
36510                       '<div class="x-grid-header">{header}</div>',
36511                       '<div class="x-grid-body">{body}</div>',
36512                   "</div>",
36513                   '<div class="x-grid-bottombar"></div>',
36514                  
36515                   '<div class="x-grid-resize-proxy">&#160;</div>',
36516                "</div>"
36517             );
36518             tpls.master.disableformats = true;
36519         }
36520
36521         if(!tpls.header){
36522             tpls.header = new Roo.Template(
36523                '<table border="0" cellspacing="0" cellpadding="0">',
36524                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36525                "</table>{splits}"
36526             );
36527             tpls.header.disableformats = true;
36528         }
36529         tpls.header.compile();
36530
36531         if(!tpls.hcell){
36532             tpls.hcell = new Roo.Template(
36533                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36534                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36535                 "</div></td>"
36536              );
36537              tpls.hcell.disableFormats = true;
36538         }
36539         tpls.hcell.compile();
36540
36541         if(!tpls.hsplit){
36542             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36543                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36544             tpls.hsplit.disableFormats = true;
36545         }
36546         tpls.hsplit.compile();
36547
36548         if(!tpls.body){
36549             tpls.body = new Roo.Template(
36550                '<table border="0" cellspacing="0" cellpadding="0">',
36551                "<tbody>{rows}</tbody>",
36552                "</table>"
36553             );
36554             tpls.body.disableFormats = true;
36555         }
36556         tpls.body.compile();
36557
36558         if(!tpls.row){
36559             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36560             tpls.row.disableFormats = true;
36561         }
36562         tpls.row.compile();
36563
36564         if(!tpls.cell){
36565             tpls.cell = new Roo.Template(
36566                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36567                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36568                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36569                 "</td>"
36570             );
36571             tpls.cell.disableFormats = true;
36572         }
36573         tpls.cell.compile();
36574
36575         this.templates = tpls;
36576     },
36577
36578     // remap these for backwards compat
36579     onColWidthChange : function(){
36580         this.updateColumns.apply(this, arguments);
36581     },
36582     onHeaderChange : function(){
36583         this.updateHeaders.apply(this, arguments);
36584     }, 
36585     onHiddenChange : function(){
36586         this.handleHiddenChange.apply(this, arguments);
36587     },
36588     onColumnMove : function(){
36589         this.handleColumnMove.apply(this, arguments);
36590     },
36591     onColumnLock : function(){
36592         this.handleLockChange.apply(this, arguments);
36593     },
36594
36595     onDataChange : function(){
36596         this.refresh();
36597         this.updateHeaderSortState();
36598     },
36599
36600     onClear : function(){
36601         this.refresh();
36602     },
36603
36604     onUpdate : function(ds, record){
36605         this.refreshRow(record);
36606     },
36607
36608     refreshRow : function(record){
36609         var ds = this.ds, index;
36610         if(typeof record == 'number'){
36611             index = record;
36612             record = ds.getAt(index);
36613         }else{
36614             index = ds.indexOf(record);
36615         }
36616         this.insertRows(ds, index, index, true);
36617         this.onRemove(ds, record, index+1, true);
36618         this.syncRowHeights(index, index);
36619         this.layout();
36620         this.fireEvent("rowupdated", this, index, record);
36621     },
36622
36623     onAdd : function(ds, records, index){
36624         this.insertRows(ds, index, index + (records.length-1));
36625     },
36626
36627     onRemove : function(ds, record, index, isUpdate){
36628         if(isUpdate !== true){
36629             this.fireEvent("beforerowremoved", this, index, record);
36630         }
36631         var bt = this.getBodyTable(), lt = this.getLockedTable();
36632         if(bt.rows[index]){
36633             bt.firstChild.removeChild(bt.rows[index]);
36634         }
36635         if(lt.rows[index]){
36636             lt.firstChild.removeChild(lt.rows[index]);
36637         }
36638         if(isUpdate !== true){
36639             this.stripeRows(index);
36640             this.syncRowHeights(index, index);
36641             this.layout();
36642             this.fireEvent("rowremoved", this, index, record);
36643         }
36644     },
36645
36646     onLoad : function(){
36647         this.scrollToTop();
36648     },
36649
36650     /**
36651      * Scrolls the grid to the top
36652      */
36653     scrollToTop : function(){
36654         if(this.scroller){
36655             this.scroller.dom.scrollTop = 0;
36656             this.syncScroll();
36657         }
36658     },
36659
36660     /**
36661      * Gets a panel in the header of the grid that can be used for toolbars etc.
36662      * After modifying the contents of this panel a call to grid.autoSize() may be
36663      * required to register any changes in size.
36664      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36665      * @return Roo.Element
36666      */
36667     getHeaderPanel : function(doShow){
36668         if(doShow){
36669             this.headerPanel.show();
36670         }
36671         return this.headerPanel;
36672     },
36673
36674     /**
36675      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36676      * After modifying the contents of this panel a call to grid.autoSize() may be
36677      * required to register any changes in size.
36678      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36679      * @return Roo.Element
36680      */
36681     getFooterPanel : function(doShow){
36682         if(doShow){
36683             this.footerPanel.show();
36684         }
36685         return this.footerPanel;
36686     },
36687
36688     initElements : function(){
36689         var E = Roo.Element;
36690         var el = this.grid.getGridEl().dom.firstChild;
36691         var cs = el.childNodes;
36692
36693         this.el = new E(el);
36694         
36695          this.focusEl = new E(el.firstChild);
36696         this.focusEl.swallowEvent("click", true);
36697         
36698         this.headerPanel = new E(cs[1]);
36699         this.headerPanel.enableDisplayMode("block");
36700
36701         this.scroller = new E(cs[2]);
36702         this.scrollSizer = new E(this.scroller.dom.firstChild);
36703
36704         this.lockedWrap = new E(cs[3]);
36705         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36706         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36707
36708         this.mainWrap = new E(cs[4]);
36709         this.mainHd = new E(this.mainWrap.dom.firstChild);
36710         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36711
36712         this.footerPanel = new E(cs[5]);
36713         this.footerPanel.enableDisplayMode("block");
36714
36715         this.resizeProxy = new E(cs[6]);
36716
36717         this.headerSelector = String.format(
36718            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36719            this.lockedHd.id, this.mainHd.id
36720         );
36721
36722         this.splitterSelector = String.format(
36723            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36724            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36725         );
36726     },
36727     idToCssName : function(s)
36728     {
36729         return s.replace(/[^a-z0-9]+/ig, '-');
36730     },
36731
36732     getHeaderCell : function(index){
36733         return Roo.DomQuery.select(this.headerSelector)[index];
36734     },
36735
36736     getHeaderCellMeasure : function(index){
36737         return this.getHeaderCell(index).firstChild;
36738     },
36739
36740     getHeaderCellText : function(index){
36741         return this.getHeaderCell(index).firstChild.firstChild;
36742     },
36743
36744     getLockedTable : function(){
36745         return this.lockedBody.dom.firstChild;
36746     },
36747
36748     getBodyTable : function(){
36749         return this.mainBody.dom.firstChild;
36750     },
36751
36752     getLockedRow : function(index){
36753         return this.getLockedTable().rows[index];
36754     },
36755
36756     getRow : function(index){
36757         return this.getBodyTable().rows[index];
36758     },
36759
36760     getRowComposite : function(index){
36761         if(!this.rowEl){
36762             this.rowEl = new Roo.CompositeElementLite();
36763         }
36764         var els = [], lrow, mrow;
36765         if(lrow = this.getLockedRow(index)){
36766             els.push(lrow);
36767         }
36768         if(mrow = this.getRow(index)){
36769             els.push(mrow);
36770         }
36771         this.rowEl.elements = els;
36772         return this.rowEl;
36773     },
36774     /**
36775      * Gets the 'td' of the cell
36776      * 
36777      * @param {Integer} rowIndex row to select
36778      * @param {Integer} colIndex column to select
36779      * 
36780      * @return {Object} 
36781      */
36782     getCell : function(rowIndex, colIndex){
36783         var locked = this.cm.getLockedCount();
36784         var source;
36785         if(colIndex < locked){
36786             source = this.lockedBody.dom.firstChild;
36787         }else{
36788             source = this.mainBody.dom.firstChild;
36789             colIndex -= locked;
36790         }
36791         return source.rows[rowIndex].childNodes[colIndex];
36792     },
36793
36794     getCellText : function(rowIndex, colIndex){
36795         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36796     },
36797
36798     getCellBox : function(cell){
36799         var b = this.fly(cell).getBox();
36800         if(Roo.isOpera){ // opera fails to report the Y
36801             b.y = cell.offsetTop + this.mainBody.getY();
36802         }
36803         return b;
36804     },
36805
36806     getCellIndex : function(cell){
36807         var id = String(cell.className).match(this.cellRE);
36808         if(id){
36809             return parseInt(id[1], 10);
36810         }
36811         return 0;
36812     },
36813
36814     findHeaderIndex : function(n){
36815         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36816         return r ? this.getCellIndex(r) : false;
36817     },
36818
36819     findHeaderCell : function(n){
36820         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36821         return r ? r : false;
36822     },
36823
36824     findRowIndex : function(n){
36825         if(!n){
36826             return false;
36827         }
36828         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36829         return r ? r.rowIndex : false;
36830     },
36831
36832     findCellIndex : function(node){
36833         var stop = this.el.dom;
36834         while(node && node != stop){
36835             if(this.findRE.test(node.className)){
36836                 return this.getCellIndex(node);
36837             }
36838             node = node.parentNode;
36839         }
36840         return false;
36841     },
36842
36843     getColumnId : function(index){
36844         return this.cm.getColumnId(index);
36845     },
36846
36847     getSplitters : function()
36848     {
36849         if(this.splitterSelector){
36850            return Roo.DomQuery.select(this.splitterSelector);
36851         }else{
36852             return null;
36853       }
36854     },
36855
36856     getSplitter : function(index){
36857         return this.getSplitters()[index];
36858     },
36859
36860     onRowOver : function(e, t){
36861         var row;
36862         if((row = this.findRowIndex(t)) !== false){
36863             this.getRowComposite(row).addClass("x-grid-row-over");
36864         }
36865     },
36866
36867     onRowOut : function(e, t){
36868         var row;
36869         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36870             this.getRowComposite(row).removeClass("x-grid-row-over");
36871         }
36872     },
36873
36874     renderHeaders : function(){
36875         var cm = this.cm;
36876         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36877         var cb = [], lb = [], sb = [], lsb = [], p = {};
36878         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36879             p.cellId = "x-grid-hd-0-" + i;
36880             p.splitId = "x-grid-csplit-0-" + i;
36881             p.id = cm.getColumnId(i);
36882             p.value = cm.getColumnHeader(i) || "";
36883             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
36884             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36885             if(!cm.isLocked(i)){
36886                 cb[cb.length] = ct.apply(p);
36887                 sb[sb.length] = st.apply(p);
36888             }else{
36889                 lb[lb.length] = ct.apply(p);
36890                 lsb[lsb.length] = st.apply(p);
36891             }
36892         }
36893         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36894                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36895     },
36896
36897     updateHeaders : function(){
36898         var html = this.renderHeaders();
36899         this.lockedHd.update(html[0]);
36900         this.mainHd.update(html[1]);
36901     },
36902
36903     /**
36904      * Focuses the specified row.
36905      * @param {Number} row The row index
36906      */
36907     focusRow : function(row)
36908     {
36909         //Roo.log('GridView.focusRow');
36910         var x = this.scroller.dom.scrollLeft;
36911         this.focusCell(row, 0, false);
36912         this.scroller.dom.scrollLeft = x;
36913     },
36914
36915     /**
36916      * Focuses the specified cell.
36917      * @param {Number} row The row index
36918      * @param {Number} col The column index
36919      * @param {Boolean} hscroll false to disable horizontal scrolling
36920      */
36921     focusCell : function(row, col, hscroll)
36922     {
36923         //Roo.log('GridView.focusCell');
36924         var el = this.ensureVisible(row, col, hscroll);
36925         this.focusEl.alignTo(el, "tl-tl");
36926         if(Roo.isGecko){
36927             this.focusEl.focus();
36928         }else{
36929             this.focusEl.focus.defer(1, this.focusEl);
36930         }
36931     },
36932
36933     /**
36934      * Scrolls the specified cell into view
36935      * @param {Number} row The row index
36936      * @param {Number} col The column index
36937      * @param {Boolean} hscroll false to disable horizontal scrolling
36938      */
36939     ensureVisible : function(row, col, hscroll)
36940     {
36941         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36942         //return null; //disable for testing.
36943         if(typeof row != "number"){
36944             row = row.rowIndex;
36945         }
36946         if(row < 0 && row >= this.ds.getCount()){
36947             return  null;
36948         }
36949         col = (col !== undefined ? col : 0);
36950         var cm = this.grid.colModel;
36951         while(cm.isHidden(col)){
36952             col++;
36953         }
36954
36955         var el = this.getCell(row, col);
36956         if(!el){
36957             return null;
36958         }
36959         var c = this.scroller.dom;
36960
36961         var ctop = parseInt(el.offsetTop, 10);
36962         var cleft = parseInt(el.offsetLeft, 10);
36963         var cbot = ctop + el.offsetHeight;
36964         var cright = cleft + el.offsetWidth;
36965         
36966         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36967         var stop = parseInt(c.scrollTop, 10);
36968         var sleft = parseInt(c.scrollLeft, 10);
36969         var sbot = stop + ch;
36970         var sright = sleft + c.clientWidth;
36971         /*
36972         Roo.log('GridView.ensureVisible:' +
36973                 ' ctop:' + ctop +
36974                 ' c.clientHeight:' + c.clientHeight +
36975                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36976                 ' stop:' + stop +
36977                 ' cbot:' + cbot +
36978                 ' sbot:' + sbot +
36979                 ' ch:' + ch  
36980                 );
36981         */
36982         if(ctop < stop){
36983             c.scrollTop = ctop;
36984             //Roo.log("set scrolltop to ctop DISABLE?");
36985         }else if(cbot > sbot){
36986             //Roo.log("set scrolltop to cbot-ch");
36987             c.scrollTop = cbot-ch;
36988         }
36989         
36990         if(hscroll !== false){
36991             if(cleft < sleft){
36992                 c.scrollLeft = cleft;
36993             }else if(cright > sright){
36994                 c.scrollLeft = cright-c.clientWidth;
36995             }
36996         }
36997          
36998         return el;
36999     },
37000
37001     updateColumns : function(){
37002         this.grid.stopEditing();
37003         var cm = this.grid.colModel, colIds = this.getColumnIds();
37004         //var totalWidth = cm.getTotalWidth();
37005         var pos = 0;
37006         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37007             //if(cm.isHidden(i)) continue;
37008             var w = cm.getColumnWidth(i);
37009             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37010             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37011         }
37012         this.updateSplitters();
37013     },
37014
37015     generateRules : function(cm){
37016         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37017         Roo.util.CSS.removeStyleSheet(rulesId);
37018         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37019             var cid = cm.getColumnId(i);
37020             var align = '';
37021             if(cm.config[i].align){
37022                 align = 'text-align:'+cm.config[i].align+';';
37023             }
37024             var hidden = '';
37025             if(cm.isHidden(i)){
37026                 hidden = 'display:none;';
37027             }
37028             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37029             ruleBuf.push(
37030                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37031                     this.hdSelector, cid, " {\n", align, width, "}\n",
37032                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37033                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37034         }
37035         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37036     },
37037
37038     updateSplitters : function(){
37039         var cm = this.cm, s = this.getSplitters();
37040         if(s){ // splitters not created yet
37041             var pos = 0, locked = true;
37042             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37043                 if(cm.isHidden(i)) {
37044                     continue;
37045                 }
37046                 var w = cm.getColumnWidth(i); // make sure it's a number
37047                 if(!cm.isLocked(i) && locked){
37048                     pos = 0;
37049                     locked = false;
37050                 }
37051                 pos += w;
37052                 s[i].style.left = (pos-this.splitOffset) + "px";
37053             }
37054         }
37055     },
37056
37057     handleHiddenChange : function(colModel, colIndex, hidden){
37058         if(hidden){
37059             this.hideColumn(colIndex);
37060         }else{
37061             this.unhideColumn(colIndex);
37062         }
37063     },
37064
37065     hideColumn : function(colIndex){
37066         var cid = this.getColumnId(colIndex);
37067         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37068         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37069         if(Roo.isSafari){
37070             this.updateHeaders();
37071         }
37072         this.updateSplitters();
37073         this.layout();
37074     },
37075
37076     unhideColumn : function(colIndex){
37077         var cid = this.getColumnId(colIndex);
37078         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37079         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37080
37081         if(Roo.isSafari){
37082             this.updateHeaders();
37083         }
37084         this.updateSplitters();
37085         this.layout();
37086     },
37087
37088     insertRows : function(dm, firstRow, lastRow, isUpdate){
37089         if(firstRow == 0 && lastRow == dm.getCount()-1){
37090             this.refresh();
37091         }else{
37092             if(!isUpdate){
37093                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37094             }
37095             var s = this.getScrollState();
37096             var markup = this.renderRows(firstRow, lastRow);
37097             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37098             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37099             this.restoreScroll(s);
37100             if(!isUpdate){
37101                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37102                 this.syncRowHeights(firstRow, lastRow);
37103                 this.stripeRows(firstRow);
37104                 this.layout();
37105             }
37106         }
37107     },
37108
37109     bufferRows : function(markup, target, index){
37110         var before = null, trows = target.rows, tbody = target.tBodies[0];
37111         if(index < trows.length){
37112             before = trows[index];
37113         }
37114         var b = document.createElement("div");
37115         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37116         var rows = b.firstChild.rows;
37117         for(var i = 0, len = rows.length; i < len; i++){
37118             if(before){
37119                 tbody.insertBefore(rows[0], before);
37120             }else{
37121                 tbody.appendChild(rows[0]);
37122             }
37123         }
37124         b.innerHTML = "";
37125         b = null;
37126     },
37127
37128     deleteRows : function(dm, firstRow, lastRow){
37129         if(dm.getRowCount()<1){
37130             this.fireEvent("beforerefresh", this);
37131             this.mainBody.update("");
37132             this.lockedBody.update("");
37133             this.fireEvent("refresh", this);
37134         }else{
37135             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37136             var bt = this.getBodyTable();
37137             var tbody = bt.firstChild;
37138             var rows = bt.rows;
37139             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37140                 tbody.removeChild(rows[firstRow]);
37141             }
37142             this.stripeRows(firstRow);
37143             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37144         }
37145     },
37146
37147     updateRows : function(dataSource, firstRow, lastRow){
37148         var s = this.getScrollState();
37149         this.refresh();
37150         this.restoreScroll(s);
37151     },
37152
37153     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37154         if(!noRefresh){
37155            this.refresh();
37156         }
37157         this.updateHeaderSortState();
37158     },
37159
37160     getScrollState : function(){
37161         
37162         var sb = this.scroller.dom;
37163         return {left: sb.scrollLeft, top: sb.scrollTop};
37164     },
37165
37166     stripeRows : function(startRow){
37167         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37168             return;
37169         }
37170         startRow = startRow || 0;
37171         var rows = this.getBodyTable().rows;
37172         var lrows = this.getLockedTable().rows;
37173         var cls = ' x-grid-row-alt ';
37174         for(var i = startRow, len = rows.length; i < len; i++){
37175             var row = rows[i], lrow = lrows[i];
37176             var isAlt = ((i+1) % 2 == 0);
37177             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37178             if(isAlt == hasAlt){
37179                 continue;
37180             }
37181             if(isAlt){
37182                 row.className += " x-grid-row-alt";
37183             }else{
37184                 row.className = row.className.replace("x-grid-row-alt", "");
37185             }
37186             if(lrow){
37187                 lrow.className = row.className;
37188             }
37189         }
37190     },
37191
37192     restoreScroll : function(state){
37193         //Roo.log('GridView.restoreScroll');
37194         var sb = this.scroller.dom;
37195         sb.scrollLeft = state.left;
37196         sb.scrollTop = state.top;
37197         this.syncScroll();
37198     },
37199
37200     syncScroll : function(){
37201         //Roo.log('GridView.syncScroll');
37202         var sb = this.scroller.dom;
37203         var sh = this.mainHd.dom;
37204         var bs = this.mainBody.dom;
37205         var lv = this.lockedBody.dom;
37206         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37207         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37208     },
37209
37210     handleScroll : function(e){
37211         this.syncScroll();
37212         var sb = this.scroller.dom;
37213         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37214         e.stopEvent();
37215     },
37216
37217     handleWheel : function(e){
37218         var d = e.getWheelDelta();
37219         this.scroller.dom.scrollTop -= d*22;
37220         // set this here to prevent jumpy scrolling on large tables
37221         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37222         e.stopEvent();
37223     },
37224
37225     renderRows : function(startRow, endRow){
37226         // pull in all the crap needed to render rows
37227         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37228         var colCount = cm.getColumnCount();
37229
37230         if(ds.getCount() < 1){
37231             return ["", ""];
37232         }
37233
37234         // build a map for all the columns
37235         var cs = [];
37236         for(var i = 0; i < colCount; i++){
37237             var name = cm.getDataIndex(i);
37238             cs[i] = {
37239                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37240                 renderer : cm.getRenderer(i),
37241                 id : cm.getColumnId(i),
37242                 locked : cm.isLocked(i),
37243                 has_editor : cm.isCellEditable(i)
37244             };
37245         }
37246
37247         startRow = startRow || 0;
37248         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37249
37250         // records to render
37251         var rs = ds.getRange(startRow, endRow);
37252
37253         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37254     },
37255
37256     // As much as I hate to duplicate code, this was branched because FireFox really hates
37257     // [].join("") on strings. The performance difference was substantial enough to
37258     // branch this function
37259     doRender : Roo.isGecko ?
37260             function(cs, rs, ds, startRow, colCount, stripe){
37261                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37262                 // buffers
37263                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37264                 
37265                 var hasListener = this.grid.hasListener('rowclass');
37266                 var rowcfg = {};
37267                 for(var j = 0, len = rs.length; j < len; j++){
37268                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37269                     for(var i = 0; i < colCount; i++){
37270                         c = cs[i];
37271                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37272                         p.id = c.id;
37273                         p.css = p.attr = "";
37274                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37275                         if(p.value == undefined || p.value === "") {
37276                             p.value = "&#160;";
37277                         }
37278                         if(c.has_editor){
37279                             p.css += ' x-grid-editable-cell';
37280                         }
37281                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
37282                             p.css +=  ' x-grid-dirty-cell';
37283                         }
37284                         var markup = ct.apply(p);
37285                         if(!c.locked){
37286                             cb+= markup;
37287                         }else{
37288                             lcb+= markup;
37289                         }
37290                     }
37291                     var alt = [];
37292                     if(stripe && ((rowIndex+1) % 2 == 0)){
37293                         alt.push("x-grid-row-alt")
37294                     }
37295                     if(r.dirty){
37296                         alt.push(  " x-grid-dirty-row");
37297                     }
37298                     rp.cells = lcb;
37299                     if(this.getRowClass){
37300                         alt.push(this.getRowClass(r, rowIndex));
37301                     }
37302                     if (hasListener) {
37303                         rowcfg = {
37304                              
37305                             record: r,
37306                             rowIndex : rowIndex,
37307                             rowClass : ''
37308                         };
37309                         this.grid.fireEvent('rowclass', this, rowcfg);
37310                         alt.push(rowcfg.rowClass);
37311                     }
37312                     rp.alt = alt.join(" ");
37313                     lbuf+= rt.apply(rp);
37314                     rp.cells = cb;
37315                     buf+=  rt.apply(rp);
37316                 }
37317                 return [lbuf, buf];
37318             } :
37319             function(cs, rs, ds, startRow, colCount, stripe){
37320                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37321                 // buffers
37322                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37323                 var hasListener = this.grid.hasListener('rowclass');
37324  
37325                 var rowcfg = {};
37326                 for(var j = 0, len = rs.length; j < len; j++){
37327                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37328                     for(var i = 0; i < colCount; i++){
37329                         c = cs[i];
37330                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37331                         p.id = c.id;
37332                         p.css = p.attr = "";
37333                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37334                         if(p.value == undefined || p.value === "") {
37335                             p.value = "&#160;";
37336                         }
37337                         //Roo.log(c);
37338                          if(c.has_editor){
37339                             p.css += ' x-grid-editable-cell';
37340                         }
37341                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37342                             p.css += ' x-grid-dirty-cell' 
37343                         }
37344                         
37345                         var markup = ct.apply(p);
37346                         if(!c.locked){
37347                             cb[cb.length] = markup;
37348                         }else{
37349                             lcb[lcb.length] = markup;
37350                         }
37351                     }
37352                     var alt = [];
37353                     if(stripe && ((rowIndex+1) % 2 == 0)){
37354                         alt.push( "x-grid-row-alt");
37355                     }
37356                     if(r.dirty){
37357                         alt.push(" x-grid-dirty-row");
37358                     }
37359                     rp.cells = lcb;
37360                     if(this.getRowClass){
37361                         alt.push( this.getRowClass(r, rowIndex));
37362                     }
37363                     if (hasListener) {
37364                         rowcfg = {
37365                              
37366                             record: r,
37367                             rowIndex : rowIndex,
37368                             rowClass : ''
37369                         };
37370                         this.grid.fireEvent('rowclass', this, rowcfg);
37371                         alt.push(rowcfg.rowClass);
37372                     }
37373                     
37374                     rp.alt = alt.join(" ");
37375                     rp.cells = lcb.join("");
37376                     lbuf[lbuf.length] = rt.apply(rp);
37377                     rp.cells = cb.join("");
37378                     buf[buf.length] =  rt.apply(rp);
37379                 }
37380                 return [lbuf.join(""), buf.join("")];
37381             },
37382
37383     renderBody : function(){
37384         var markup = this.renderRows();
37385         var bt = this.templates.body;
37386         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37387     },
37388
37389     /**
37390      * Refreshes the grid
37391      * @param {Boolean} headersToo
37392      */
37393     refresh : function(headersToo){
37394         this.fireEvent("beforerefresh", this);
37395         this.grid.stopEditing();
37396         var result = this.renderBody();
37397         this.lockedBody.update(result[0]);
37398         this.mainBody.update(result[1]);
37399         if(headersToo === true){
37400             this.updateHeaders();
37401             this.updateColumns();
37402             this.updateSplitters();
37403             this.updateHeaderSortState();
37404         }
37405         this.syncRowHeights();
37406         this.layout();
37407         this.fireEvent("refresh", this);
37408     },
37409
37410     handleColumnMove : function(cm, oldIndex, newIndex){
37411         this.indexMap = null;
37412         var s = this.getScrollState();
37413         this.refresh(true);
37414         this.restoreScroll(s);
37415         this.afterMove(newIndex);
37416     },
37417
37418     afterMove : function(colIndex){
37419         if(this.enableMoveAnim && Roo.enableFx){
37420             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37421         }
37422         // if multisort - fix sortOrder, and reload..
37423         if (this.grid.dataSource.multiSort) {
37424             // the we can call sort again..
37425             var dm = this.grid.dataSource;
37426             var cm = this.grid.colModel;
37427             var so = [];
37428             for(var i = 0; i < cm.config.length; i++ ) {
37429                 
37430                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37431                     continue; // dont' bother, it's not in sort list or being set.
37432                 }
37433                 
37434                 so.push(cm.config[i].dataIndex);
37435             };
37436             dm.sortOrder = so;
37437             dm.load(dm.lastOptions);
37438             
37439             
37440         }
37441         
37442     },
37443
37444     updateCell : function(dm, rowIndex, dataIndex){
37445         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37446         if(typeof colIndex == "undefined"){ // not present in grid
37447             return;
37448         }
37449         var cm = this.grid.colModel;
37450         var cell = this.getCell(rowIndex, colIndex);
37451         var cellText = this.getCellText(rowIndex, colIndex);
37452
37453         var p = {
37454             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37455             id : cm.getColumnId(colIndex),
37456             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37457         };
37458         var renderer = cm.getRenderer(colIndex);
37459         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37460         if(typeof val == "undefined" || val === "") {
37461             val = "&#160;";
37462         }
37463         cellText.innerHTML = val;
37464         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37465         this.syncRowHeights(rowIndex, rowIndex);
37466     },
37467
37468     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37469         var maxWidth = 0;
37470         if(this.grid.autoSizeHeaders){
37471             var h = this.getHeaderCellMeasure(colIndex);
37472             maxWidth = Math.max(maxWidth, h.scrollWidth);
37473         }
37474         var tb, index;
37475         if(this.cm.isLocked(colIndex)){
37476             tb = this.getLockedTable();
37477             index = colIndex;
37478         }else{
37479             tb = this.getBodyTable();
37480             index = colIndex - this.cm.getLockedCount();
37481         }
37482         if(tb && tb.rows){
37483             var rows = tb.rows;
37484             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37485             for(var i = 0; i < stopIndex; i++){
37486                 var cell = rows[i].childNodes[index].firstChild;
37487                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37488             }
37489         }
37490         return maxWidth + /*margin for error in IE*/ 5;
37491     },
37492     /**
37493      * Autofit a column to its content.
37494      * @param {Number} colIndex
37495      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37496      */
37497      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37498          if(this.cm.isHidden(colIndex)){
37499              return; // can't calc a hidden column
37500          }
37501         if(forceMinSize){
37502             var cid = this.cm.getColumnId(colIndex);
37503             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37504            if(this.grid.autoSizeHeaders){
37505                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37506            }
37507         }
37508         var newWidth = this.calcColumnWidth(colIndex);
37509         this.cm.setColumnWidth(colIndex,
37510             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37511         if(!suppressEvent){
37512             this.grid.fireEvent("columnresize", colIndex, newWidth);
37513         }
37514     },
37515
37516     /**
37517      * Autofits all columns to their content and then expands to fit any extra space in the grid
37518      */
37519      autoSizeColumns : function(){
37520         var cm = this.grid.colModel;
37521         var colCount = cm.getColumnCount();
37522         for(var i = 0; i < colCount; i++){
37523             this.autoSizeColumn(i, true, true);
37524         }
37525         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37526             this.fitColumns();
37527         }else{
37528             this.updateColumns();
37529             this.layout();
37530         }
37531     },
37532
37533     /**
37534      * Autofits all columns to the grid's width proportionate with their current size
37535      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37536      */
37537     fitColumns : function(reserveScrollSpace){
37538         var cm = this.grid.colModel;
37539         var colCount = cm.getColumnCount();
37540         var cols = [];
37541         var width = 0;
37542         var i, w;
37543         for (i = 0; i < colCount; i++){
37544             if(!cm.isHidden(i) && !cm.isFixed(i)){
37545                 w = cm.getColumnWidth(i);
37546                 cols.push(i);
37547                 cols.push(w);
37548                 width += w;
37549             }
37550         }
37551         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37552         if(reserveScrollSpace){
37553             avail -= 17;
37554         }
37555         var frac = (avail - cm.getTotalWidth())/width;
37556         while (cols.length){
37557             w = cols.pop();
37558             i = cols.pop();
37559             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37560         }
37561         this.updateColumns();
37562         this.layout();
37563     },
37564
37565     onRowSelect : function(rowIndex){
37566         var row = this.getRowComposite(rowIndex);
37567         row.addClass("x-grid-row-selected");
37568     },
37569
37570     onRowDeselect : function(rowIndex){
37571         var row = this.getRowComposite(rowIndex);
37572         row.removeClass("x-grid-row-selected");
37573     },
37574
37575     onCellSelect : function(row, col){
37576         var cell = this.getCell(row, col);
37577         if(cell){
37578             Roo.fly(cell).addClass("x-grid-cell-selected");
37579         }
37580     },
37581
37582     onCellDeselect : function(row, col){
37583         var cell = this.getCell(row, col);
37584         if(cell){
37585             Roo.fly(cell).removeClass("x-grid-cell-selected");
37586         }
37587     },
37588
37589     updateHeaderSortState : function(){
37590         
37591         // sort state can be single { field: xxx, direction : yyy}
37592         // or   { xxx=>ASC , yyy : DESC ..... }
37593         
37594         var mstate = {};
37595         if (!this.ds.multiSort) { 
37596             var state = this.ds.getSortState();
37597             if(!state){
37598                 return;
37599             }
37600             mstate[state.field] = state.direction;
37601             // FIXME... - this is not used here.. but might be elsewhere..
37602             this.sortState = state;
37603             
37604         } else {
37605             mstate = this.ds.sortToggle;
37606         }
37607         //remove existing sort classes..
37608         
37609         var sc = this.sortClasses;
37610         var hds = this.el.select(this.headerSelector).removeClass(sc);
37611         
37612         for(var f in mstate) {
37613         
37614             var sortColumn = this.cm.findColumnIndex(f);
37615             
37616             if(sortColumn != -1){
37617                 var sortDir = mstate[f];        
37618                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37619             }
37620         }
37621         
37622          
37623         
37624     },
37625
37626
37627     handleHeaderClick : function(g, index,e){
37628         
37629         Roo.log("header click");
37630         
37631         if (Roo.isTouch) {
37632             // touch events on header are handled by context
37633             this.handleHdCtx(g,index,e);
37634             return;
37635         }
37636         
37637         
37638         if(this.headersDisabled){
37639             return;
37640         }
37641         var dm = g.dataSource, cm = g.colModel;
37642         if(!cm.isSortable(index)){
37643             return;
37644         }
37645         g.stopEditing();
37646         
37647         if (dm.multiSort) {
37648             // update the sortOrder
37649             var so = [];
37650             for(var i = 0; i < cm.config.length; i++ ) {
37651                 
37652                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37653                     continue; // dont' bother, it's not in sort list or being set.
37654                 }
37655                 
37656                 so.push(cm.config[i].dataIndex);
37657             };
37658             dm.sortOrder = so;
37659         }
37660         
37661         
37662         dm.sort(cm.getDataIndex(index));
37663     },
37664
37665
37666     destroy : function(){
37667         if(this.colMenu){
37668             this.colMenu.removeAll();
37669             Roo.menu.MenuMgr.unregister(this.colMenu);
37670             this.colMenu.getEl().remove();
37671             delete this.colMenu;
37672         }
37673         if(this.hmenu){
37674             this.hmenu.removeAll();
37675             Roo.menu.MenuMgr.unregister(this.hmenu);
37676             this.hmenu.getEl().remove();
37677             delete this.hmenu;
37678         }
37679         if(this.grid.enableColumnMove){
37680             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37681             if(dds){
37682                 for(var dd in dds){
37683                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37684                         var elid = dds[dd].dragElId;
37685                         dds[dd].unreg();
37686                         Roo.get(elid).remove();
37687                     } else if(dds[dd].config.isTarget){
37688                         dds[dd].proxyTop.remove();
37689                         dds[dd].proxyBottom.remove();
37690                         dds[dd].unreg();
37691                     }
37692                     if(Roo.dd.DDM.locationCache[dd]){
37693                         delete Roo.dd.DDM.locationCache[dd];
37694                     }
37695                 }
37696                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37697             }
37698         }
37699         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37700         this.bind(null, null);
37701         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37702     },
37703
37704     handleLockChange : function(){
37705         this.refresh(true);
37706     },
37707
37708     onDenyColumnLock : function(){
37709
37710     },
37711
37712     onDenyColumnHide : function(){
37713
37714     },
37715
37716     handleHdMenuClick : function(item){
37717         var index = this.hdCtxIndex;
37718         var cm = this.cm, ds = this.ds;
37719         switch(item.id){
37720             case "asc":
37721                 ds.sort(cm.getDataIndex(index), "ASC");
37722                 break;
37723             case "desc":
37724                 ds.sort(cm.getDataIndex(index), "DESC");
37725                 break;
37726             case "lock":
37727                 var lc = cm.getLockedCount();
37728                 if(cm.getColumnCount(true) <= lc+1){
37729                     this.onDenyColumnLock();
37730                     return;
37731                 }
37732                 if(lc != index){
37733                     cm.setLocked(index, true, true);
37734                     cm.moveColumn(index, lc);
37735                     this.grid.fireEvent("columnmove", index, lc);
37736                 }else{
37737                     cm.setLocked(index, true);
37738                 }
37739             break;
37740             case "unlock":
37741                 var lc = cm.getLockedCount();
37742                 if((lc-1) != index){
37743                     cm.setLocked(index, false, true);
37744                     cm.moveColumn(index, lc-1);
37745                     this.grid.fireEvent("columnmove", index, lc-1);
37746                 }else{
37747                     cm.setLocked(index, false);
37748                 }
37749             break;
37750             case 'wider': // used to expand cols on touch..
37751             case 'narrow':
37752                 var cw = cm.getColumnWidth(index);
37753                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37754                 cw = Math.max(0, cw);
37755                 cw = Math.min(cw,4000);
37756                 cm.setColumnWidth(index, cw);
37757                 break;
37758                 
37759             default:
37760                 index = cm.getIndexById(item.id.substr(4));
37761                 if(index != -1){
37762                     if(item.checked && cm.getColumnCount(true) <= 1){
37763                         this.onDenyColumnHide();
37764                         return false;
37765                     }
37766                     cm.setHidden(index, item.checked);
37767                 }
37768         }
37769         return true;
37770     },
37771
37772     beforeColMenuShow : function(){
37773         var cm = this.cm,  colCount = cm.getColumnCount();
37774         this.colMenu.removeAll();
37775         
37776         var items = [];
37777         for(var i = 0; i < colCount; i++){
37778             items.push({
37779                 id: "col-"+cm.getColumnId(i),
37780                 text: cm.getColumnHeader(i),
37781                 checked: !cm.isHidden(i),
37782                 hideOnClick:false
37783             });
37784         }
37785         
37786         if (this.grid.sortColMenu) {
37787             items.sort(function(a,b) {
37788                 if (a.text == b.text) {
37789                     return 0;
37790                 }
37791                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
37792             });
37793         }
37794         
37795         for(var i = 0; i < colCount; i++){
37796             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
37797         }
37798     },
37799
37800     handleHdCtx : function(g, index, e){
37801         e.stopEvent();
37802         var hd = this.getHeaderCell(index);
37803         this.hdCtxIndex = index;
37804         var ms = this.hmenu.items, cm = this.cm;
37805         ms.get("asc").setDisabled(!cm.isSortable(index));
37806         ms.get("desc").setDisabled(!cm.isSortable(index));
37807         if(this.grid.enableColLock !== false){
37808             ms.get("lock").setDisabled(cm.isLocked(index));
37809             ms.get("unlock").setDisabled(!cm.isLocked(index));
37810         }
37811         this.hmenu.show(hd, "tl-bl");
37812     },
37813
37814     handleHdOver : function(e){
37815         var hd = this.findHeaderCell(e.getTarget());
37816         if(hd && !this.headersDisabled){
37817             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37818                this.fly(hd).addClass("x-grid-hd-over");
37819             }
37820         }
37821     },
37822
37823     handleHdOut : function(e){
37824         var hd = this.findHeaderCell(e.getTarget());
37825         if(hd){
37826             this.fly(hd).removeClass("x-grid-hd-over");
37827         }
37828     },
37829
37830     handleSplitDblClick : function(e, t){
37831         var i = this.getCellIndex(t);
37832         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37833             this.autoSizeColumn(i, true);
37834             this.layout();
37835         }
37836     },
37837
37838     render : function(){
37839
37840         var cm = this.cm;
37841         var colCount = cm.getColumnCount();
37842
37843         if(this.grid.monitorWindowResize === true){
37844             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37845         }
37846         var header = this.renderHeaders();
37847         var body = this.templates.body.apply({rows:""});
37848         var html = this.templates.master.apply({
37849             lockedBody: body,
37850             body: body,
37851             lockedHeader: header[0],
37852             header: header[1]
37853         });
37854
37855         //this.updateColumns();
37856
37857         this.grid.getGridEl().dom.innerHTML = html;
37858
37859         this.initElements();
37860         
37861         // a kludge to fix the random scolling effect in webkit
37862         this.el.on("scroll", function() {
37863             this.el.dom.scrollTop=0; // hopefully not recursive..
37864         },this);
37865
37866         this.scroller.on("scroll", this.handleScroll, this);
37867         this.lockedBody.on("mousewheel", this.handleWheel, this);
37868         this.mainBody.on("mousewheel", this.handleWheel, this);
37869
37870         this.mainHd.on("mouseover", this.handleHdOver, this);
37871         this.mainHd.on("mouseout", this.handleHdOut, this);
37872         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37873                 {delegate: "."+this.splitClass});
37874
37875         this.lockedHd.on("mouseover", this.handleHdOver, this);
37876         this.lockedHd.on("mouseout", this.handleHdOut, this);
37877         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37878                 {delegate: "."+this.splitClass});
37879
37880         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37881             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37882         }
37883
37884         this.updateSplitters();
37885
37886         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37887             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37888             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37889         }
37890
37891         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37892             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37893             this.hmenu.add(
37894                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37895                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37896             );
37897             if(this.grid.enableColLock !== false){
37898                 this.hmenu.add('-',
37899                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37900                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37901                 );
37902             }
37903             if (Roo.isTouch) {
37904                  this.hmenu.add('-',
37905                     {id:"wider", text: this.columnsWiderText},
37906                     {id:"narrow", text: this.columnsNarrowText }
37907                 );
37908                 
37909                  
37910             }
37911             
37912             if(this.grid.enableColumnHide !== false){
37913
37914                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37915                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37916                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37917
37918                 this.hmenu.add('-',
37919                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37920                 );
37921             }
37922             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37923
37924             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37925         }
37926
37927         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37928             this.dd = new Roo.grid.GridDragZone(this.grid, {
37929                 ddGroup : this.grid.ddGroup || 'GridDD'
37930             });
37931             
37932         }
37933
37934         /*
37935         for(var i = 0; i < colCount; i++){
37936             if(cm.isHidden(i)){
37937                 this.hideColumn(i);
37938             }
37939             if(cm.config[i].align){
37940                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37941                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37942             }
37943         }*/
37944         
37945         this.updateHeaderSortState();
37946
37947         this.beforeInitialResize();
37948         this.layout(true);
37949
37950         // two part rendering gives faster view to the user
37951         this.renderPhase2.defer(1, this);
37952     },
37953
37954     renderPhase2 : function(){
37955         // render the rows now
37956         this.refresh();
37957         if(this.grid.autoSizeColumns){
37958             this.autoSizeColumns();
37959         }
37960     },
37961
37962     beforeInitialResize : function(){
37963
37964     },
37965
37966     onColumnSplitterMoved : function(i, w){
37967         this.userResized = true;
37968         var cm = this.grid.colModel;
37969         cm.setColumnWidth(i, w, true);
37970         var cid = cm.getColumnId(i);
37971         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37972         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37973         this.updateSplitters();
37974         this.layout();
37975         this.grid.fireEvent("columnresize", i, w);
37976     },
37977
37978     syncRowHeights : function(startIndex, endIndex){
37979         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37980             startIndex = startIndex || 0;
37981             var mrows = this.getBodyTable().rows;
37982             var lrows = this.getLockedTable().rows;
37983             var len = mrows.length-1;
37984             endIndex = Math.min(endIndex || len, len);
37985             for(var i = startIndex; i <= endIndex; i++){
37986                 var m = mrows[i], l = lrows[i];
37987                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37988                 m.style.height = l.style.height = h + "px";
37989             }
37990         }
37991     },
37992
37993     layout : function(initialRender, is2ndPass)
37994     {
37995         var g = this.grid;
37996         var auto = g.autoHeight;
37997         var scrollOffset = 16;
37998         var c = g.getGridEl(), cm = this.cm,
37999                 expandCol = g.autoExpandColumn,
38000                 gv = this;
38001         //c.beginMeasure();
38002
38003         if(!c.dom.offsetWidth){ // display:none?
38004             if(initialRender){
38005                 this.lockedWrap.show();
38006                 this.mainWrap.show();
38007             }
38008             return;
38009         }
38010
38011         var hasLock = this.cm.isLocked(0);
38012
38013         var tbh = this.headerPanel.getHeight();
38014         var bbh = this.footerPanel.getHeight();
38015
38016         if(auto){
38017             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38018             var newHeight = ch + c.getBorderWidth("tb");
38019             if(g.maxHeight){
38020                 newHeight = Math.min(g.maxHeight, newHeight);
38021             }
38022             c.setHeight(newHeight);
38023         }
38024
38025         if(g.autoWidth){
38026             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38027         }
38028
38029         var s = this.scroller;
38030
38031         var csize = c.getSize(true);
38032
38033         this.el.setSize(csize.width, csize.height);
38034
38035         this.headerPanel.setWidth(csize.width);
38036         this.footerPanel.setWidth(csize.width);
38037
38038         var hdHeight = this.mainHd.getHeight();
38039         var vw = csize.width;
38040         var vh = csize.height - (tbh + bbh);
38041
38042         s.setSize(vw, vh);
38043
38044         var bt = this.getBodyTable();
38045         
38046         if(cm.getLockedCount() == cm.config.length){
38047             bt = this.getLockedTable();
38048         }
38049         
38050         var ltWidth = hasLock ?
38051                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38052
38053         var scrollHeight = bt.offsetHeight;
38054         var scrollWidth = ltWidth + bt.offsetWidth;
38055         var vscroll = false, hscroll = false;
38056
38057         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38058
38059         var lw = this.lockedWrap, mw = this.mainWrap;
38060         var lb = this.lockedBody, mb = this.mainBody;
38061
38062         setTimeout(function(){
38063             var t = s.dom.offsetTop;
38064             var w = s.dom.clientWidth,
38065                 h = s.dom.clientHeight;
38066
38067             lw.setTop(t);
38068             lw.setSize(ltWidth, h);
38069
38070             mw.setLeftTop(ltWidth, t);
38071             mw.setSize(w-ltWidth, h);
38072
38073             lb.setHeight(h-hdHeight);
38074             mb.setHeight(h-hdHeight);
38075
38076             if(is2ndPass !== true && !gv.userResized && expandCol){
38077                 // high speed resize without full column calculation
38078                 
38079                 var ci = cm.getIndexById(expandCol);
38080                 if (ci < 0) {
38081                     ci = cm.findColumnIndex(expandCol);
38082                 }
38083                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38084                 var expandId = cm.getColumnId(ci);
38085                 var  tw = cm.getTotalWidth(false);
38086                 var currentWidth = cm.getColumnWidth(ci);
38087                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38088                 if(currentWidth != cw){
38089                     cm.setColumnWidth(ci, cw, true);
38090                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38091                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38092                     gv.updateSplitters();
38093                     gv.layout(false, true);
38094                 }
38095             }
38096
38097             if(initialRender){
38098                 lw.show();
38099                 mw.show();
38100             }
38101             //c.endMeasure();
38102         }, 10);
38103     },
38104
38105     onWindowResize : function(){
38106         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38107             return;
38108         }
38109         this.layout();
38110     },
38111
38112     appendFooter : function(parentEl){
38113         return null;
38114     },
38115
38116     sortAscText : "Sort Ascending",
38117     sortDescText : "Sort Descending",
38118     lockText : "Lock Column",
38119     unlockText : "Unlock Column",
38120     columnsText : "Columns",
38121  
38122     columnsWiderText : "Wider",
38123     columnsNarrowText : "Thinner"
38124 });
38125
38126
38127 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38128     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38129     this.proxy.el.addClass('x-grid3-col-dd');
38130 };
38131
38132 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38133     handleMouseDown : function(e){
38134
38135     },
38136
38137     callHandleMouseDown : function(e){
38138         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38139     }
38140 });
38141 /*
38142  * Based on:
38143  * Ext JS Library 1.1.1
38144  * Copyright(c) 2006-2007, Ext JS, LLC.
38145  *
38146  * Originally Released Under LGPL - original licence link has changed is not relivant.
38147  *
38148  * Fork - LGPL
38149  * <script type="text/javascript">
38150  */
38151  /**
38152  * @extends Roo.dd.DDProxy
38153  * @class Roo.grid.SplitDragZone
38154  * Support for Column Header resizing
38155  * @constructor
38156  * @param {Object} config
38157  */
38158 // private
38159 // This is a support class used internally by the Grid components
38160 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38161     this.grid = grid;
38162     this.view = grid.getView();
38163     this.proxy = this.view.resizeProxy;
38164     Roo.grid.SplitDragZone.superclass.constructor.call(
38165         this,
38166         hd, // ID
38167         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
38168         {  // CONFIG
38169             dragElId : Roo.id(this.proxy.dom),
38170             resizeFrame:false
38171         }
38172     );
38173     
38174     this.setHandleElId(Roo.id(hd));
38175     if (hd2 !== false) {
38176         this.setOuterHandleElId(Roo.id(hd2));
38177     }
38178     
38179     this.scroll = false;
38180 };
38181 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38182     fly: Roo.Element.fly,
38183
38184     b4StartDrag : function(x, y){
38185         this.view.headersDisabled = true;
38186         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
38187                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
38188         );
38189         this.proxy.setHeight(h);
38190         
38191         // for old system colWidth really stored the actual width?
38192         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
38193         // which in reality did not work.. - it worked only for fixed sizes
38194         // for resizable we need to use actual sizes.
38195         var w = this.cm.getColumnWidth(this.cellIndex);
38196         if (!this.view.mainWrap) {
38197             // bootstrap.
38198             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
38199         }
38200         
38201         
38202         
38203         // this was w-this.grid.minColumnWidth;
38204         // doesnt really make sense? - w = thie curren width or the rendered one?
38205         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38206         this.resetConstraints();
38207         this.setXConstraint(minw, 1000);
38208         this.setYConstraint(0, 0);
38209         this.minX = x - minw;
38210         this.maxX = x + 1000;
38211         this.startPos = x;
38212         if (!this.view.mainWrap) { // this is Bootstrap code..
38213             this.getDragEl().style.display='block';
38214         }
38215         
38216         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38217     },
38218
38219
38220     handleMouseDown : function(e){
38221         ev = Roo.EventObject.setEvent(e);
38222         var t = this.fly(ev.getTarget());
38223         if(t.hasClass("x-grid-split")){
38224             this.cellIndex = this.view.getCellIndex(t.dom);
38225             this.split = t.dom;
38226             this.cm = this.grid.colModel;
38227             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38228                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38229             }
38230         }
38231     },
38232
38233     endDrag : function(e){
38234         this.view.headersDisabled = false;
38235         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38236         var diff = endX - this.startPos;
38237         // 
38238         var w = this.cm.getColumnWidth(this.cellIndex);
38239         if (!this.view.mainWrap) {
38240             w = 0;
38241         }
38242         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
38243     },
38244
38245     autoOffset : function(){
38246         this.setDelta(0,0);
38247     }
38248 });/*
38249  * Based on:
38250  * Ext JS Library 1.1.1
38251  * Copyright(c) 2006-2007, Ext JS, LLC.
38252  *
38253  * Originally Released Under LGPL - original licence link has changed is not relivant.
38254  *
38255  * Fork - LGPL
38256  * <script type="text/javascript">
38257  */
38258  
38259 // private
38260 // This is a support class used internally by the Grid components
38261 Roo.grid.GridDragZone = function(grid, config){
38262     this.view = grid.getView();
38263     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38264     if(this.view.lockedBody){
38265         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38266         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38267     }
38268     this.scroll = false;
38269     this.grid = grid;
38270     this.ddel = document.createElement('div');
38271     this.ddel.className = 'x-grid-dd-wrap';
38272 };
38273
38274 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38275     ddGroup : "GridDD",
38276
38277     getDragData : function(e){
38278         var t = Roo.lib.Event.getTarget(e);
38279         var rowIndex = this.view.findRowIndex(t);
38280         var sm = this.grid.selModel;
38281             
38282         //Roo.log(rowIndex);
38283         
38284         if (sm.getSelectedCell) {
38285             // cell selection..
38286             if (!sm.getSelectedCell()) {
38287                 return false;
38288             }
38289             if (rowIndex != sm.getSelectedCell()[0]) {
38290                 return false;
38291             }
38292         
38293         }
38294         if (sm.getSelections && sm.getSelections().length < 1) {
38295             return false;
38296         }
38297         
38298         
38299         // before it used to all dragging of unseleted... - now we dont do that.
38300         if(rowIndex !== false){
38301             
38302             // if editorgrid.. 
38303             
38304             
38305             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38306                
38307             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38308               //  
38309             //}
38310             if (e.hasModifier()){
38311                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38312             }
38313             
38314             Roo.log("getDragData");
38315             
38316             return {
38317                 grid: this.grid,
38318                 ddel: this.ddel,
38319                 rowIndex: rowIndex,
38320                 selections: sm.getSelections ? sm.getSelections() : (
38321                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
38322             };
38323         }
38324         return false;
38325     },
38326     
38327     
38328     onInitDrag : function(e){
38329         var data = this.dragData;
38330         this.ddel.innerHTML = this.grid.getDragDropText();
38331         this.proxy.update(this.ddel);
38332         // fire start drag?
38333     },
38334
38335     afterRepair : function(){
38336         this.dragging = false;
38337     },
38338
38339     getRepairXY : function(e, data){
38340         return false;
38341     },
38342
38343     onEndDrag : function(data, e){
38344         // fire end drag?
38345     },
38346
38347     onValidDrop : function(dd, e, id){
38348         // fire drag drop?
38349         this.hideProxy();
38350     },
38351
38352     beforeInvalidDrop : function(e, id){
38353
38354     }
38355 });/*
38356  * Based on:
38357  * Ext JS Library 1.1.1
38358  * Copyright(c) 2006-2007, Ext JS, LLC.
38359  *
38360  * Originally Released Under LGPL - original licence link has changed is not relivant.
38361  *
38362  * Fork - LGPL
38363  * <script type="text/javascript">
38364  */
38365  
38366
38367 /**
38368  * @class Roo.grid.ColumnModel
38369  * @extends Roo.util.Observable
38370  * This is the default implementation of a ColumnModel used by the Grid. It defines
38371  * the columns in the grid.
38372  * <br>Usage:<br>
38373  <pre><code>
38374  var colModel = new Roo.grid.ColumnModel([
38375         {header: "Ticker", width: 60, sortable: true, locked: true},
38376         {header: "Company Name", width: 150, sortable: true},
38377         {header: "Market Cap.", width: 100, sortable: true},
38378         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38379         {header: "Employees", width: 100, sortable: true, resizable: false}
38380  ]);
38381  </code></pre>
38382  * <p>
38383  
38384  * The config options listed for this class are options which may appear in each
38385  * individual column definition.
38386  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38387  * @constructor
38388  * @param {Object} config An Array of column config objects. See this class's
38389  * config objects for details.
38390 */
38391 Roo.grid.ColumnModel = function(config){
38392         /**
38393      * The config passed into the constructor
38394      */
38395     this.config = []; //config;
38396     this.lookup = {};
38397
38398     // if no id, create one
38399     // if the column does not have a dataIndex mapping,
38400     // map it to the order it is in the config
38401     for(var i = 0, len = config.length; i < len; i++){
38402         this.addColumn(config[i]);
38403         
38404     }
38405
38406     /**
38407      * The width of columns which have no width specified (defaults to 100)
38408      * @type Number
38409      */
38410     this.defaultWidth = 100;
38411
38412     /**
38413      * Default sortable of columns which have no sortable specified (defaults to false)
38414      * @type Boolean
38415      */
38416     this.defaultSortable = false;
38417
38418     this.addEvents({
38419         /**
38420              * @event widthchange
38421              * Fires when the width of a column changes.
38422              * @param {ColumnModel} this
38423              * @param {Number} columnIndex The column index
38424              * @param {Number} newWidth The new width
38425              */
38426             "widthchange": true,
38427         /**
38428              * @event headerchange
38429              * Fires when the text of a header changes.
38430              * @param {ColumnModel} this
38431              * @param {Number} columnIndex The column index
38432              * @param {Number} newText The new header text
38433              */
38434             "headerchange": true,
38435         /**
38436              * @event hiddenchange
38437              * Fires when a column is hidden or "unhidden".
38438              * @param {ColumnModel} this
38439              * @param {Number} columnIndex The column index
38440              * @param {Boolean} hidden true if hidden, false otherwise
38441              */
38442             "hiddenchange": true,
38443             /**
38444          * @event columnmoved
38445          * Fires when a column is moved.
38446          * @param {ColumnModel} this
38447          * @param {Number} oldIndex
38448          * @param {Number} newIndex
38449          */
38450         "columnmoved" : true,
38451         /**
38452          * @event columlockchange
38453          * Fires when a column's locked state is changed
38454          * @param {ColumnModel} this
38455          * @param {Number} colIndex
38456          * @param {Boolean} locked true if locked
38457          */
38458         "columnlockchange" : true
38459     });
38460     Roo.grid.ColumnModel.superclass.constructor.call(this);
38461 };
38462 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38463     /**
38464      * @cfg {String} header The header text to display in the Grid view.
38465      */
38466         /**
38467      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
38468      */
38469         /**
38470      * @cfg {String} smHeader Header at Bootsrap Small width
38471      */
38472         /**
38473      * @cfg {String} mdHeader Header at Bootsrap Medium width
38474      */
38475         /**
38476      * @cfg {String} lgHeader Header at Bootsrap Large width
38477      */
38478         /**
38479      * @cfg {String} xlHeader Header at Bootsrap extra Large width
38480      */
38481     /**
38482      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38483      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38484      * specified, the column's index is used as an index into the Record's data Array.
38485      */
38486     /**
38487      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38488      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38489      */
38490     /**
38491      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38492      * Defaults to the value of the {@link #defaultSortable} property.
38493      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38494      */
38495     /**
38496      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38497      */
38498     /**
38499      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38500      */
38501     /**
38502      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38503      */
38504     /**
38505      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38506      */
38507     /**
38508      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38509      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38510      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
38511      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38512      */
38513        /**
38514      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38515      */
38516     /**
38517      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38518      */
38519     /**
38520      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
38521      */
38522     /**
38523      * @cfg {String} cursor (Optional)
38524      */
38525     /**
38526      * @cfg {String} tooltip (Optional)
38527      */
38528     /**
38529      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
38530      */
38531     /**
38532      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
38533      */
38534     /**
38535      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
38536      */
38537     /**
38538      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
38539      */
38540         /**
38541      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
38542      */
38543     /**
38544      * Returns the id of the column at the specified index.
38545      * @param {Number} index The column index
38546      * @return {String} the id
38547      */
38548     getColumnId : function(index){
38549         return this.config[index].id;
38550     },
38551
38552     /**
38553      * Returns the column for a specified id.
38554      * @param {String} id The column id
38555      * @return {Object} the column
38556      */
38557     getColumnById : function(id){
38558         return this.lookup[id];
38559     },
38560
38561     
38562     /**
38563      * Returns the column Object for a specified dataIndex.
38564      * @param {String} dataIndex The column dataIndex
38565      * @return {Object|Boolean} the column or false if not found
38566      */
38567     getColumnByDataIndex: function(dataIndex){
38568         var index = this.findColumnIndex(dataIndex);
38569         return index > -1 ? this.config[index] : false;
38570     },
38571     
38572     /**
38573      * Returns the index for a specified column id.
38574      * @param {String} id The column id
38575      * @return {Number} the index, or -1 if not found
38576      */
38577     getIndexById : function(id){
38578         for(var i = 0, len = this.config.length; i < len; i++){
38579             if(this.config[i].id == id){
38580                 return i;
38581             }
38582         }
38583         return -1;
38584     },
38585     
38586     /**
38587      * Returns the index for a specified column dataIndex.
38588      * @param {String} dataIndex The column dataIndex
38589      * @return {Number} the index, or -1 if not found
38590      */
38591     
38592     findColumnIndex : function(dataIndex){
38593         for(var i = 0, len = this.config.length; i < len; i++){
38594             if(this.config[i].dataIndex == dataIndex){
38595                 return i;
38596             }
38597         }
38598         return -1;
38599     },
38600     
38601     
38602     moveColumn : function(oldIndex, newIndex){
38603         var c = this.config[oldIndex];
38604         this.config.splice(oldIndex, 1);
38605         this.config.splice(newIndex, 0, c);
38606         this.dataMap = null;
38607         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38608     },
38609
38610     isLocked : function(colIndex){
38611         return this.config[colIndex].locked === true;
38612     },
38613
38614     setLocked : function(colIndex, value, suppressEvent){
38615         if(this.isLocked(colIndex) == value){
38616             return;
38617         }
38618         this.config[colIndex].locked = value;
38619         if(!suppressEvent){
38620             this.fireEvent("columnlockchange", this, colIndex, value);
38621         }
38622     },
38623
38624     getTotalLockedWidth : function(){
38625         var totalWidth = 0;
38626         for(var i = 0; i < this.config.length; i++){
38627             if(this.isLocked(i) && !this.isHidden(i)){
38628                 this.totalWidth += this.getColumnWidth(i);
38629             }
38630         }
38631         return totalWidth;
38632     },
38633
38634     getLockedCount : function(){
38635         for(var i = 0, len = this.config.length; i < len; i++){
38636             if(!this.isLocked(i)){
38637                 return i;
38638             }
38639         }
38640         
38641         return this.config.length;
38642     },
38643
38644     /**
38645      * Returns the number of columns.
38646      * @return {Number}
38647      */
38648     getColumnCount : function(visibleOnly){
38649         if(visibleOnly === true){
38650             var c = 0;
38651             for(var i = 0, len = this.config.length; i < len; i++){
38652                 if(!this.isHidden(i)){
38653                     c++;
38654                 }
38655             }
38656             return c;
38657         }
38658         return this.config.length;
38659     },
38660
38661     /**
38662      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38663      * @param {Function} fn
38664      * @param {Object} scope (optional)
38665      * @return {Array} result
38666      */
38667     getColumnsBy : function(fn, scope){
38668         var r = [];
38669         for(var i = 0, len = this.config.length; i < len; i++){
38670             var c = this.config[i];
38671             if(fn.call(scope||this, c, i) === true){
38672                 r[r.length] = c;
38673             }
38674         }
38675         return r;
38676     },
38677
38678     /**
38679      * Returns true if the specified column is sortable.
38680      * @param {Number} col The column index
38681      * @return {Boolean}
38682      */
38683     isSortable : function(col){
38684         if(typeof this.config[col].sortable == "undefined"){
38685             return this.defaultSortable;
38686         }
38687         return this.config[col].sortable;
38688     },
38689
38690     /**
38691      * Returns the rendering (formatting) function defined for the column.
38692      * @param {Number} col The column index.
38693      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38694      */
38695     getRenderer : function(col){
38696         if(!this.config[col].renderer){
38697             return Roo.grid.ColumnModel.defaultRenderer;
38698         }
38699         return this.config[col].renderer;
38700     },
38701
38702     /**
38703      * Sets the rendering (formatting) function for a column.
38704      * @param {Number} col The column index
38705      * @param {Function} fn The function to use to process the cell's raw data
38706      * to return HTML markup for the grid view. The render function is called with
38707      * the following parameters:<ul>
38708      * <li>Data value.</li>
38709      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38710      * <li>css A CSS style string to apply to the table cell.</li>
38711      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38712      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38713      * <li>Row index</li>
38714      * <li>Column index</li>
38715      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38716      */
38717     setRenderer : function(col, fn){
38718         this.config[col].renderer = fn;
38719     },
38720
38721     /**
38722      * Returns the width for the specified column.
38723      * @param {Number} col The column index
38724      * @param (optional) {String} gridSize bootstrap width size.
38725      * @return {Number}
38726      */
38727     getColumnWidth : function(col, gridSize)
38728         {
38729                 var cfg = this.config[col];
38730                 
38731                 if (typeof(gridSize) == 'undefined') {
38732                         return cfg.width * 1 || this.defaultWidth;
38733                 }
38734                 if (gridSize === false) { // if we set it..
38735                         return cfg.width || false;
38736                 }
38737                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
38738                 
38739                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
38740                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
38741                                 continue;
38742                         }
38743                         return cfg[ sizes[i] ];
38744                 }
38745                 return 1;
38746                 
38747     },
38748
38749     /**
38750      * Sets the width for a column.
38751      * @param {Number} col The column index
38752      * @param {Number} width The new width
38753      */
38754     setColumnWidth : function(col, width, suppressEvent){
38755         this.config[col].width = width;
38756         this.totalWidth = null;
38757         if(!suppressEvent){
38758              this.fireEvent("widthchange", this, col, width);
38759         }
38760     },
38761
38762     /**
38763      * Returns the total width of all columns.
38764      * @param {Boolean} includeHidden True to include hidden column widths
38765      * @return {Number}
38766      */
38767     getTotalWidth : function(includeHidden){
38768         if(!this.totalWidth){
38769             this.totalWidth = 0;
38770             for(var i = 0, len = this.config.length; i < len; i++){
38771                 if(includeHidden || !this.isHidden(i)){
38772                     this.totalWidth += this.getColumnWidth(i);
38773                 }
38774             }
38775         }
38776         return this.totalWidth;
38777     },
38778
38779     /**
38780      * Returns the header for the specified column.
38781      * @param {Number} col The column index
38782      * @return {String}
38783      */
38784     getColumnHeader : function(col){
38785         return this.config[col].header;
38786     },
38787
38788     /**
38789      * Sets the header for a column.
38790      * @param {Number} col The column index
38791      * @param {String} header The new header
38792      */
38793     setColumnHeader : function(col, header){
38794         this.config[col].header = header;
38795         this.fireEvent("headerchange", this, col, header);
38796     },
38797
38798     /**
38799      * Returns the tooltip for the specified column.
38800      * @param {Number} col The column index
38801      * @return {String}
38802      */
38803     getColumnTooltip : function(col){
38804             return this.config[col].tooltip;
38805     },
38806     /**
38807      * Sets the tooltip for a column.
38808      * @param {Number} col The column index
38809      * @param {String} tooltip The new tooltip
38810      */
38811     setColumnTooltip : function(col, tooltip){
38812             this.config[col].tooltip = tooltip;
38813     },
38814
38815     /**
38816      * Returns the dataIndex for the specified column.
38817      * @param {Number} col The column index
38818      * @return {Number}
38819      */
38820     getDataIndex : function(col){
38821         return this.config[col].dataIndex;
38822     },
38823
38824     /**
38825      * Sets the dataIndex for a column.
38826      * @param {Number} col The column index
38827      * @param {Number} dataIndex The new dataIndex
38828      */
38829     setDataIndex : function(col, dataIndex){
38830         this.config[col].dataIndex = dataIndex;
38831     },
38832
38833     
38834     
38835     /**
38836      * Returns true if the cell is editable.
38837      * @param {Number} colIndex The column index
38838      * @param {Number} rowIndex The row index - this is nto actually used..?
38839      * @return {Boolean}
38840      */
38841     isCellEditable : function(colIndex, rowIndex){
38842         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38843     },
38844
38845     /**
38846      * Returns the editor defined for the cell/column.
38847      * return false or null to disable editing.
38848      * @param {Number} colIndex The column index
38849      * @param {Number} rowIndex The row index
38850      * @return {Object}
38851      */
38852     getCellEditor : function(colIndex, rowIndex){
38853         return this.config[colIndex].editor;
38854     },
38855
38856     /**
38857      * Sets if a column is editable.
38858      * @param {Number} col The column index
38859      * @param {Boolean} editable True if the column is editable
38860      */
38861     setEditable : function(col, editable){
38862         this.config[col].editable = editable;
38863     },
38864
38865
38866     /**
38867      * Returns true if the column is hidden.
38868      * @param {Number} colIndex The column index
38869      * @return {Boolean}
38870      */
38871     isHidden : function(colIndex){
38872         return this.config[colIndex].hidden;
38873     },
38874
38875
38876     /**
38877      * Returns true if the column width cannot be changed
38878      */
38879     isFixed : function(colIndex){
38880         return this.config[colIndex].fixed;
38881     },
38882
38883     /**
38884      * Returns true if the column can be resized
38885      * @return {Boolean}
38886      */
38887     isResizable : function(colIndex){
38888         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38889     },
38890     /**
38891      * Sets if a column is hidden.
38892      * @param {Number} colIndex The column index
38893      * @param {Boolean} hidden True if the column is hidden
38894      */
38895     setHidden : function(colIndex, hidden){
38896         this.config[colIndex].hidden = hidden;
38897         this.totalWidth = null;
38898         this.fireEvent("hiddenchange", this, colIndex, hidden);
38899     },
38900
38901     /**
38902      * Sets the editor for a column.
38903      * @param {Number} col The column index
38904      * @param {Object} editor The editor object
38905      */
38906     setEditor : function(col, editor){
38907         this.config[col].editor = editor;
38908     },
38909     /**
38910      * Add a column (experimental...) - defaults to adding to the end..
38911      * @param {Object} config 
38912     */
38913     addColumn : function(c)
38914     {
38915     
38916         var i = this.config.length;
38917         this.config[i] = c;
38918         
38919         if(typeof c.dataIndex == "undefined"){
38920             c.dataIndex = i;
38921         }
38922         if(typeof c.renderer == "string"){
38923             c.renderer = Roo.util.Format[c.renderer];
38924         }
38925         if(typeof c.id == "undefined"){
38926             c.id = Roo.id();
38927         }
38928         if(c.editor && c.editor.xtype){
38929             c.editor  = Roo.factory(c.editor, Roo.grid);
38930         }
38931         if(c.editor && c.editor.isFormField){
38932             c.editor = new Roo.grid.GridEditor(c.editor);
38933         }
38934         this.lookup[c.id] = c;
38935     }
38936     
38937 });
38938
38939 Roo.grid.ColumnModel.defaultRenderer = function(value)
38940 {
38941     if(typeof value == "object") {
38942         return value;
38943     }
38944         if(typeof value == "string" && value.length < 1){
38945             return "&#160;";
38946         }
38947     
38948         return String.format("{0}", value);
38949 };
38950
38951 // Alias for backwards compatibility
38952 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38953 /*
38954  * Based on:
38955  * Ext JS Library 1.1.1
38956  * Copyright(c) 2006-2007, Ext JS, LLC.
38957  *
38958  * Originally Released Under LGPL - original licence link has changed is not relivant.
38959  *
38960  * Fork - LGPL
38961  * <script type="text/javascript">
38962  */
38963
38964 /**
38965  * @class Roo.grid.AbstractSelectionModel
38966  * @extends Roo.util.Observable
38967  * @abstract
38968  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38969  * implemented by descendant classes.  This class should not be directly instantiated.
38970  * @constructor
38971  */
38972 Roo.grid.AbstractSelectionModel = function(){
38973     this.locked = false;
38974     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38975 };
38976
38977 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38978     /** @ignore Called by the grid automatically. Do not call directly. */
38979     init : function(grid){
38980         this.grid = grid;
38981         this.initEvents();
38982     },
38983
38984     /**
38985      * Locks the selections.
38986      */
38987     lock : function(){
38988         this.locked = true;
38989     },
38990
38991     /**
38992      * Unlocks the selections.
38993      */
38994     unlock : function(){
38995         this.locked = false;
38996     },
38997
38998     /**
38999      * Returns true if the selections are locked.
39000      * @return {Boolean}
39001      */
39002     isLocked : function(){
39003         return this.locked;
39004     }
39005 });/*
39006  * Based on:
39007  * Ext JS Library 1.1.1
39008  * Copyright(c) 2006-2007, Ext JS, LLC.
39009  *
39010  * Originally Released Under LGPL - original licence link has changed is not relivant.
39011  *
39012  * Fork - LGPL
39013  * <script type="text/javascript">
39014  */
39015 /**
39016  * @extends Roo.grid.AbstractSelectionModel
39017  * @class Roo.grid.RowSelectionModel
39018  * The default SelectionModel used by {@link Roo.grid.Grid}.
39019  * It supports multiple selections and keyboard selection/navigation. 
39020  * @constructor
39021  * @param {Object} config
39022  */
39023 Roo.grid.RowSelectionModel = function(config){
39024     Roo.apply(this, config);
39025     this.selections = new Roo.util.MixedCollection(false, function(o){
39026         return o.id;
39027     });
39028
39029     this.last = false;
39030     this.lastActive = false;
39031
39032     this.addEvents({
39033         /**
39034         * @event selectionchange
39035         * Fires when the selection changes
39036         * @param {SelectionModel} this
39037         */
39038        "selectionchange" : true,
39039        /**
39040         * @event afterselectionchange
39041         * Fires after the selection changes (eg. by key press or clicking)
39042         * @param {SelectionModel} this
39043         */
39044        "afterselectionchange" : true,
39045        /**
39046         * @event beforerowselect
39047         * Fires when a row is selected being selected, return false to cancel.
39048         * @param {SelectionModel} this
39049         * @param {Number} rowIndex The selected index
39050         * @param {Boolean} keepExisting False if other selections will be cleared
39051         */
39052        "beforerowselect" : true,
39053        /**
39054         * @event rowselect
39055         * Fires when a row is selected.
39056         * @param {SelectionModel} this
39057         * @param {Number} rowIndex The selected index
39058         * @param {Roo.data.Record} r The record
39059         */
39060        "rowselect" : true,
39061        /**
39062         * @event rowdeselect
39063         * Fires when a row is deselected.
39064         * @param {SelectionModel} this
39065         * @param {Number} rowIndex The selected index
39066         */
39067         "rowdeselect" : true
39068     });
39069     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39070     this.locked = false;
39071 };
39072
39073 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39074     /**
39075      * @cfg {Boolean} singleSelect
39076      * True to allow selection of only one row at a time (defaults to false)
39077      */
39078     singleSelect : false,
39079
39080     // private
39081     initEvents : function(){
39082
39083         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39084             this.grid.on("mousedown", this.handleMouseDown, this);
39085         }else{ // allow click to work like normal
39086             this.grid.on("rowclick", this.handleDragableRowClick, this);
39087         }
39088         // bootstrap does not have a view..
39089         var view = this.grid.view ? this.grid.view : this.grid;
39090         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39091             "up" : function(e){
39092                 if(!e.shiftKey){
39093                     this.selectPrevious(e.shiftKey);
39094                 }else if(this.last !== false && this.lastActive !== false){
39095                     var last = this.last;
39096                     this.selectRange(this.last,  this.lastActive-1);
39097                     view.focusRow(this.lastActive);
39098                     if(last !== false){
39099                         this.last = last;
39100                     }
39101                 }else{
39102                     this.selectFirstRow();
39103                 }
39104                 this.fireEvent("afterselectionchange", this);
39105             },
39106             "down" : function(e){
39107                 if(!e.shiftKey){
39108                     this.selectNext(e.shiftKey);
39109                 }else if(this.last !== false && this.lastActive !== false){
39110                     var last = this.last;
39111                     this.selectRange(this.last,  this.lastActive+1);
39112                     view.focusRow(this.lastActive);
39113                     if(last !== false){
39114                         this.last = last;
39115                     }
39116                 }else{
39117                     this.selectFirstRow();
39118                 }
39119                 this.fireEvent("afterselectionchange", this);
39120             },
39121             scope: this
39122         });
39123
39124          
39125         view.on("refresh", this.onRefresh, this);
39126         view.on("rowupdated", this.onRowUpdated, this);
39127         view.on("rowremoved", this.onRemove, this);
39128     },
39129
39130     // private
39131     onRefresh : function(){
39132         var ds = this.grid.ds, i, v = this.grid.view;
39133         var s = this.selections;
39134         s.each(function(r){
39135             if((i = ds.indexOfId(r.id)) != -1){
39136                 v.onRowSelect(i);
39137                 s.add(ds.getAt(i)); // updating the selection relate data
39138             }else{
39139                 s.remove(r);
39140             }
39141         });
39142     },
39143
39144     // private
39145     onRemove : function(v, index, r){
39146         this.selections.remove(r);
39147     },
39148
39149     // private
39150     onRowUpdated : function(v, index, r){
39151         if(this.isSelected(r)){
39152             v.onRowSelect(index);
39153         }
39154     },
39155
39156     /**
39157      * Select records.
39158      * @param {Array} records The records to select
39159      * @param {Boolean} keepExisting (optional) True to keep existing selections
39160      */
39161     selectRecords : function(records, keepExisting){
39162         if(!keepExisting){
39163             this.clearSelections();
39164         }
39165         var ds = this.grid.ds;
39166         for(var i = 0, len = records.length; i < len; i++){
39167             this.selectRow(ds.indexOf(records[i]), true);
39168         }
39169     },
39170
39171     /**
39172      * Gets the number of selected rows.
39173      * @return {Number}
39174      */
39175     getCount : function(){
39176         return this.selections.length;
39177     },
39178
39179     /**
39180      * Selects the first row in the grid.
39181      */
39182     selectFirstRow : function(){
39183         this.selectRow(0);
39184     },
39185
39186     /**
39187      * Select the last row.
39188      * @param {Boolean} keepExisting (optional) True to keep existing selections
39189      */
39190     selectLastRow : function(keepExisting){
39191         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
39192     },
39193
39194     /**
39195      * Selects the row immediately following the last selected row.
39196      * @param {Boolean} keepExisting (optional) True to keep existing selections
39197      */
39198     selectNext : function(keepExisting){
39199         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
39200             this.selectRow(this.last+1, keepExisting);
39201             var view = this.grid.view ? this.grid.view : this.grid;
39202             view.focusRow(this.last);
39203         }
39204     },
39205
39206     /**
39207      * Selects the row that precedes the last selected row.
39208      * @param {Boolean} keepExisting (optional) True to keep existing selections
39209      */
39210     selectPrevious : function(keepExisting){
39211         if(this.last){
39212             this.selectRow(this.last-1, keepExisting);
39213             var view = this.grid.view ? this.grid.view : this.grid;
39214             view.focusRow(this.last);
39215         }
39216     },
39217
39218     /**
39219      * Returns the selected records
39220      * @return {Array} Array of selected records
39221      */
39222     getSelections : function(){
39223         return [].concat(this.selections.items);
39224     },
39225
39226     /**
39227      * Returns the first selected record.
39228      * @return {Record}
39229      */
39230     getSelected : function(){
39231         return this.selections.itemAt(0);
39232     },
39233
39234
39235     /**
39236      * Clears all selections.
39237      */
39238     clearSelections : function(fast){
39239         if(this.locked) {
39240             return;
39241         }
39242         if(fast !== true){
39243             var ds = this.grid.ds;
39244             var s = this.selections;
39245             s.each(function(r){
39246                 this.deselectRow(ds.indexOfId(r.id));
39247             }, this);
39248             s.clear();
39249         }else{
39250             this.selections.clear();
39251         }
39252         this.last = false;
39253     },
39254
39255
39256     /**
39257      * Selects all rows.
39258      */
39259     selectAll : function(){
39260         if(this.locked) {
39261             return;
39262         }
39263         this.selections.clear();
39264         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
39265             this.selectRow(i, true);
39266         }
39267     },
39268
39269     /**
39270      * Returns True if there is a selection.
39271      * @return {Boolean}
39272      */
39273     hasSelection : function(){
39274         return this.selections.length > 0;
39275     },
39276
39277     /**
39278      * Returns True if the specified row is selected.
39279      * @param {Number/Record} record The record or index of the record to check
39280      * @return {Boolean}
39281      */
39282     isSelected : function(index){
39283         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
39284         return (r && this.selections.key(r.id) ? true : false);
39285     },
39286
39287     /**
39288      * Returns True if the specified record id is selected.
39289      * @param {String} id The id of record to check
39290      * @return {Boolean}
39291      */
39292     isIdSelected : function(id){
39293         return (this.selections.key(id) ? true : false);
39294     },
39295
39296     // private
39297     handleMouseDown : function(e, t)
39298     {
39299         var view = this.grid.view ? this.grid.view : this.grid;
39300         var rowIndex;
39301         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39302             return;
39303         };
39304         if(e.shiftKey && this.last !== false){
39305             var last = this.last;
39306             this.selectRange(last, rowIndex, e.ctrlKey);
39307             this.last = last; // reset the last
39308             view.focusRow(rowIndex);
39309         }else{
39310             var isSelected = this.isSelected(rowIndex);
39311             if(e.button !== 0 && isSelected){
39312                 view.focusRow(rowIndex);
39313             }else if(e.ctrlKey && isSelected){
39314                 this.deselectRow(rowIndex);
39315             }else if(!isSelected){
39316                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39317                 view.focusRow(rowIndex);
39318             }
39319         }
39320         this.fireEvent("afterselectionchange", this);
39321     },
39322     // private
39323     handleDragableRowClick :  function(grid, rowIndex, e) 
39324     {
39325         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39326             this.selectRow(rowIndex, false);
39327             var view = this.grid.view ? this.grid.view : this.grid;
39328             view.focusRow(rowIndex);
39329              this.fireEvent("afterselectionchange", this);
39330         }
39331     },
39332     
39333     /**
39334      * Selects multiple rows.
39335      * @param {Array} rows Array of the indexes of the row to select
39336      * @param {Boolean} keepExisting (optional) True to keep existing selections
39337      */
39338     selectRows : function(rows, keepExisting){
39339         if(!keepExisting){
39340             this.clearSelections();
39341         }
39342         for(var i = 0, len = rows.length; i < len; i++){
39343             this.selectRow(rows[i], true);
39344         }
39345     },
39346
39347     /**
39348      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39349      * @param {Number} startRow The index of the first row in the range
39350      * @param {Number} endRow The index of the last row in the range
39351      * @param {Boolean} keepExisting (optional) True to retain existing selections
39352      */
39353     selectRange : function(startRow, endRow, keepExisting){
39354         if(this.locked) {
39355             return;
39356         }
39357         if(!keepExisting){
39358             this.clearSelections();
39359         }
39360         if(startRow <= endRow){
39361             for(var i = startRow; i <= endRow; i++){
39362                 this.selectRow(i, true);
39363             }
39364         }else{
39365             for(var i = startRow; i >= endRow; i--){
39366                 this.selectRow(i, true);
39367             }
39368         }
39369     },
39370
39371     /**
39372      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39373      * @param {Number} startRow The index of the first row in the range
39374      * @param {Number} endRow The index of the last row in the range
39375      */
39376     deselectRange : function(startRow, endRow, preventViewNotify){
39377         if(this.locked) {
39378             return;
39379         }
39380         for(var i = startRow; i <= endRow; i++){
39381             this.deselectRow(i, preventViewNotify);
39382         }
39383     },
39384
39385     /**
39386      * Selects a row.
39387      * @param {Number} row The index of the row to select
39388      * @param {Boolean} keepExisting (optional) True to keep existing selections
39389      */
39390     selectRow : function(index, keepExisting, preventViewNotify){
39391         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
39392             return;
39393         }
39394         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39395             if(!keepExisting || this.singleSelect){
39396                 this.clearSelections();
39397             }
39398             var r = this.grid.ds.getAt(index);
39399             this.selections.add(r);
39400             this.last = this.lastActive = index;
39401             if(!preventViewNotify){
39402                 var view = this.grid.view ? this.grid.view : this.grid;
39403                 view.onRowSelect(index);
39404             }
39405             this.fireEvent("rowselect", this, index, r);
39406             this.fireEvent("selectionchange", this);
39407         }
39408     },
39409
39410     /**
39411      * Deselects a row.
39412      * @param {Number} row The index of the row to deselect
39413      */
39414     deselectRow : function(index, preventViewNotify){
39415         if(this.locked) {
39416             return;
39417         }
39418         if(this.last == index){
39419             this.last = false;
39420         }
39421         if(this.lastActive == index){
39422             this.lastActive = false;
39423         }
39424         var r = this.grid.ds.getAt(index);
39425         this.selections.remove(r);
39426         if(!preventViewNotify){
39427             var view = this.grid.view ? this.grid.view : this.grid;
39428             view.onRowDeselect(index);
39429         }
39430         this.fireEvent("rowdeselect", this, index);
39431         this.fireEvent("selectionchange", this);
39432     },
39433
39434     // private
39435     restoreLast : function(){
39436         if(this._last){
39437             this.last = this._last;
39438         }
39439     },
39440
39441     // private
39442     acceptsNav : function(row, col, cm){
39443         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39444     },
39445
39446     // private
39447     onEditorKey : function(field, e){
39448         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39449         if(k == e.TAB){
39450             e.stopEvent();
39451             ed.completeEdit();
39452             if(e.shiftKey){
39453                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39454             }else{
39455                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39456             }
39457         }else if(k == e.ENTER && !e.ctrlKey){
39458             e.stopEvent();
39459             ed.completeEdit();
39460             if(e.shiftKey){
39461                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39462             }else{
39463                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39464             }
39465         }else if(k == e.ESC){
39466             ed.cancelEdit();
39467         }
39468         if(newCell){
39469             g.startEditing(newCell[0], newCell[1]);
39470         }
39471     }
39472 });/*
39473  * Based on:
39474  * Ext JS Library 1.1.1
39475  * Copyright(c) 2006-2007, Ext JS, LLC.
39476  *
39477  * Originally Released Under LGPL - original licence link has changed is not relivant.
39478  *
39479  * Fork - LGPL
39480  * <script type="text/javascript">
39481  */
39482 /**
39483  * @class Roo.grid.CellSelectionModel
39484  * @extends Roo.grid.AbstractSelectionModel
39485  * This class provides the basic implementation for cell selection in a grid.
39486  * @constructor
39487  * @param {Object} config The object containing the configuration of this model.
39488  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39489  */
39490 Roo.grid.CellSelectionModel = function(config){
39491     Roo.apply(this, config);
39492
39493     this.selection = null;
39494
39495     this.addEvents({
39496         /**
39497              * @event beforerowselect
39498              * Fires before a cell is selected.
39499              * @param {SelectionModel} this
39500              * @param {Number} rowIndex The selected row index
39501              * @param {Number} colIndex The selected cell index
39502              */
39503             "beforecellselect" : true,
39504         /**
39505              * @event cellselect
39506              * Fires when a cell is selected.
39507              * @param {SelectionModel} this
39508              * @param {Number} rowIndex The selected row index
39509              * @param {Number} colIndex The selected cell index
39510              */
39511             "cellselect" : true,
39512         /**
39513              * @event selectionchange
39514              * Fires when the active selection changes.
39515              * @param {SelectionModel} this
39516              * @param {Object} selection null for no selection or an object (o) with two properties
39517                 <ul>
39518                 <li>o.record: the record object for the row the selection is in</li>
39519                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39520                 </ul>
39521              */
39522             "selectionchange" : true,
39523         /**
39524              * @event tabend
39525              * Fires when the tab (or enter) was pressed on the last editable cell
39526              * You can use this to trigger add new row.
39527              * @param {SelectionModel} this
39528              */
39529             "tabend" : true,
39530          /**
39531              * @event beforeeditnext
39532              * Fires before the next editable sell is made active
39533              * You can use this to skip to another cell or fire the tabend
39534              *    if you set cell to false
39535              * @param {Object} eventdata object : { cell : [ row, col ] } 
39536              */
39537             "beforeeditnext" : true
39538     });
39539     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39540 };
39541
39542 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39543     
39544     enter_is_tab: false,
39545
39546     /** @ignore */
39547     initEvents : function(){
39548         this.grid.on("mousedown", this.handleMouseDown, this);
39549         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39550         var view = this.grid.view;
39551         view.on("refresh", this.onViewChange, this);
39552         view.on("rowupdated", this.onRowUpdated, this);
39553         view.on("beforerowremoved", this.clearSelections, this);
39554         view.on("beforerowsinserted", this.clearSelections, this);
39555         if(this.grid.isEditor){
39556             this.grid.on("beforeedit", this.beforeEdit,  this);
39557         }
39558     },
39559
39560         //private
39561     beforeEdit : function(e){
39562         this.select(e.row, e.column, false, true, e.record);
39563     },
39564
39565         //private
39566     onRowUpdated : function(v, index, r){
39567         if(this.selection && this.selection.record == r){
39568             v.onCellSelect(index, this.selection.cell[1]);
39569         }
39570     },
39571
39572         //private
39573     onViewChange : function(){
39574         this.clearSelections(true);
39575     },
39576
39577         /**
39578          * Returns the currently selected cell,.
39579          * @return {Array} The selected cell (row, column) or null if none selected.
39580          */
39581     getSelectedCell : function(){
39582         return this.selection ? this.selection.cell : null;
39583     },
39584
39585     /**
39586      * Clears all selections.
39587      * @param {Boolean} true to prevent the gridview from being notified about the change.
39588      */
39589     clearSelections : function(preventNotify){
39590         var s = this.selection;
39591         if(s){
39592             if(preventNotify !== true){
39593                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39594             }
39595             this.selection = null;
39596             this.fireEvent("selectionchange", this, null);
39597         }
39598     },
39599
39600     /**
39601      * Returns true if there is a selection.
39602      * @return {Boolean}
39603      */
39604     hasSelection : function(){
39605         return this.selection ? true : false;
39606     },
39607
39608     /** @ignore */
39609     handleMouseDown : function(e, t){
39610         var v = this.grid.getView();
39611         if(this.isLocked()){
39612             return;
39613         };
39614         var row = v.findRowIndex(t);
39615         var cell = v.findCellIndex(t);
39616         if(row !== false && cell !== false){
39617             this.select(row, cell);
39618         }
39619     },
39620
39621     /**
39622      * Selects a cell.
39623      * @param {Number} rowIndex
39624      * @param {Number} collIndex
39625      */
39626     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39627         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39628             this.clearSelections();
39629             r = r || this.grid.dataSource.getAt(rowIndex);
39630             this.selection = {
39631                 record : r,
39632                 cell : [rowIndex, colIndex]
39633             };
39634             if(!preventViewNotify){
39635                 var v = this.grid.getView();
39636                 v.onCellSelect(rowIndex, colIndex);
39637                 if(preventFocus !== true){
39638                     v.focusCell(rowIndex, colIndex);
39639                 }
39640             }
39641             this.fireEvent("cellselect", this, rowIndex, colIndex);
39642             this.fireEvent("selectionchange", this, this.selection);
39643         }
39644     },
39645
39646         //private
39647     isSelectable : function(rowIndex, colIndex, cm){
39648         return !cm.isHidden(colIndex);
39649     },
39650
39651     /** @ignore */
39652     handleKeyDown : function(e){
39653         //Roo.log('Cell Sel Model handleKeyDown');
39654         if(!e.isNavKeyPress()){
39655             return;
39656         }
39657         var g = this.grid, s = this.selection;
39658         if(!s){
39659             e.stopEvent();
39660             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39661             if(cell){
39662                 this.select(cell[0], cell[1]);
39663             }
39664             return;
39665         }
39666         var sm = this;
39667         var walk = function(row, col, step){
39668             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39669         };
39670         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39671         var newCell;
39672
39673       
39674
39675         switch(k){
39676             case e.TAB:
39677                 // handled by onEditorKey
39678                 if (g.isEditor && g.editing) {
39679                     return;
39680                 }
39681                 if(e.shiftKey) {
39682                     newCell = walk(r, c-1, -1);
39683                 } else {
39684                     newCell = walk(r, c+1, 1);
39685                 }
39686                 break;
39687             
39688             case e.DOWN:
39689                newCell = walk(r+1, c, 1);
39690                 break;
39691             
39692             case e.UP:
39693                 newCell = walk(r-1, c, -1);
39694                 break;
39695             
39696             case e.RIGHT:
39697                 newCell = walk(r, c+1, 1);
39698                 break;
39699             
39700             case e.LEFT:
39701                 newCell = walk(r, c-1, -1);
39702                 break;
39703             
39704             case e.ENTER:
39705                 
39706                 if(g.isEditor && !g.editing){
39707                    g.startEditing(r, c);
39708                    e.stopEvent();
39709                    return;
39710                 }
39711                 
39712                 
39713              break;
39714         };
39715         if(newCell){
39716             this.select(newCell[0], newCell[1]);
39717             e.stopEvent();
39718             
39719         }
39720     },
39721
39722     acceptsNav : function(row, col, cm){
39723         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39724     },
39725     /**
39726      * Selects a cell.
39727      * @param {Number} field (not used) - as it's normally used as a listener
39728      * @param {Number} e - event - fake it by using
39729      *
39730      * var e = Roo.EventObjectImpl.prototype;
39731      * e.keyCode = e.TAB
39732      *
39733      * 
39734      */
39735     onEditorKey : function(field, e){
39736         
39737         var k = e.getKey(),
39738             newCell,
39739             g = this.grid,
39740             ed = g.activeEditor,
39741             forward = false;
39742         ///Roo.log('onEditorKey' + k);
39743         
39744         
39745         if (this.enter_is_tab && k == e.ENTER) {
39746             k = e.TAB;
39747         }
39748         
39749         if(k == e.TAB){
39750             if(e.shiftKey){
39751                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39752             }else{
39753                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39754                 forward = true;
39755             }
39756             
39757             e.stopEvent();
39758             
39759         } else if(k == e.ENTER &&  !e.ctrlKey){
39760             ed.completeEdit();
39761             e.stopEvent();
39762             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39763         
39764                 } else if(k == e.ESC){
39765             ed.cancelEdit();
39766         }
39767                 
39768         if (newCell) {
39769             var ecall = { cell : newCell, forward : forward };
39770             this.fireEvent('beforeeditnext', ecall );
39771             newCell = ecall.cell;
39772                         forward = ecall.forward;
39773         }
39774                 
39775         if(newCell){
39776             //Roo.log('next cell after edit');
39777             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39778         } else if (forward) {
39779             // tabbed past last
39780             this.fireEvent.defer(100, this, ['tabend',this]);
39781         }
39782     }
39783 });/*
39784  * Based on:
39785  * Ext JS Library 1.1.1
39786  * Copyright(c) 2006-2007, Ext JS, LLC.
39787  *
39788  * Originally Released Under LGPL - original licence link has changed is not relivant.
39789  *
39790  * Fork - LGPL
39791  * <script type="text/javascript">
39792  */
39793  
39794 /**
39795  * @class Roo.grid.EditorGrid
39796  * @extends Roo.grid.Grid
39797  * Class for creating and editable grid.
39798  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39799  * The container MUST have some type of size defined for the grid to fill. The container will be 
39800  * automatically set to position relative if it isn't already.
39801  * @param {Object} dataSource The data model to bind to
39802  * @param {Object} colModel The column model with info about this grid's columns
39803  */
39804 Roo.grid.EditorGrid = function(container, config){
39805     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39806     this.getGridEl().addClass("xedit-grid");
39807
39808     if(!this.selModel){
39809         this.selModel = new Roo.grid.CellSelectionModel();
39810     }
39811
39812     this.activeEditor = null;
39813
39814         this.addEvents({
39815             /**
39816              * @event beforeedit
39817              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39818              * <ul style="padding:5px;padding-left:16px;">
39819              * <li>grid - This grid</li>
39820              * <li>record - The record being edited</li>
39821              * <li>field - The field name being edited</li>
39822              * <li>value - The value for the field being edited.</li>
39823              * <li>row - The grid row index</li>
39824              * <li>column - The grid column index</li>
39825              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39826              * </ul>
39827              * @param {Object} e An edit event (see above for description)
39828              */
39829             "beforeedit" : true,
39830             /**
39831              * @event afteredit
39832              * Fires after a cell is edited. <br />
39833              * <ul style="padding:5px;padding-left:16px;">
39834              * <li>grid - This grid</li>
39835              * <li>record - The record being edited</li>
39836              * <li>field - The field name being edited</li>
39837              * <li>value - The value being set</li>
39838              * <li>originalValue - The original value for the field, before the edit.</li>
39839              * <li>row - The grid row index</li>
39840              * <li>column - The grid column index</li>
39841              * </ul>
39842              * @param {Object} e An edit event (see above for description)
39843              */
39844             "afteredit" : true,
39845             /**
39846              * @event validateedit
39847              * Fires after a cell is edited, but before the value is set in the record. 
39848          * You can use this to modify the value being set in the field, Return false
39849              * to cancel the change. The edit event object has the following properties <br />
39850              * <ul style="padding:5px;padding-left:16px;">
39851          * <li>editor - This editor</li>
39852              * <li>grid - This grid</li>
39853              * <li>record - The record being edited</li>
39854              * <li>field - The field name being edited</li>
39855              * <li>value - The value being set</li>
39856              * <li>originalValue - The original value for the field, before the edit.</li>
39857              * <li>row - The grid row index</li>
39858              * <li>column - The grid column index</li>
39859              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39860              * </ul>
39861              * @param {Object} e An edit event (see above for description)
39862              */
39863             "validateedit" : true
39864         });
39865     this.on("bodyscroll", this.stopEditing,  this);
39866     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39867 };
39868
39869 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39870     /**
39871      * @cfg {Number} clicksToEdit
39872      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39873      */
39874     clicksToEdit: 2,
39875
39876     // private
39877     isEditor : true,
39878     // private
39879     trackMouseOver: false, // causes very odd FF errors
39880
39881     onCellDblClick : function(g, row, col){
39882         this.startEditing(row, col);
39883     },
39884
39885     onEditComplete : function(ed, value, startValue){
39886         this.editing = false;
39887         this.activeEditor = null;
39888         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39889         var r = ed.record;
39890         var field = this.colModel.getDataIndex(ed.col);
39891         var e = {
39892             grid: this,
39893             record: r,
39894             field: field,
39895             originalValue: startValue,
39896             value: value,
39897             row: ed.row,
39898             column: ed.col,
39899             cancel:false,
39900             editor: ed
39901         };
39902         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
39903         cell.show();
39904           
39905         if(String(value) !== String(startValue)){
39906             
39907             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39908                 r.set(field, e.value);
39909                 // if we are dealing with a combo box..
39910                 // then we also set the 'name' colum to be the displayField
39911                 if (ed.field.displayField && ed.field.name) {
39912                     r.set(ed.field.name, ed.field.el.dom.value);
39913                 }
39914                 
39915                 delete e.cancel; //?? why!!!
39916                 this.fireEvent("afteredit", e);
39917             }
39918         } else {
39919             this.fireEvent("afteredit", e); // always fire it!
39920         }
39921         this.view.focusCell(ed.row, ed.col);
39922     },
39923
39924     /**
39925      * Starts editing the specified for the specified row/column
39926      * @param {Number} rowIndex
39927      * @param {Number} colIndex
39928      */
39929     startEditing : function(row, col){
39930         this.stopEditing();
39931         if(this.colModel.isCellEditable(col, row)){
39932             this.view.ensureVisible(row, col, true);
39933           
39934             var r = this.dataSource.getAt(row);
39935             var field = this.colModel.getDataIndex(col);
39936             var cell = Roo.get(this.view.getCell(row,col));
39937             var e = {
39938                 grid: this,
39939                 record: r,
39940                 field: field,
39941                 value: r.data[field],
39942                 row: row,
39943                 column: col,
39944                 cancel:false 
39945             };
39946             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39947                 this.editing = true;
39948                 var ed = this.colModel.getCellEditor(col, row);
39949                 
39950                 if (!ed) {
39951                     return;
39952                 }
39953                 if(!ed.rendered){
39954                     ed.render(ed.parentEl || document.body);
39955                 }
39956                 ed.field.reset();
39957                
39958                 cell.hide();
39959                 
39960                 (function(){ // complex but required for focus issues in safari, ie and opera
39961                     ed.row = row;
39962                     ed.col = col;
39963                     ed.record = r;
39964                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39965                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39966                     this.activeEditor = ed;
39967                     var v = r.data[field];
39968                     ed.startEdit(this.view.getCell(row, col), v);
39969                     // combo's with 'displayField and name set
39970                     if (ed.field.displayField && ed.field.name) {
39971                         ed.field.el.dom.value = r.data[ed.field.name];
39972                     }
39973                     
39974                     
39975                 }).defer(50, this);
39976             }
39977         }
39978     },
39979         
39980     /**
39981      * Stops any active editing
39982      */
39983     stopEditing : function(){
39984         if(this.activeEditor){
39985             this.activeEditor.completeEdit();
39986         }
39987         this.activeEditor = null;
39988     },
39989         
39990          /**
39991      * Called to get grid's drag proxy text, by default returns this.ddText.
39992      * @return {String}
39993      */
39994     getDragDropText : function(){
39995         var count = this.selModel.getSelectedCell() ? 1 : 0;
39996         return String.format(this.ddText, count, count == 1 ? '' : 's');
39997     }
39998         
39999 });/*
40000  * Based on:
40001  * Ext JS Library 1.1.1
40002  * Copyright(c) 2006-2007, Ext JS, LLC.
40003  *
40004  * Originally Released Under LGPL - original licence link has changed is not relivant.
40005  *
40006  * Fork - LGPL
40007  * <script type="text/javascript">
40008  */
40009
40010 // private - not really -- you end up using it !
40011 // This is a support class used internally by the Grid components
40012
40013 /**
40014  * @class Roo.grid.GridEditor
40015  * @extends Roo.Editor
40016  * Class for creating and editable grid elements.
40017  * @param {Object} config any settings (must include field)
40018  */
40019 Roo.grid.GridEditor = function(field, config){
40020     if (!config && field.field) {
40021         config = field;
40022         field = Roo.factory(config.field, Roo.form);
40023     }
40024     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40025     field.monitorTab = false;
40026 };
40027
40028 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40029     
40030     /**
40031      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40032      */
40033     
40034     alignment: "tl-tl",
40035     autoSize: "width",
40036     hideEl : false,
40037     cls: "x-small-editor x-grid-editor",
40038     shim:false,
40039     shadow:"frame"
40040 });/*
40041  * Based on:
40042  * Ext JS Library 1.1.1
40043  * Copyright(c) 2006-2007, Ext JS, LLC.
40044  *
40045  * Originally Released Under LGPL - original licence link has changed is not relivant.
40046  *
40047  * Fork - LGPL
40048  * <script type="text/javascript">
40049  */
40050   
40051
40052   
40053 Roo.grid.PropertyRecord = Roo.data.Record.create([
40054     {name:'name',type:'string'},  'value'
40055 ]);
40056
40057
40058 Roo.grid.PropertyStore = function(grid, source){
40059     this.grid = grid;
40060     this.store = new Roo.data.Store({
40061         recordType : Roo.grid.PropertyRecord
40062     });
40063     this.store.on('update', this.onUpdate,  this);
40064     if(source){
40065         this.setSource(source);
40066     }
40067     Roo.grid.PropertyStore.superclass.constructor.call(this);
40068 };
40069
40070
40071
40072 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40073     setSource : function(o){
40074         this.source = o;
40075         this.store.removeAll();
40076         var data = [];
40077         for(var k in o){
40078             if(this.isEditableValue(o[k])){
40079                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40080             }
40081         }
40082         this.store.loadRecords({records: data}, {}, true);
40083     },
40084
40085     onUpdate : function(ds, record, type){
40086         if(type == Roo.data.Record.EDIT){
40087             var v = record.data['value'];
40088             var oldValue = record.modified['value'];
40089             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40090                 this.source[record.id] = v;
40091                 record.commit();
40092                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40093             }else{
40094                 record.reject();
40095             }
40096         }
40097     },
40098
40099     getProperty : function(row){
40100        return this.store.getAt(row);
40101     },
40102
40103     isEditableValue: function(val){
40104         if(val && val instanceof Date){
40105             return true;
40106         }else if(typeof val == 'object' || typeof val == 'function'){
40107             return false;
40108         }
40109         return true;
40110     },
40111
40112     setValue : function(prop, value){
40113         this.source[prop] = value;
40114         this.store.getById(prop).set('value', value);
40115     },
40116
40117     getSource : function(){
40118         return this.source;
40119     }
40120 });
40121
40122 Roo.grid.PropertyColumnModel = function(grid, store){
40123     this.grid = grid;
40124     var g = Roo.grid;
40125     g.PropertyColumnModel.superclass.constructor.call(this, [
40126         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40127         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40128     ]);
40129     this.store = store;
40130     this.bselect = Roo.DomHelper.append(document.body, {
40131         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40132             {tag: 'option', value: 'true', html: 'true'},
40133             {tag: 'option', value: 'false', html: 'false'}
40134         ]
40135     });
40136     Roo.id(this.bselect);
40137     var f = Roo.form;
40138     this.editors = {
40139         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40140         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40141         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40142         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40143         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40144     };
40145     this.renderCellDelegate = this.renderCell.createDelegate(this);
40146     this.renderPropDelegate = this.renderProp.createDelegate(this);
40147 };
40148
40149 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40150     
40151     
40152     nameText : 'Name',
40153     valueText : 'Value',
40154     
40155     dateFormat : 'm/j/Y',
40156     
40157     
40158     renderDate : function(dateVal){
40159         return dateVal.dateFormat(this.dateFormat);
40160     },
40161
40162     renderBool : function(bVal){
40163         return bVal ? 'true' : 'false';
40164     },
40165
40166     isCellEditable : function(colIndex, rowIndex){
40167         return colIndex == 1;
40168     },
40169
40170     getRenderer : function(col){
40171         return col == 1 ?
40172             this.renderCellDelegate : this.renderPropDelegate;
40173     },
40174
40175     renderProp : function(v){
40176         return this.getPropertyName(v);
40177     },
40178
40179     renderCell : function(val){
40180         var rv = val;
40181         if(val instanceof Date){
40182             rv = this.renderDate(val);
40183         }else if(typeof val == 'boolean'){
40184             rv = this.renderBool(val);
40185         }
40186         return Roo.util.Format.htmlEncode(rv);
40187     },
40188
40189     getPropertyName : function(name){
40190         var pn = this.grid.propertyNames;
40191         return pn && pn[name] ? pn[name] : name;
40192     },
40193
40194     getCellEditor : function(colIndex, rowIndex){
40195         var p = this.store.getProperty(rowIndex);
40196         var n = p.data['name'], val = p.data['value'];
40197         
40198         if(typeof(this.grid.customEditors[n]) == 'string'){
40199             return this.editors[this.grid.customEditors[n]];
40200         }
40201         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40202             return this.grid.customEditors[n];
40203         }
40204         if(val instanceof Date){
40205             return this.editors['date'];
40206         }else if(typeof val == 'number'){
40207             return this.editors['number'];
40208         }else if(typeof val == 'boolean'){
40209             return this.editors['boolean'];
40210         }else{
40211             return this.editors['string'];
40212         }
40213     }
40214 });
40215
40216 /**
40217  * @class Roo.grid.PropertyGrid
40218  * @extends Roo.grid.EditorGrid
40219  * This class represents the  interface of a component based property grid control.
40220  * <br><br>Usage:<pre><code>
40221  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40222       
40223  });
40224  // set any options
40225  grid.render();
40226  * </code></pre>
40227   
40228  * @constructor
40229  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40230  * The container MUST have some type of size defined for the grid to fill. The container will be
40231  * automatically set to position relative if it isn't already.
40232  * @param {Object} config A config object that sets properties on this grid.
40233  */
40234 Roo.grid.PropertyGrid = function(container, config){
40235     config = config || {};
40236     var store = new Roo.grid.PropertyStore(this);
40237     this.store = store;
40238     var cm = new Roo.grid.PropertyColumnModel(this, store);
40239     store.store.sort('name', 'ASC');
40240     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40241         ds: store.store,
40242         cm: cm,
40243         enableColLock:false,
40244         enableColumnMove:false,
40245         stripeRows:false,
40246         trackMouseOver: false,
40247         clicksToEdit:1
40248     }, config));
40249     this.getGridEl().addClass('x-props-grid');
40250     this.lastEditRow = null;
40251     this.on('columnresize', this.onColumnResize, this);
40252     this.addEvents({
40253          /**
40254              * @event beforepropertychange
40255              * Fires before a property changes (return false to stop?)
40256              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40257              * @param {String} id Record Id
40258              * @param {String} newval New Value
40259          * @param {String} oldval Old Value
40260              */
40261         "beforepropertychange": true,
40262         /**
40263              * @event propertychange
40264              * Fires after a property changes
40265              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40266              * @param {String} id Record Id
40267              * @param {String} newval New Value
40268          * @param {String} oldval Old Value
40269              */
40270         "propertychange": true
40271     });
40272     this.customEditors = this.customEditors || {};
40273 };
40274 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40275     
40276      /**
40277      * @cfg {Object} customEditors map of colnames=> custom editors.
40278      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40279      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40280      * false disables editing of the field.
40281          */
40282     
40283       /**
40284      * @cfg {Object} propertyNames map of property Names to their displayed value
40285          */
40286     
40287     render : function(){
40288         Roo.grid.PropertyGrid.superclass.render.call(this);
40289         this.autoSize.defer(100, this);
40290     },
40291
40292     autoSize : function(){
40293         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40294         if(this.view){
40295             this.view.fitColumns();
40296         }
40297     },
40298
40299     onColumnResize : function(){
40300         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40301         this.autoSize();
40302     },
40303     /**
40304      * Sets the data for the Grid
40305      * accepts a Key => Value object of all the elements avaiable.
40306      * @param {Object} data  to appear in grid.
40307      */
40308     setSource : function(source){
40309         this.store.setSource(source);
40310         //this.autoSize();
40311     },
40312     /**
40313      * Gets all the data from the grid.
40314      * @return {Object} data  data stored in grid
40315      */
40316     getSource : function(){
40317         return this.store.getSource();
40318     }
40319 });/*
40320   
40321  * Licence LGPL
40322  
40323  */
40324  
40325 /**
40326  * @class Roo.grid.Calendar
40327  * @extends Roo.grid.Grid
40328  * This class extends the Grid to provide a calendar widget
40329  * <br><br>Usage:<pre><code>
40330  var grid = new Roo.grid.Calendar("my-container-id", {
40331      ds: myDataStore,
40332      cm: myColModel,
40333      selModel: mySelectionModel,
40334      autoSizeColumns: true,
40335      monitorWindowResize: false,
40336      trackMouseOver: true
40337      eventstore : real data store..
40338  });
40339  // set any options
40340  grid.render();
40341   
40342   * @constructor
40343  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40344  * The container MUST have some type of size defined for the grid to fill. The container will be
40345  * automatically set to position relative if it isn't already.
40346  * @param {Object} config A config object that sets properties on this grid.
40347  */
40348 Roo.grid.Calendar = function(container, config){
40349         // initialize the container
40350         this.container = Roo.get(container);
40351         this.container.update("");
40352         this.container.setStyle("overflow", "hidden");
40353     this.container.addClass('x-grid-container');
40354
40355     this.id = this.container.id;
40356
40357     Roo.apply(this, config);
40358     // check and correct shorthanded configs
40359     
40360     var rows = [];
40361     var d =1;
40362     for (var r = 0;r < 6;r++) {
40363         
40364         rows[r]=[];
40365         for (var c =0;c < 7;c++) {
40366             rows[r][c]= '';
40367         }
40368     }
40369     if (this.eventStore) {
40370         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40371         this.eventStore.on('load',this.onLoad, this);
40372         this.eventStore.on('beforeload',this.clearEvents, this);
40373          
40374     }
40375     
40376     this.dataSource = new Roo.data.Store({
40377             proxy: new Roo.data.MemoryProxy(rows),
40378             reader: new Roo.data.ArrayReader({}, [
40379                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40380     });
40381
40382     this.dataSource.load();
40383     this.ds = this.dataSource;
40384     this.ds.xmodule = this.xmodule || false;
40385     
40386     
40387     var cellRender = function(v,x,r)
40388     {
40389         return String.format(
40390             '<div class="fc-day  fc-widget-content"><div>' +
40391                 '<div class="fc-event-container"></div>' +
40392                 '<div class="fc-day-number">{0}</div>'+
40393                 
40394                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40395             '</div></div>', v);
40396     
40397     }
40398     
40399     
40400     this.colModel = new Roo.grid.ColumnModel( [
40401         {
40402             xtype: 'ColumnModel',
40403             xns: Roo.grid,
40404             dataIndex : 'weekday0',
40405             header : 'Sunday',
40406             renderer : cellRender
40407         },
40408         {
40409             xtype: 'ColumnModel',
40410             xns: Roo.grid,
40411             dataIndex : 'weekday1',
40412             header : 'Monday',
40413             renderer : cellRender
40414         },
40415         {
40416             xtype: 'ColumnModel',
40417             xns: Roo.grid,
40418             dataIndex : 'weekday2',
40419             header : 'Tuesday',
40420             renderer : cellRender
40421         },
40422         {
40423             xtype: 'ColumnModel',
40424             xns: Roo.grid,
40425             dataIndex : 'weekday3',
40426             header : 'Wednesday',
40427             renderer : cellRender
40428         },
40429         {
40430             xtype: 'ColumnModel',
40431             xns: Roo.grid,
40432             dataIndex : 'weekday4',
40433             header : 'Thursday',
40434             renderer : cellRender
40435         },
40436         {
40437             xtype: 'ColumnModel',
40438             xns: Roo.grid,
40439             dataIndex : 'weekday5',
40440             header : 'Friday',
40441             renderer : cellRender
40442         },
40443         {
40444             xtype: 'ColumnModel',
40445             xns: Roo.grid,
40446             dataIndex : 'weekday6',
40447             header : 'Saturday',
40448             renderer : cellRender
40449         }
40450     ]);
40451     this.cm = this.colModel;
40452     this.cm.xmodule = this.xmodule || false;
40453  
40454         
40455           
40456     //this.selModel = new Roo.grid.CellSelectionModel();
40457     //this.sm = this.selModel;
40458     //this.selModel.init(this);
40459     
40460     
40461     if(this.width){
40462         this.container.setWidth(this.width);
40463     }
40464
40465     if(this.height){
40466         this.container.setHeight(this.height);
40467     }
40468     /** @private */
40469         this.addEvents({
40470         // raw events
40471         /**
40472          * @event click
40473          * The raw click event for the entire grid.
40474          * @param {Roo.EventObject} e
40475          */
40476         "click" : true,
40477         /**
40478          * @event dblclick
40479          * The raw dblclick event for the entire grid.
40480          * @param {Roo.EventObject} e
40481          */
40482         "dblclick" : true,
40483         /**
40484          * @event contextmenu
40485          * The raw contextmenu event for the entire grid.
40486          * @param {Roo.EventObject} e
40487          */
40488         "contextmenu" : true,
40489         /**
40490          * @event mousedown
40491          * The raw mousedown event for the entire grid.
40492          * @param {Roo.EventObject} e
40493          */
40494         "mousedown" : true,
40495         /**
40496          * @event mouseup
40497          * The raw mouseup event for the entire grid.
40498          * @param {Roo.EventObject} e
40499          */
40500         "mouseup" : true,
40501         /**
40502          * @event mouseover
40503          * The raw mouseover event for the entire grid.
40504          * @param {Roo.EventObject} e
40505          */
40506         "mouseover" : true,
40507         /**
40508          * @event mouseout
40509          * The raw mouseout event for the entire grid.
40510          * @param {Roo.EventObject} e
40511          */
40512         "mouseout" : true,
40513         /**
40514          * @event keypress
40515          * The raw keypress event for the entire grid.
40516          * @param {Roo.EventObject} e
40517          */
40518         "keypress" : true,
40519         /**
40520          * @event keydown
40521          * The raw keydown event for the entire grid.
40522          * @param {Roo.EventObject} e
40523          */
40524         "keydown" : true,
40525
40526         // custom events
40527
40528         /**
40529          * @event cellclick
40530          * Fires when a cell is clicked
40531          * @param {Grid} this
40532          * @param {Number} rowIndex
40533          * @param {Number} columnIndex
40534          * @param {Roo.EventObject} e
40535          */
40536         "cellclick" : true,
40537         /**
40538          * @event celldblclick
40539          * Fires when a cell is double clicked
40540          * @param {Grid} this
40541          * @param {Number} rowIndex
40542          * @param {Number} columnIndex
40543          * @param {Roo.EventObject} e
40544          */
40545         "celldblclick" : true,
40546         /**
40547          * @event rowclick
40548          * Fires when a row is clicked
40549          * @param {Grid} this
40550          * @param {Number} rowIndex
40551          * @param {Roo.EventObject} e
40552          */
40553         "rowclick" : true,
40554         /**
40555          * @event rowdblclick
40556          * Fires when a row is double clicked
40557          * @param {Grid} this
40558          * @param {Number} rowIndex
40559          * @param {Roo.EventObject} e
40560          */
40561         "rowdblclick" : true,
40562         /**
40563          * @event headerclick
40564          * Fires when a header is clicked
40565          * @param {Grid} this
40566          * @param {Number} columnIndex
40567          * @param {Roo.EventObject} e
40568          */
40569         "headerclick" : true,
40570         /**
40571          * @event headerdblclick
40572          * Fires when a header cell is double clicked
40573          * @param {Grid} this
40574          * @param {Number} columnIndex
40575          * @param {Roo.EventObject} e
40576          */
40577         "headerdblclick" : true,
40578         /**
40579          * @event rowcontextmenu
40580          * Fires when a row is right clicked
40581          * @param {Grid} this
40582          * @param {Number} rowIndex
40583          * @param {Roo.EventObject} e
40584          */
40585         "rowcontextmenu" : true,
40586         /**
40587          * @event cellcontextmenu
40588          * Fires when a cell is right clicked
40589          * @param {Grid} this
40590          * @param {Number} rowIndex
40591          * @param {Number} cellIndex
40592          * @param {Roo.EventObject} e
40593          */
40594          "cellcontextmenu" : true,
40595         /**
40596          * @event headercontextmenu
40597          * Fires when a header is right clicked
40598          * @param {Grid} this
40599          * @param {Number} columnIndex
40600          * @param {Roo.EventObject} e
40601          */
40602         "headercontextmenu" : true,
40603         /**
40604          * @event bodyscroll
40605          * Fires when the body element is scrolled
40606          * @param {Number} scrollLeft
40607          * @param {Number} scrollTop
40608          */
40609         "bodyscroll" : true,
40610         /**
40611          * @event columnresize
40612          * Fires when the user resizes a column
40613          * @param {Number} columnIndex
40614          * @param {Number} newSize
40615          */
40616         "columnresize" : true,
40617         /**
40618          * @event columnmove
40619          * Fires when the user moves a column
40620          * @param {Number} oldIndex
40621          * @param {Number} newIndex
40622          */
40623         "columnmove" : true,
40624         /**
40625          * @event startdrag
40626          * Fires when row(s) start being dragged
40627          * @param {Grid} this
40628          * @param {Roo.GridDD} dd The drag drop object
40629          * @param {event} e The raw browser event
40630          */
40631         "startdrag" : true,
40632         /**
40633          * @event enddrag
40634          * Fires when a drag operation is complete
40635          * @param {Grid} this
40636          * @param {Roo.GridDD} dd The drag drop object
40637          * @param {event} e The raw browser event
40638          */
40639         "enddrag" : true,
40640         /**
40641          * @event dragdrop
40642          * Fires when dragged row(s) are dropped on a valid DD target
40643          * @param {Grid} this
40644          * @param {Roo.GridDD} dd The drag drop object
40645          * @param {String} targetId The target drag drop object
40646          * @param {event} e The raw browser event
40647          */
40648         "dragdrop" : true,
40649         /**
40650          * @event dragover
40651          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40652          * @param {Grid} this
40653          * @param {Roo.GridDD} dd The drag drop object
40654          * @param {String} targetId The target drag drop object
40655          * @param {event} e The raw browser event
40656          */
40657         "dragover" : true,
40658         /**
40659          * @event dragenter
40660          *  Fires when the dragged row(s) first cross another DD target while being dragged
40661          * @param {Grid} this
40662          * @param {Roo.GridDD} dd The drag drop object
40663          * @param {String} targetId The target drag drop object
40664          * @param {event} e The raw browser event
40665          */
40666         "dragenter" : true,
40667         /**
40668          * @event dragout
40669          * Fires when the dragged row(s) leave another DD target while being dragged
40670          * @param {Grid} this
40671          * @param {Roo.GridDD} dd The drag drop object
40672          * @param {String} targetId The target drag drop object
40673          * @param {event} e The raw browser event
40674          */
40675         "dragout" : true,
40676         /**
40677          * @event rowclass
40678          * Fires when a row is rendered, so you can change add a style to it.
40679          * @param {GridView} gridview   The grid view
40680          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40681          */
40682         'rowclass' : true,
40683
40684         /**
40685          * @event render
40686          * Fires when the grid is rendered
40687          * @param {Grid} grid
40688          */
40689         'render' : true,
40690             /**
40691              * @event select
40692              * Fires when a date is selected
40693              * @param {DatePicker} this
40694              * @param {Date} date The selected date
40695              */
40696         'select': true,
40697         /**
40698              * @event monthchange
40699              * Fires when the displayed month changes 
40700              * @param {DatePicker} this
40701              * @param {Date} date The selected month
40702              */
40703         'monthchange': true,
40704         /**
40705              * @event evententer
40706              * Fires when mouse over an event
40707              * @param {Calendar} this
40708              * @param {event} Event
40709              */
40710         'evententer': true,
40711         /**
40712              * @event eventleave
40713              * Fires when the mouse leaves an
40714              * @param {Calendar} this
40715              * @param {event}
40716              */
40717         'eventleave': true,
40718         /**
40719              * @event eventclick
40720              * Fires when the mouse click an
40721              * @param {Calendar} this
40722              * @param {event}
40723              */
40724         'eventclick': true,
40725         /**
40726              * @event eventrender
40727              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40728              * @param {Calendar} this
40729              * @param {data} data to be modified
40730              */
40731         'eventrender': true
40732         
40733     });
40734
40735     Roo.grid.Grid.superclass.constructor.call(this);
40736     this.on('render', function() {
40737         this.view.el.addClass('x-grid-cal'); 
40738         
40739         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40740
40741     },this);
40742     
40743     if (!Roo.grid.Calendar.style) {
40744         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40745             
40746             
40747             '.x-grid-cal .x-grid-col' :  {
40748                 height: 'auto !important',
40749                 'vertical-align': 'top'
40750             },
40751             '.x-grid-cal  .fc-event-hori' : {
40752                 height: '14px'
40753             }
40754              
40755             
40756         }, Roo.id());
40757     }
40758
40759     
40760     
40761 };
40762 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40763     /**
40764      * @cfg {Store} eventStore The store that loads events.
40765      */
40766     eventStore : 25,
40767
40768      
40769     activeDate : false,
40770     startDay : 0,
40771     autoWidth : true,
40772     monitorWindowResize : false,
40773
40774     
40775     resizeColumns : function() {
40776         var col = (this.view.el.getWidth() / 7) - 3;
40777         // loop through cols, and setWidth
40778         for(var i =0 ; i < 7 ; i++){
40779             this.cm.setColumnWidth(i, col);
40780         }
40781     },
40782      setDate :function(date) {
40783         
40784         Roo.log('setDate?');
40785         
40786         this.resizeColumns();
40787         var vd = this.activeDate;
40788         this.activeDate = date;
40789 //        if(vd && this.el){
40790 //            var t = date.getTime();
40791 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40792 //                Roo.log('using add remove');
40793 //                
40794 //                this.fireEvent('monthchange', this, date);
40795 //                
40796 //                this.cells.removeClass("fc-state-highlight");
40797 //                this.cells.each(function(c){
40798 //                   if(c.dateValue == t){
40799 //                       c.addClass("fc-state-highlight");
40800 //                       setTimeout(function(){
40801 //                            try{c.dom.firstChild.focus();}catch(e){}
40802 //                       }, 50);
40803 //                       return false;
40804 //                   }
40805 //                   return true;
40806 //                });
40807 //                return;
40808 //            }
40809 //        }
40810         
40811         var days = date.getDaysInMonth();
40812         
40813         var firstOfMonth = date.getFirstDateOfMonth();
40814         var startingPos = firstOfMonth.getDay()-this.startDay;
40815         
40816         if(startingPos < this.startDay){
40817             startingPos += 7;
40818         }
40819         
40820         var pm = date.add(Date.MONTH, -1);
40821         var prevStart = pm.getDaysInMonth()-startingPos;
40822 //        
40823         
40824         
40825         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40826         
40827         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40828         //this.cells.addClassOnOver('fc-state-hover');
40829         
40830         var cells = this.cells.elements;
40831         var textEls = this.textNodes;
40832         
40833         //Roo.each(cells, function(cell){
40834         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40835         //});
40836         
40837         days += startingPos;
40838
40839         // convert everything to numbers so it's fast
40840         var day = 86400000;
40841         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40842         //Roo.log(d);
40843         //Roo.log(pm);
40844         //Roo.log(prevStart);
40845         
40846         var today = new Date().clearTime().getTime();
40847         var sel = date.clearTime().getTime();
40848         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40849         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40850         var ddMatch = this.disabledDatesRE;
40851         var ddText = this.disabledDatesText;
40852         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40853         var ddaysText = this.disabledDaysText;
40854         var format = this.format;
40855         
40856         var setCellClass = function(cal, cell){
40857             
40858             //Roo.log('set Cell Class');
40859             cell.title = "";
40860             var t = d.getTime();
40861             
40862             //Roo.log(d);
40863             
40864             
40865             cell.dateValue = t;
40866             if(t == today){
40867                 cell.className += " fc-today";
40868                 cell.className += " fc-state-highlight";
40869                 cell.title = cal.todayText;
40870             }
40871             if(t == sel){
40872                 // disable highlight in other month..
40873                 cell.className += " fc-state-highlight";
40874                 
40875             }
40876             // disabling
40877             if(t < min) {
40878                 //cell.className = " fc-state-disabled";
40879                 cell.title = cal.minText;
40880                 return;
40881             }
40882             if(t > max) {
40883                 //cell.className = " fc-state-disabled";
40884                 cell.title = cal.maxText;
40885                 return;
40886             }
40887             if(ddays){
40888                 if(ddays.indexOf(d.getDay()) != -1){
40889                     // cell.title = ddaysText;
40890                    // cell.className = " fc-state-disabled";
40891                 }
40892             }
40893             if(ddMatch && format){
40894                 var fvalue = d.dateFormat(format);
40895                 if(ddMatch.test(fvalue)){
40896                     cell.title = ddText.replace("%0", fvalue);
40897                    cell.className = " fc-state-disabled";
40898                 }
40899             }
40900             
40901             if (!cell.initialClassName) {
40902                 cell.initialClassName = cell.dom.className;
40903             }
40904             
40905             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40906         };
40907
40908         var i = 0;
40909         
40910         for(; i < startingPos; i++) {
40911             cells[i].dayName =  (++prevStart);
40912             Roo.log(textEls[i]);
40913             d.setDate(d.getDate()+1);
40914             
40915             //cells[i].className = "fc-past fc-other-month";
40916             setCellClass(this, cells[i]);
40917         }
40918         
40919         var intDay = 0;
40920         
40921         for(; i < days; i++){
40922             intDay = i - startingPos + 1;
40923             cells[i].dayName =  (intDay);
40924             d.setDate(d.getDate()+1);
40925             
40926             cells[i].className = ''; // "x-date-active";
40927             setCellClass(this, cells[i]);
40928         }
40929         var extraDays = 0;
40930         
40931         for(; i < 42; i++) {
40932             //textEls[i].innerHTML = (++extraDays);
40933             
40934             d.setDate(d.getDate()+1);
40935             cells[i].dayName = (++extraDays);
40936             cells[i].className = "fc-future fc-other-month";
40937             setCellClass(this, cells[i]);
40938         }
40939         
40940         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40941         
40942         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40943         
40944         // this will cause all the cells to mis
40945         var rows= [];
40946         var i =0;
40947         for (var r = 0;r < 6;r++) {
40948             for (var c =0;c < 7;c++) {
40949                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40950             }    
40951         }
40952         
40953         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40954         for(i=0;i<cells.length;i++) {
40955             
40956             this.cells.elements[i].dayName = cells[i].dayName ;
40957             this.cells.elements[i].className = cells[i].className;
40958             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40959             this.cells.elements[i].title = cells[i].title ;
40960             this.cells.elements[i].dateValue = cells[i].dateValue ;
40961         }
40962         
40963         
40964         
40965         
40966         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40967         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40968         
40969         ////if(totalRows != 6){
40970             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40971            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40972        // }
40973         
40974         this.fireEvent('monthchange', this, date);
40975         
40976         
40977     },
40978  /**
40979      * Returns the grid's SelectionModel.
40980      * @return {SelectionModel}
40981      */
40982     getSelectionModel : function(){
40983         if(!this.selModel){
40984             this.selModel = new Roo.grid.CellSelectionModel();
40985         }
40986         return this.selModel;
40987     },
40988
40989     load: function() {
40990         this.eventStore.load()
40991         
40992         
40993         
40994     },
40995     
40996     findCell : function(dt) {
40997         dt = dt.clearTime().getTime();
40998         var ret = false;
40999         this.cells.each(function(c){
41000             //Roo.log("check " +c.dateValue + '?=' + dt);
41001             if(c.dateValue == dt){
41002                 ret = c;
41003                 return false;
41004             }
41005             return true;
41006         });
41007         
41008         return ret;
41009     },
41010     
41011     findCells : function(rec) {
41012         var s = rec.data.start_dt.clone().clearTime().getTime();
41013        // Roo.log(s);
41014         var e= rec.data.end_dt.clone().clearTime().getTime();
41015        // Roo.log(e);
41016         var ret = [];
41017         this.cells.each(function(c){
41018              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41019             
41020             if(c.dateValue > e){
41021                 return ;
41022             }
41023             if(c.dateValue < s){
41024                 return ;
41025             }
41026             ret.push(c);
41027         });
41028         
41029         return ret;    
41030     },
41031     
41032     findBestRow: function(cells)
41033     {
41034         var ret = 0;
41035         
41036         for (var i =0 ; i < cells.length;i++) {
41037             ret  = Math.max(cells[i].rows || 0,ret);
41038         }
41039         return ret;
41040         
41041     },
41042     
41043     
41044     addItem : function(rec)
41045     {
41046         // look for vertical location slot in
41047         var cells = this.findCells(rec);
41048         
41049         rec.row = this.findBestRow(cells);
41050         
41051         // work out the location.
41052         
41053         var crow = false;
41054         var rows = [];
41055         for(var i =0; i < cells.length; i++) {
41056             if (!crow) {
41057                 crow = {
41058                     start : cells[i],
41059                     end :  cells[i]
41060                 };
41061                 continue;
41062             }
41063             if (crow.start.getY() == cells[i].getY()) {
41064                 // on same row.
41065                 crow.end = cells[i];
41066                 continue;
41067             }
41068             // different row.
41069             rows.push(crow);
41070             crow = {
41071                 start: cells[i],
41072                 end : cells[i]
41073             };
41074             
41075         }
41076         
41077         rows.push(crow);
41078         rec.els = [];
41079         rec.rows = rows;
41080         rec.cells = cells;
41081         for (var i = 0; i < cells.length;i++) {
41082             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41083             
41084         }
41085         
41086         
41087     },
41088     
41089     clearEvents: function() {
41090         
41091         if (!this.eventStore.getCount()) {
41092             return;
41093         }
41094         // reset number of rows in cells.
41095         Roo.each(this.cells.elements, function(c){
41096             c.rows = 0;
41097         });
41098         
41099         this.eventStore.each(function(e) {
41100             this.clearEvent(e);
41101         },this);
41102         
41103     },
41104     
41105     clearEvent : function(ev)
41106     {
41107         if (ev.els) {
41108             Roo.each(ev.els, function(el) {
41109                 el.un('mouseenter' ,this.onEventEnter, this);
41110                 el.un('mouseleave' ,this.onEventLeave, this);
41111                 el.remove();
41112             },this);
41113             ev.els = [];
41114         }
41115     },
41116     
41117     
41118     renderEvent : function(ev,ctr) {
41119         if (!ctr) {
41120              ctr = this.view.el.select('.fc-event-container',true).first();
41121         }
41122         
41123          
41124         this.clearEvent(ev);
41125             //code
41126        
41127         
41128         
41129         ev.els = [];
41130         var cells = ev.cells;
41131         var rows = ev.rows;
41132         this.fireEvent('eventrender', this, ev);
41133         
41134         for(var i =0; i < rows.length; i++) {
41135             
41136             cls = '';
41137             if (i == 0) {
41138                 cls += ' fc-event-start';
41139             }
41140             if ((i+1) == rows.length) {
41141                 cls += ' fc-event-end';
41142             }
41143             
41144             //Roo.log(ev.data);
41145             // how many rows should it span..
41146             var cg = this.eventTmpl.append(ctr,Roo.apply({
41147                 fccls : cls
41148                 
41149             }, ev.data) , true);
41150             
41151             
41152             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41153             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41154             cg.on('click', this.onEventClick, this, ev);
41155             
41156             ev.els.push(cg);
41157             
41158             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41159             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41160             //Roo.log(cg);
41161              
41162             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41163             cg.setWidth(ebox.right - sbox.x -2);
41164         }
41165     },
41166     
41167     renderEvents: function()
41168     {   
41169         // first make sure there is enough space..
41170         
41171         if (!this.eventTmpl) {
41172             this.eventTmpl = new Roo.Template(
41173                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41174                     '<div class="fc-event-inner">' +
41175                         '<span class="fc-event-time">{time}</span>' +
41176                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41177                     '</div>' +
41178                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41179                 '</div>'
41180             );
41181                 
41182         }
41183                
41184         
41185         
41186         this.cells.each(function(c) {
41187             //Roo.log(c.select('.fc-day-content div',true).first());
41188             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41189         });
41190         
41191         var ctr = this.view.el.select('.fc-event-container',true).first();
41192         
41193         var cls;
41194         this.eventStore.each(function(ev){
41195             
41196             this.renderEvent(ev);
41197              
41198              
41199         }, this);
41200         this.view.layout();
41201         
41202     },
41203     
41204     onEventEnter: function (e, el,event,d) {
41205         this.fireEvent('evententer', this, el, event);
41206     },
41207     
41208     onEventLeave: function (e, el,event,d) {
41209         this.fireEvent('eventleave', this, el, event);
41210     },
41211     
41212     onEventClick: function (e, el,event,d) {
41213         this.fireEvent('eventclick', this, el, event);
41214     },
41215     
41216     onMonthChange: function () {
41217         this.store.load();
41218     },
41219     
41220     onLoad: function () {
41221         
41222         //Roo.log('calendar onload');
41223 //         
41224         if(this.eventStore.getCount() > 0){
41225             
41226            
41227             
41228             this.eventStore.each(function(d){
41229                 
41230                 
41231                 // FIXME..
41232                 var add =   d.data;
41233                 if (typeof(add.end_dt) == 'undefined')  {
41234                     Roo.log("Missing End time in calendar data: ");
41235                     Roo.log(d);
41236                     return;
41237                 }
41238                 if (typeof(add.start_dt) == 'undefined')  {
41239                     Roo.log("Missing Start time in calendar data: ");
41240                     Roo.log(d);
41241                     return;
41242                 }
41243                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41244                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41245                 add.id = add.id || d.id;
41246                 add.title = add.title || '??';
41247                 
41248                 this.addItem(d);
41249                 
41250              
41251             },this);
41252         }
41253         
41254         this.renderEvents();
41255     }
41256     
41257
41258 });
41259 /*
41260  grid : {
41261                 xtype: 'Grid',
41262                 xns: Roo.grid,
41263                 listeners : {
41264                     render : function ()
41265                     {
41266                         _this.grid = this;
41267                         
41268                         if (!this.view.el.hasClass('course-timesheet')) {
41269                             this.view.el.addClass('course-timesheet');
41270                         }
41271                         if (this.tsStyle) {
41272                             this.ds.load({});
41273                             return; 
41274                         }
41275                         Roo.log('width');
41276                         Roo.log(_this.grid.view.el.getWidth());
41277                         
41278                         
41279                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41280                             '.course-timesheet .x-grid-row' : {
41281                                 height: '80px'
41282                             },
41283                             '.x-grid-row td' : {
41284                                 'vertical-align' : 0
41285                             },
41286                             '.course-edit-link' : {
41287                                 'color' : 'blue',
41288                                 'text-overflow' : 'ellipsis',
41289                                 'overflow' : 'hidden',
41290                                 'white-space' : 'nowrap',
41291                                 'cursor' : 'pointer'
41292                             },
41293                             '.sub-link' : {
41294                                 'color' : 'green'
41295                             },
41296                             '.de-act-sup-link' : {
41297                                 'color' : 'purple',
41298                                 'text-decoration' : 'line-through'
41299                             },
41300                             '.de-act-link' : {
41301                                 'color' : 'red',
41302                                 'text-decoration' : 'line-through'
41303                             },
41304                             '.course-timesheet .course-highlight' : {
41305                                 'border-top-style': 'dashed !important',
41306                                 'border-bottom-bottom': 'dashed !important'
41307                             },
41308                             '.course-timesheet .course-item' : {
41309                                 'font-family'   : 'tahoma, arial, helvetica',
41310                                 'font-size'     : '11px',
41311                                 'overflow'      : 'hidden',
41312                                 'padding-left'  : '10px',
41313                                 'padding-right' : '10px',
41314                                 'padding-top' : '10px' 
41315                             }
41316                             
41317                         }, Roo.id());
41318                                 this.ds.load({});
41319                     }
41320                 },
41321                 autoWidth : true,
41322                 monitorWindowResize : false,
41323                 cellrenderer : function(v,x,r)
41324                 {
41325                     return v;
41326                 },
41327                 sm : {
41328                     xtype: 'CellSelectionModel',
41329                     xns: Roo.grid
41330                 },
41331                 dataSource : {
41332                     xtype: 'Store',
41333                     xns: Roo.data,
41334                     listeners : {
41335                         beforeload : function (_self, options)
41336                         {
41337                             options.params = options.params || {};
41338                             options.params._month = _this.monthField.getValue();
41339                             options.params.limit = 9999;
41340                             options.params['sort'] = 'when_dt';    
41341                             options.params['dir'] = 'ASC';    
41342                             this.proxy.loadResponse = this.loadResponse;
41343                             Roo.log("load?");
41344                             //this.addColumns();
41345                         },
41346                         load : function (_self, records, options)
41347                         {
41348                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41349                                 // if you click on the translation.. you can edit it...
41350                                 var el = Roo.get(this);
41351                                 var id = el.dom.getAttribute('data-id');
41352                                 var d = el.dom.getAttribute('data-date');
41353                                 var t = el.dom.getAttribute('data-time');
41354                                 //var id = this.child('span').dom.textContent;
41355                                 
41356                                 //Roo.log(this);
41357                                 Pman.Dialog.CourseCalendar.show({
41358                                     id : id,
41359                                     when_d : d,
41360                                     when_t : t,
41361                                     productitem_active : id ? 1 : 0
41362                                 }, function() {
41363                                     _this.grid.ds.load({});
41364                                 });
41365                            
41366                            });
41367                            
41368                            _this.panel.fireEvent('resize', [ '', '' ]);
41369                         }
41370                     },
41371                     loadResponse : function(o, success, response){
41372                             // this is overridden on before load..
41373                             
41374                             Roo.log("our code?");       
41375                             //Roo.log(success);
41376                             //Roo.log(response)
41377                             delete this.activeRequest;
41378                             if(!success){
41379                                 this.fireEvent("loadexception", this, o, response);
41380                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41381                                 return;
41382                             }
41383                             var result;
41384                             try {
41385                                 result = o.reader.read(response);
41386                             }catch(e){
41387                                 Roo.log("load exception?");
41388                                 this.fireEvent("loadexception", this, o, response, e);
41389                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41390                                 return;
41391                             }
41392                             Roo.log("ready...");        
41393                             // loop through result.records;
41394                             // and set this.tdate[date] = [] << array of records..
41395                             _this.tdata  = {};
41396                             Roo.each(result.records, function(r){
41397                                 //Roo.log(r.data);
41398                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41399                                     _this.tdata[r.data.when_dt.format('j')] = [];
41400                                 }
41401                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41402                             });
41403                             
41404                             //Roo.log(_this.tdata);
41405                             
41406                             result.records = [];
41407                             result.totalRecords = 6;
41408                     
41409                             // let's generate some duumy records for the rows.
41410                             //var st = _this.dateField.getValue();
41411                             
41412                             // work out monday..
41413                             //st = st.add(Date.DAY, -1 * st.format('w'));
41414                             
41415                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41416                             
41417                             var firstOfMonth = date.getFirstDayOfMonth();
41418                             var days = date.getDaysInMonth();
41419                             var d = 1;
41420                             var firstAdded = false;
41421                             for (var i = 0; i < result.totalRecords ; i++) {
41422                                 //var d= st.add(Date.DAY, i);
41423                                 var row = {};
41424                                 var added = 0;
41425                                 for(var w = 0 ; w < 7 ; w++){
41426                                     if(!firstAdded && firstOfMonth != w){
41427                                         continue;
41428                                     }
41429                                     if(d > days){
41430                                         continue;
41431                                     }
41432                                     firstAdded = true;
41433                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41434                                     row['weekday'+w] = String.format(
41435                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41436                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41437                                                     d,
41438                                                     date.format('Y-m-')+dd
41439                                                 );
41440                                     added++;
41441                                     if(typeof(_this.tdata[d]) != 'undefined'){
41442                                         Roo.each(_this.tdata[d], function(r){
41443                                             var is_sub = '';
41444                                             var deactive = '';
41445                                             var id = r.id;
41446                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41447                                             if(r.parent_id*1>0){
41448                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41449                                                 id = r.parent_id;
41450                                             }
41451                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41452                                                 deactive = 'de-act-link';
41453                                             }
41454                                             
41455                                             row['weekday'+w] += String.format(
41456                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41457                                                     id, //0
41458                                                     r.product_id_name, //1
41459                                                     r.when_dt.format('h:ia'), //2
41460                                                     is_sub, //3
41461                                                     deactive, //4
41462                                                     desc // 5
41463                                             );
41464                                         });
41465                                     }
41466                                     d++;
41467                                 }
41468                                 
41469                                 // only do this if something added..
41470                                 if(added > 0){ 
41471                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41472                                 }
41473                                 
41474                                 
41475                                 // push it twice. (second one with an hour..
41476                                 
41477                             }
41478                             //Roo.log(result);
41479                             this.fireEvent("load", this, o, o.request.arg);
41480                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41481                         },
41482                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41483                     proxy : {
41484                         xtype: 'HttpProxy',
41485                         xns: Roo.data,
41486                         method : 'GET',
41487                         url : baseURL + '/Roo/Shop_course.php'
41488                     },
41489                     reader : {
41490                         xtype: 'JsonReader',
41491                         xns: Roo.data,
41492                         id : 'id',
41493                         fields : [
41494                             {
41495                                 'name': 'id',
41496                                 'type': 'int'
41497                             },
41498                             {
41499                                 'name': 'when_dt',
41500                                 'type': 'string'
41501                             },
41502                             {
41503                                 'name': 'end_dt',
41504                                 'type': 'string'
41505                             },
41506                             {
41507                                 'name': 'parent_id',
41508                                 'type': 'int'
41509                             },
41510                             {
41511                                 'name': 'product_id',
41512                                 'type': 'int'
41513                             },
41514                             {
41515                                 'name': 'productitem_id',
41516                                 'type': 'int'
41517                             },
41518                             {
41519                                 'name': 'guid',
41520                                 'type': 'int'
41521                             }
41522                         ]
41523                     }
41524                 },
41525                 toolbar : {
41526                     xtype: 'Toolbar',
41527                     xns: Roo,
41528                     items : [
41529                         {
41530                             xtype: 'Button',
41531                             xns: Roo.Toolbar,
41532                             listeners : {
41533                                 click : function (_self, e)
41534                                 {
41535                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41536                                     sd.setMonth(sd.getMonth()-1);
41537                                     _this.monthField.setValue(sd.format('Y-m-d'));
41538                                     _this.grid.ds.load({});
41539                                 }
41540                             },
41541                             text : "Back"
41542                         },
41543                         {
41544                             xtype: 'Separator',
41545                             xns: Roo.Toolbar
41546                         },
41547                         {
41548                             xtype: 'MonthField',
41549                             xns: Roo.form,
41550                             listeners : {
41551                                 render : function (_self)
41552                                 {
41553                                     _this.monthField = _self;
41554                                    // _this.monthField.set  today
41555                                 },
41556                                 select : function (combo, date)
41557                                 {
41558                                     _this.grid.ds.load({});
41559                                 }
41560                             },
41561                             value : (function() { return new Date(); })()
41562                         },
41563                         {
41564                             xtype: 'Separator',
41565                             xns: Roo.Toolbar
41566                         },
41567                         {
41568                             xtype: 'TextItem',
41569                             xns: Roo.Toolbar,
41570                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41571                         },
41572                         {
41573                             xtype: 'Fill',
41574                             xns: Roo.Toolbar
41575                         },
41576                         {
41577                             xtype: 'Button',
41578                             xns: Roo.Toolbar,
41579                             listeners : {
41580                                 click : function (_self, e)
41581                                 {
41582                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41583                                     sd.setMonth(sd.getMonth()+1);
41584                                     _this.monthField.setValue(sd.format('Y-m-d'));
41585                                     _this.grid.ds.load({});
41586                                 }
41587                             },
41588                             text : "Next"
41589                         }
41590                     ]
41591                 },
41592                  
41593             }
41594         };
41595         
41596         *//*
41597  * Based on:
41598  * Ext JS Library 1.1.1
41599  * Copyright(c) 2006-2007, Ext JS, LLC.
41600  *
41601  * Originally Released Under LGPL - original licence link has changed is not relivant.
41602  *
41603  * Fork - LGPL
41604  * <script type="text/javascript">
41605  */
41606  
41607 /**
41608  * @class Roo.LoadMask
41609  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41610  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41611  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41612  * element's UpdateManager load indicator and will be destroyed after the initial load.
41613  * @constructor
41614  * Create a new LoadMask
41615  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41616  * @param {Object} config The config object
41617  */
41618 Roo.LoadMask = function(el, config){
41619     this.el = Roo.get(el);
41620     Roo.apply(this, config);
41621     if(this.store){
41622         this.store.on('beforeload', this.onBeforeLoad, this);
41623         this.store.on('load', this.onLoad, this);
41624         this.store.on('loadexception', this.onLoadException, this);
41625         this.removeMask = false;
41626     }else{
41627         var um = this.el.getUpdateManager();
41628         um.showLoadIndicator = false; // disable the default indicator
41629         um.on('beforeupdate', this.onBeforeLoad, this);
41630         um.on('update', this.onLoad, this);
41631         um.on('failure', this.onLoad, this);
41632         this.removeMask = true;
41633     }
41634 };
41635
41636 Roo.LoadMask.prototype = {
41637     /**
41638      * @cfg {Boolean} removeMask
41639      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41640      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41641      */
41642     removeMask : false,
41643     /**
41644      * @cfg {String} msg
41645      * The text to display in a centered loading message box (defaults to 'Loading...')
41646      */
41647     msg : 'Loading...',
41648     /**
41649      * @cfg {String} msgCls
41650      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41651      */
41652     msgCls : 'x-mask-loading',
41653
41654     /**
41655      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41656      * @type Boolean
41657      */
41658     disabled: false,
41659
41660     /**
41661      * Disables the mask to prevent it from being displayed
41662      */
41663     disable : function(){
41664        this.disabled = true;
41665     },
41666
41667     /**
41668      * Enables the mask so that it can be displayed
41669      */
41670     enable : function(){
41671         this.disabled = false;
41672     },
41673     
41674     onLoadException : function()
41675     {
41676         Roo.log(arguments);
41677         
41678         if (typeof(arguments[3]) != 'undefined') {
41679             Roo.MessageBox.alert("Error loading",arguments[3]);
41680         } 
41681         /*
41682         try {
41683             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41684                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41685             }   
41686         } catch(e) {
41687             
41688         }
41689         */
41690     
41691         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
41692     },
41693     // private
41694     onLoad : function()
41695     {
41696         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
41697     },
41698
41699     // private
41700     onBeforeLoad : function(){
41701         if(!this.disabled){
41702             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
41703         }
41704     },
41705
41706     // private
41707     destroy : function(){
41708         if(this.store){
41709             this.store.un('beforeload', this.onBeforeLoad, this);
41710             this.store.un('load', this.onLoad, this);
41711             this.store.un('loadexception', this.onLoadException, this);
41712         }else{
41713             var um = this.el.getUpdateManager();
41714             um.un('beforeupdate', this.onBeforeLoad, this);
41715             um.un('update', this.onLoad, this);
41716             um.un('failure', this.onLoad, this);
41717         }
41718     }
41719 };/*
41720  * Based on:
41721  * Ext JS Library 1.1.1
41722  * Copyright(c) 2006-2007, Ext JS, LLC.
41723  *
41724  * Originally Released Under LGPL - original licence link has changed is not relivant.
41725  *
41726  * Fork - LGPL
41727  * <script type="text/javascript">
41728  */
41729
41730
41731 /**
41732  * @class Roo.XTemplate
41733  * @extends Roo.Template
41734  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41735 <pre><code>
41736 var t = new Roo.XTemplate(
41737         '&lt;select name="{name}"&gt;',
41738                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41739         '&lt;/select&gt;'
41740 );
41741  
41742 // then append, applying the master template values
41743  </code></pre>
41744  *
41745  * Supported features:
41746  *
41747  *  Tags:
41748
41749 <pre><code>
41750       {a_variable} - output encoded.
41751       {a_variable.format:("Y-m-d")} - call a method on the variable
41752       {a_variable:raw} - unencoded output
41753       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41754       {a_variable:this.method_on_template(...)} - call a method on the template object.
41755  
41756 </code></pre>
41757  *  The tpl tag:
41758 <pre><code>
41759         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41760         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41761         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41762         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41763   
41764         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41765         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41766 </code></pre>
41767  *      
41768  */
41769 Roo.XTemplate = function()
41770 {
41771     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41772     if (this.html) {
41773         this.compile();
41774     }
41775 };
41776
41777
41778 Roo.extend(Roo.XTemplate, Roo.Template, {
41779
41780     /**
41781      * The various sub templates
41782      */
41783     tpls : false,
41784     /**
41785      *
41786      * basic tag replacing syntax
41787      * WORD:WORD()
41788      *
41789      * // you can fake an object call by doing this
41790      *  x.t:(test,tesT) 
41791      * 
41792      */
41793     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41794
41795     /**
41796      * compile the template
41797      *
41798      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41799      *
41800      */
41801     compile: function()
41802     {
41803         var s = this.html;
41804      
41805         s = ['<tpl>', s, '</tpl>'].join('');
41806     
41807         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41808             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41809             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41810             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41811             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41812             m,
41813             id     = 0,
41814             tpls   = [];
41815     
41816         while(true == !!(m = s.match(re))){
41817             var forMatch   = m[0].match(nameRe),
41818                 ifMatch   = m[0].match(ifRe),
41819                 execMatch   = m[0].match(execRe),
41820                 namedMatch   = m[0].match(namedRe),
41821                 
41822                 exp  = null, 
41823                 fn   = null,
41824                 exec = null,
41825                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41826                 
41827             if (ifMatch) {
41828                 // if - puts fn into test..
41829                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41830                 if(exp){
41831                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41832                 }
41833             }
41834             
41835             if (execMatch) {
41836                 // exec - calls a function... returns empty if true is  returned.
41837                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41838                 if(exp){
41839                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41840                 }
41841             }
41842             
41843             
41844             if (name) {
41845                 // for = 
41846                 switch(name){
41847                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41848                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41849                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41850                 }
41851             }
41852             var uid = namedMatch ? namedMatch[1] : id;
41853             
41854             
41855             tpls.push({
41856                 id:     namedMatch ? namedMatch[1] : id,
41857                 target: name,
41858                 exec:   exec,
41859                 test:   fn,
41860                 body:   m[1] || ''
41861             });
41862             if (namedMatch) {
41863                 s = s.replace(m[0], '');
41864             } else { 
41865                 s = s.replace(m[0], '{xtpl'+ id + '}');
41866             }
41867             ++id;
41868         }
41869         this.tpls = [];
41870         for(var i = tpls.length-1; i >= 0; --i){
41871             this.compileTpl(tpls[i]);
41872             this.tpls[tpls[i].id] = tpls[i];
41873         }
41874         this.master = tpls[tpls.length-1];
41875         return this;
41876     },
41877     /**
41878      * same as applyTemplate, except it's done to one of the subTemplates
41879      * when using named templates, you can do:
41880      *
41881      * var str = pl.applySubTemplate('your-name', values);
41882      *
41883      * 
41884      * @param {Number} id of the template
41885      * @param {Object} values to apply to template
41886      * @param {Object} parent (normaly the instance of this object)
41887      */
41888     applySubTemplate : function(id, values, parent)
41889     {
41890         
41891         
41892         var t = this.tpls[id];
41893         
41894         
41895         try { 
41896             if(t.test && !t.test.call(this, values, parent)){
41897                 return '';
41898             }
41899         } catch(e) {
41900             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41901             Roo.log(e.toString());
41902             Roo.log(t.test);
41903             return ''
41904         }
41905         try { 
41906             
41907             if(t.exec && t.exec.call(this, values, parent)){
41908                 return '';
41909             }
41910         } catch(e) {
41911             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41912             Roo.log(e.toString());
41913             Roo.log(t.exec);
41914             return ''
41915         }
41916         try {
41917             var vs = t.target ? t.target.call(this, values, parent) : values;
41918             parent = t.target ? values : parent;
41919             if(t.target && vs instanceof Array){
41920                 var buf = [];
41921                 for(var i = 0, len = vs.length; i < len; i++){
41922                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41923                 }
41924                 return buf.join('');
41925             }
41926             return t.compiled.call(this, vs, parent);
41927         } catch (e) {
41928             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41929             Roo.log(e.toString());
41930             Roo.log(t.compiled);
41931             return '';
41932         }
41933     },
41934
41935     compileTpl : function(tpl)
41936     {
41937         var fm = Roo.util.Format;
41938         var useF = this.disableFormats !== true;
41939         var sep = Roo.isGecko ? "+" : ",";
41940         var undef = function(str) {
41941             Roo.log("Property not found :"  + str);
41942             return '';
41943         };
41944         
41945         var fn = function(m, name, format, args)
41946         {
41947             //Roo.log(arguments);
41948             args = args ? args.replace(/\\'/g,"'") : args;
41949             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41950             if (typeof(format) == 'undefined') {
41951                 format= 'htmlEncode';
41952             }
41953             if (format == 'raw' ) {
41954                 format = false;
41955             }
41956             
41957             if(name.substr(0, 4) == 'xtpl'){
41958                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41959             }
41960             
41961             // build an array of options to determine if value is undefined..
41962             
41963             // basically get 'xxxx.yyyy' then do
41964             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41965             //    (function () { Roo.log("Property not found"); return ''; })() :
41966             //    ......
41967             
41968             var udef_ar = [];
41969             var lookfor = '';
41970             Roo.each(name.split('.'), function(st) {
41971                 lookfor += (lookfor.length ? '.': '') + st;
41972                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41973             });
41974             
41975             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41976             
41977             
41978             if(format && useF){
41979                 
41980                 args = args ? ',' + args : "";
41981                  
41982                 if(format.substr(0, 5) != "this."){
41983                     format = "fm." + format + '(';
41984                 }else{
41985                     format = 'this.call("'+ format.substr(5) + '", ';
41986                     args = ", values";
41987                 }
41988                 
41989                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41990             }
41991              
41992             if (args.length) {
41993                 // called with xxyx.yuu:(test,test)
41994                 // change to ()
41995                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
41996             }
41997             // raw.. - :raw modifier..
41998             return "'"+ sep + udef_st  + name + ")"+sep+"'";
41999             
42000         };
42001         var body;
42002         // branched to use + in gecko and [].join() in others
42003         if(Roo.isGecko){
42004             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42005                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42006                     "';};};";
42007         }else{
42008             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42009             body.push(tpl.body.replace(/(\r\n|\n)/g,
42010                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42011             body.push("'].join('');};};");
42012             body = body.join('');
42013         }
42014         
42015         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42016        
42017         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42018         eval(body);
42019         
42020         return this;
42021     },
42022
42023     applyTemplate : function(values){
42024         return this.master.compiled.call(this, values, {});
42025         //var s = this.subs;
42026     },
42027
42028     apply : function(){
42029         return this.applyTemplate.apply(this, arguments);
42030     }
42031
42032  });
42033
42034 Roo.XTemplate.from = function(el){
42035     el = Roo.getDom(el);
42036     return new Roo.XTemplate(el.value || el.innerHTML);
42037 };