Fix #7189 - simplify array grid example
[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  * @extends Roo.data.DataProxy
1398  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1399  * to the Reader when its load method is called.
1400  * @constructor
1401  * @param {Object} config  A config object containing the objects needed for the Store to access data,
1402  */
1403 Roo.data.MemoryProxy = function(data){
1404     if (typeof(data) != 'undefined' && typeof(data.data) != 'undefined') {
1405         data = data.data;
1406     }
1407     Roo.data.MemoryProxy.superclass.constructor.call(this);
1408     this.data = data;
1409 };
1410
1411 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1412     
1413     /**
1414      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1415      */
1416     /**
1417      * Load data from the requested source (in this case an in-memory
1418      * data object passed to the constructor), read the data object into
1419      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1420      * process that block using the passed callback.
1421      * @param {Object} params This parameter is not used by the MemoryProxy class.
1422      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1423      * object into a block of Roo.data.Records.
1424      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1425      * The function must be passed <ul>
1426      * <li>The Record block object</li>
1427      * <li>The "arg" argument from the load function</li>
1428      * <li>A boolean success indicator</li>
1429      * </ul>
1430      * @param {Object} scope The scope in which to call the callback
1431      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1432      */
1433     load : function(params, reader, callback, scope, arg){
1434         params = params || {};
1435         var result;
1436         try {
1437             result = reader.readRecords(params.data ? params.data :this.data);
1438         }catch(e){
1439             this.fireEvent("loadexception", this, arg, null, e);
1440             callback.call(scope, null, arg, false);
1441             return;
1442         }
1443         callback.call(scope, result, arg, true);
1444     },
1445     
1446     // private
1447     update : function(params, records){
1448         
1449     }
1450 });/*
1451  * Based on:
1452  * Ext JS Library 1.1.1
1453  * Copyright(c) 2006-2007, Ext JS, LLC.
1454  *
1455  * Originally Released Under LGPL - original licence link has changed is not relivant.
1456  *
1457  * Fork - LGPL
1458  * <script type="text/javascript">
1459  */
1460 /**
1461  * @class Roo.data.HttpProxy
1462  * @extends Roo.data.DataProxy
1463  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1464  * configured to reference a certain URL.<br><br>
1465  * <p>
1466  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1467  * from which the running page was served.<br><br>
1468  * <p>
1469  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1470  * <p>
1471  * Be aware that to enable the browser to parse an XML document, the server must set
1472  * the Content-Type header in the HTTP response to "text/xml".
1473  * @constructor
1474  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1475  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1476  * will be used to make the request.
1477  */
1478 Roo.data.HttpProxy = function(conn){
1479     Roo.data.HttpProxy.superclass.constructor.call(this);
1480     // is conn a conn config or a real conn?
1481     this.conn = conn;
1482     this.useAjax = !conn || !conn.events;
1483   
1484 };
1485
1486 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1487     // thse are take from connection...
1488     
1489     /**
1490      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1491      */
1492     /**
1493      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1494      * extra parameters to each request made by this object. (defaults to undefined)
1495      */
1496     /**
1497      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1498      *  to each request made by this object. (defaults to undefined)
1499      */
1500     /**
1501      * @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)
1502      */
1503     /**
1504      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1505      */
1506      /**
1507      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1508      * @type Boolean
1509      */
1510   
1511
1512     /**
1513      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1514      * @type Boolean
1515      */
1516     /**
1517      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1518      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1519      * a finer-grained basis than the DataProxy events.
1520      */
1521     getConnection : function(){
1522         return this.useAjax ? Roo.Ajax : this.conn;
1523     },
1524
1525     /**
1526      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1527      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1528      * process that block using the passed callback.
1529      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1530      * for the request to the remote server.
1531      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1532      * object into a block of Roo.data.Records.
1533      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1534      * The function must be passed <ul>
1535      * <li>The Record block object</li>
1536      * <li>The "arg" argument from the load function</li>
1537      * <li>A boolean success indicator</li>
1538      * </ul>
1539      * @param {Object} scope The scope in which to call the callback
1540      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1541      */
1542     load : function(params, reader, callback, scope, arg){
1543         if(this.fireEvent("beforeload", this, params) !== false){
1544             var  o = {
1545                 params : params || {},
1546                 request: {
1547                     callback : callback,
1548                     scope : scope,
1549                     arg : arg
1550                 },
1551                 reader: reader,
1552                 callback : this.loadResponse,
1553                 scope: this
1554             };
1555             if(this.useAjax){
1556                 Roo.applyIf(o, this.conn);
1557                 if(this.activeRequest){
1558                     Roo.Ajax.abort(this.activeRequest);
1559                 }
1560                 this.activeRequest = Roo.Ajax.request(o);
1561             }else{
1562                 this.conn.request(o);
1563             }
1564         }else{
1565             callback.call(scope||this, null, arg, false);
1566         }
1567     },
1568
1569     // private
1570     loadResponse : function(o, success, response){
1571         delete this.activeRequest;
1572         if(!success){
1573             this.fireEvent("loadexception", this, o, response);
1574             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1575             return;
1576         }
1577         var result;
1578         try {
1579             result = o.reader.read(response);
1580         }catch(e){
1581             o.success = false;
1582             o.raw = { errorMsg : response.responseText };
1583             this.fireEvent("loadexception", this, o, response, e);
1584             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1585             return;
1586         }
1587         
1588         this.fireEvent("load", this, o, o.request.arg);
1589         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1590     },
1591
1592     // private
1593     update : function(dataSet){
1594
1595     },
1596
1597     // private
1598     updateResponse : function(dataSet){
1599
1600     }
1601 });/*
1602  * Based on:
1603  * Ext JS Library 1.1.1
1604  * Copyright(c) 2006-2007, Ext JS, LLC.
1605  *
1606  * Originally Released Under LGPL - original licence link has changed is not relivant.
1607  *
1608  * Fork - LGPL
1609  * <script type="text/javascript">
1610  */
1611
1612 /**
1613  * @class Roo.data.ScriptTagProxy
1614  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1615  * other than the originating domain of the running page.<br><br>
1616  * <p>
1617  * <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
1618  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1619  * <p>
1620  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1621  * source code that is used as the source inside a &lt;script> tag.<br><br>
1622  * <p>
1623  * In order for the browser to process the returned data, the server must wrap the data object
1624  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1625  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1626  * depending on whether the callback name was passed:
1627  * <p>
1628  * <pre><code>
1629 boolean scriptTag = false;
1630 String cb = request.getParameter("callback");
1631 if (cb != null) {
1632     scriptTag = true;
1633     response.setContentType("text/javascript");
1634 } else {
1635     response.setContentType("application/x-json");
1636 }
1637 Writer out = response.getWriter();
1638 if (scriptTag) {
1639     out.write(cb + "(");
1640 }
1641 out.print(dataBlock.toJsonString());
1642 if (scriptTag) {
1643     out.write(");");
1644 }
1645 </pre></code>
1646  *
1647  * @constructor
1648  * @param {Object} config A configuration object.
1649  */
1650 Roo.data.ScriptTagProxy = function(config){
1651     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1652     Roo.apply(this, config);
1653     this.head = document.getElementsByTagName("head")[0];
1654 };
1655
1656 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1657
1658 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1659     /**
1660      * @cfg {String} url The URL from which to request the data object.
1661      */
1662     /**
1663      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1664      */
1665     timeout : 30000,
1666     /**
1667      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1668      * the server the name of the callback function set up by the load call to process the returned data object.
1669      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1670      * javascript output which calls this named function passing the data object as its only parameter.
1671      */
1672     callbackParam : "callback",
1673     /**
1674      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1675      * name to the request.
1676      */
1677     nocache : true,
1678
1679     /**
1680      * Load data from the configured URL, read the data object into
1681      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1682      * process that block using the passed callback.
1683      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1684      * for the request to the remote server.
1685      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1686      * object into a block of Roo.data.Records.
1687      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1688      * The function must be passed <ul>
1689      * <li>The Record block object</li>
1690      * <li>The "arg" argument from the load function</li>
1691      * <li>A boolean success indicator</li>
1692      * </ul>
1693      * @param {Object} scope The scope in which to call the callback
1694      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1695      */
1696     load : function(params, reader, callback, scope, arg){
1697         if(this.fireEvent("beforeload", this, params) !== false){
1698
1699             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1700
1701             var url = this.url;
1702             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1703             if(this.nocache){
1704                 url += "&_dc=" + (new Date().getTime());
1705             }
1706             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1707             var trans = {
1708                 id : transId,
1709                 cb : "stcCallback"+transId,
1710                 scriptId : "stcScript"+transId,
1711                 params : params,
1712                 arg : arg,
1713                 url : url,
1714                 callback : callback,
1715                 scope : scope,
1716                 reader : reader
1717             };
1718             var conn = this;
1719
1720             window[trans.cb] = function(o){
1721                 conn.handleResponse(o, trans);
1722             };
1723
1724             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1725
1726             if(this.autoAbort !== false){
1727                 this.abort();
1728             }
1729
1730             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1731
1732             var script = document.createElement("script");
1733             script.setAttribute("src", url);
1734             script.setAttribute("type", "text/javascript");
1735             script.setAttribute("id", trans.scriptId);
1736             this.head.appendChild(script);
1737
1738             this.trans = trans;
1739         }else{
1740             callback.call(scope||this, null, arg, false);
1741         }
1742     },
1743
1744     // private
1745     isLoading : function(){
1746         return this.trans ? true : false;
1747     },
1748
1749     /**
1750      * Abort the current server request.
1751      */
1752     abort : function(){
1753         if(this.isLoading()){
1754             this.destroyTrans(this.trans);
1755         }
1756     },
1757
1758     // private
1759     destroyTrans : function(trans, isLoaded){
1760         this.head.removeChild(document.getElementById(trans.scriptId));
1761         clearTimeout(trans.timeoutId);
1762         if(isLoaded){
1763             window[trans.cb] = undefined;
1764             try{
1765                 delete window[trans.cb];
1766             }catch(e){}
1767         }else{
1768             // if hasn't been loaded, wait for load to remove it to prevent script error
1769             window[trans.cb] = function(){
1770                 window[trans.cb] = undefined;
1771                 try{
1772                     delete window[trans.cb];
1773                 }catch(e){}
1774             };
1775         }
1776     },
1777
1778     // private
1779     handleResponse : function(o, trans){
1780         this.trans = false;
1781         this.destroyTrans(trans, true);
1782         var result;
1783         try {
1784             result = trans.reader.readRecords(o);
1785         }catch(e){
1786             this.fireEvent("loadexception", this, o, trans.arg, e);
1787             trans.callback.call(trans.scope||window, null, trans.arg, false);
1788             return;
1789         }
1790         this.fireEvent("load", this, o, trans.arg);
1791         trans.callback.call(trans.scope||window, result, trans.arg, true);
1792     },
1793
1794     // private
1795     handleFailure : function(trans){
1796         this.trans = false;
1797         this.destroyTrans(trans, false);
1798         this.fireEvent("loadexception", this, null, trans.arg);
1799         trans.callback.call(trans.scope||window, null, trans.arg, false);
1800     }
1801 });/*
1802  * Based on:
1803  * Ext JS Library 1.1.1
1804  * Copyright(c) 2006-2007, Ext JS, LLC.
1805  *
1806  * Originally Released Under LGPL - original licence link has changed is not relivant.
1807  *
1808  * Fork - LGPL
1809  * <script type="text/javascript">
1810  */
1811
1812 /**
1813  * @class Roo.data.JsonReader
1814  * @extends Roo.data.DataReader
1815  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1816  * based on mappings in a provided Roo.data.Record constructor.
1817  * 
1818  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1819  * in the reply previously. 
1820  * 
1821  * <p>
1822  * Example code:
1823  * <pre><code>
1824 var RecordDef = Roo.data.Record.create([
1825     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1826     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1827 ]);
1828 var myReader = new Roo.data.JsonReader({
1829     totalProperty: "results",    // The property which contains the total dataset size (optional)
1830     root: "rows",                // The property which contains an Array of row objects
1831     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1832 }, RecordDef);
1833 </code></pre>
1834  * <p>
1835  * This would consume a JSON file like this:
1836  * <pre><code>
1837 { 'results': 2, 'rows': [
1838     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1839     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1840 }
1841 </code></pre>
1842  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1843  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1844  * paged from the remote server.
1845  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1846  * @cfg {String} root name of the property which contains the Array of row objects.
1847  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1848  * @cfg {Array} fields Array of field definition objects
1849  * @constructor
1850  * Create a new JsonReader
1851  * @param {Object} meta Metadata configuration options
1852  * @param {Object} recordType Either an Array of field definition objects,
1853  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1854  */
1855 Roo.data.JsonReader = function(meta, recordType){
1856     
1857     meta = meta || {};
1858     // set some defaults:
1859     Roo.applyIf(meta, {
1860         totalProperty: 'total',
1861         successProperty : 'success',
1862         root : 'data',
1863         id : 'id'
1864     });
1865     
1866     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1867 };
1868 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1869     
1870     readerType : 'Json',
1871     
1872     /**
1873      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1874      * Used by Store query builder to append _requestMeta to params.
1875      * 
1876      */
1877     metaFromRemote : false,
1878     /**
1879      * This method is only used by a DataProxy which has retrieved data from a remote server.
1880      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1881      * @return {Object} data A data block which is used by an Roo.data.Store object as
1882      * a cache of Roo.data.Records.
1883      */
1884     read : function(response){
1885         var json = response.responseText;
1886        
1887         var o = /* eval:var:o */ eval("("+json+")");
1888         if(!o) {
1889             throw {message: "JsonReader.read: Json object not found"};
1890         }
1891         
1892         if(o.metaData){
1893             
1894             delete this.ef;
1895             this.metaFromRemote = true;
1896             this.meta = o.metaData;
1897             this.recordType = Roo.data.Record.create(o.metaData.fields);
1898             this.onMetaChange(this.meta, this.recordType, o);
1899         }
1900         return this.readRecords(o);
1901     },
1902
1903     // private function a store will implement
1904     onMetaChange : function(meta, recordType, o){
1905
1906     },
1907
1908     /**
1909          * @ignore
1910          */
1911     simpleAccess: function(obj, subsc) {
1912         return obj[subsc];
1913     },
1914
1915         /**
1916          * @ignore
1917          */
1918     getJsonAccessor: function(){
1919         var re = /[\[\.]/;
1920         return function(expr) {
1921             try {
1922                 return(re.test(expr))
1923                     ? new Function("obj", "return obj." + expr)
1924                     : function(obj){
1925                         return obj[expr];
1926                     };
1927             } catch(e){}
1928             return Roo.emptyFn;
1929         };
1930     }(),
1931
1932     /**
1933      * Create a data block containing Roo.data.Records from an XML document.
1934      * @param {Object} o An object which contains an Array of row objects in the property specified
1935      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1936      * which contains the total size of the dataset.
1937      * @return {Object} data A data block which is used by an Roo.data.Store object as
1938      * a cache of Roo.data.Records.
1939      */
1940     readRecords : function(o){
1941         /**
1942          * After any data loads, the raw JSON data is available for further custom processing.
1943          * @type Object
1944          */
1945         this.o = o;
1946         var s = this.meta, Record = this.recordType,
1947             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1948
1949 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1950         if (!this.ef) {
1951             if(s.totalProperty) {
1952                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1953                 }
1954                 if(s.successProperty) {
1955                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1956                 }
1957                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1958                 if (s.id) {
1959                         var g = this.getJsonAccessor(s.id);
1960                         this.getId = function(rec) {
1961                                 var r = g(rec);  
1962                                 return (r === undefined || r === "") ? null : r;
1963                         };
1964                 } else {
1965                         this.getId = function(){return null;};
1966                 }
1967             this.ef = [];
1968             for(var jj = 0; jj < fl; jj++){
1969                 f = fi[jj];
1970                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1971                 this.ef[jj] = this.getJsonAccessor(map);
1972             }
1973         }
1974
1975         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1976         if(s.totalProperty){
1977             var vt = parseInt(this.getTotal(o), 10);
1978             if(!isNaN(vt)){
1979                 totalRecords = vt;
1980             }
1981         }
1982         if(s.successProperty){
1983             var vs = this.getSuccess(o);
1984             if(vs === false || vs === 'false'){
1985                 success = false;
1986             }
1987         }
1988         var records = [];
1989         for(var i = 0; i < c; i++){
1990             var n = root[i];
1991             var values = {};
1992             var id = this.getId(n);
1993             for(var j = 0; j < fl; j++){
1994                 f = fi[j];
1995                                 var v = this.ef[j](n);
1996                                 if (!f.convert) {
1997                                         Roo.log('missing convert for ' + f.name);
1998                                         Roo.log(f);
1999                                         continue;
2000                                 }
2001                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
2002             }
2003                         if (!Record) {
2004                                 return {
2005                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2006                                         success : false,
2007                                         records : [],
2008                                         totalRecords : 0
2009                                 };
2010                         }
2011             var record = new Record(values, id);
2012             record.json = n;
2013             records[i] = record;
2014         }
2015         return {
2016             raw : o,
2017             success : success,
2018             records : records,
2019             totalRecords : totalRecords
2020         };
2021     },
2022     // used when loading children.. @see loadDataFromChildren
2023     toLoadData: function(rec)
2024     {
2025         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2026         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2027         return { data : data, total : data.length };
2028         
2029     }
2030 });/*
2031  * Based on:
2032  * Ext JS Library 1.1.1
2033  * Copyright(c) 2006-2007, Ext JS, LLC.
2034  *
2035  * Originally Released Under LGPL - original licence link has changed is not relivant.
2036  *
2037  * Fork - LGPL
2038  * <script type="text/javascript">
2039  */
2040
2041 /**
2042  * @class Roo.data.XmlReader
2043  * @extends Roo.data.DataReader
2044  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2045  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2046  * <p>
2047  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2048  * header in the HTTP response must be set to "text/xml".</em>
2049  * <p>
2050  * Example code:
2051  * <pre><code>
2052 var RecordDef = Roo.data.Record.create([
2053    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2054    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2055 ]);
2056 var myReader = new Roo.data.XmlReader({
2057    totalRecords: "results", // The element which contains the total dataset size (optional)
2058    record: "row",           // The repeated element which contains row information
2059    id: "id"                 // The element within the row that provides an ID for the record (optional)
2060 }, RecordDef);
2061 </code></pre>
2062  * <p>
2063  * This would consume an XML file like this:
2064  * <pre><code>
2065 &lt;?xml?>
2066 &lt;dataset>
2067  &lt;results>2&lt;/results>
2068  &lt;row>
2069    &lt;id>1&lt;/id>
2070    &lt;name>Bill&lt;/name>
2071    &lt;occupation>Gardener&lt;/occupation>
2072  &lt;/row>
2073  &lt;row>
2074    &lt;id>2&lt;/id>
2075    &lt;name>Ben&lt;/name>
2076    &lt;occupation>Horticulturalist&lt;/occupation>
2077  &lt;/row>
2078 &lt;/dataset>
2079 </code></pre>
2080  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2081  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2082  * paged from the remote server.
2083  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2084  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2085  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2086  * a record identifier value.
2087  * @constructor
2088  * Create a new XmlReader
2089  * @param {Object} meta Metadata configuration options
2090  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2091  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2092  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2093  */
2094 Roo.data.XmlReader = function(meta, recordType){
2095     meta = meta || {};
2096     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2097 };
2098 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2099     
2100     readerType : 'Xml',
2101     
2102     /**
2103      * This method is only used by a DataProxy which has retrieved data from a remote server.
2104          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2105          * to contain a method called 'responseXML' that returns an XML document object.
2106      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2107      * a cache of Roo.data.Records.
2108      */
2109     read : function(response){
2110         var doc = response.responseXML;
2111         if(!doc) {
2112             throw {message: "XmlReader.read: XML Document not available"};
2113         }
2114         return this.readRecords(doc);
2115     },
2116
2117     /**
2118      * Create a data block containing Roo.data.Records from an XML document.
2119          * @param {Object} doc A parsed XML document.
2120      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2121      * a cache of Roo.data.Records.
2122      */
2123     readRecords : function(doc){
2124         /**
2125          * After any data loads/reads, the raw XML Document is available for further custom processing.
2126          * @type XMLDocument
2127          */
2128         this.xmlData = doc;
2129         var root = doc.documentElement || doc;
2130         var q = Roo.DomQuery;
2131         var recordType = this.recordType, fields = recordType.prototype.fields;
2132         var sid = this.meta.id;
2133         var totalRecords = 0, success = true;
2134         if(this.meta.totalRecords){
2135             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2136         }
2137         
2138         if(this.meta.success){
2139             var sv = q.selectValue(this.meta.success, root, true);
2140             success = sv !== false && sv !== 'false';
2141         }
2142         var records = [];
2143         var ns = q.select(this.meta.record, root);
2144         for(var i = 0, len = ns.length; i < len; i++) {
2145                 var n = ns[i];
2146                 var values = {};
2147                 var id = sid ? q.selectValue(sid, n) : undefined;
2148                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2149                     var f = fields.items[j];
2150                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2151                     v = f.convert(v);
2152                     values[f.name] = v;
2153                 }
2154                 var record = new recordType(values, id);
2155                 record.node = n;
2156                 records[records.length] = record;
2157             }
2158
2159             return {
2160                 success : success,
2161                 records : records,
2162                 totalRecords : totalRecords || records.length
2163             };
2164     }
2165 });/*
2166  * Based on:
2167  * Ext JS Library 1.1.1
2168  * Copyright(c) 2006-2007, Ext JS, LLC.
2169  *
2170  * Originally Released Under LGPL - original licence link has changed is not relivant.
2171  *
2172  * Fork - LGPL
2173  * <script type="text/javascript">
2174  */
2175
2176 /**
2177  * @class Roo.data.ArrayReader
2178  * @extends Roo.data.DataReader
2179  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2180  * Each element of that Array represents a row of data fields. The
2181  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2182  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2183  * <p>
2184  * Example code:.
2185  * <pre><code>
2186 var RecordDef = Roo.data.Record.create([
2187     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2188     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2189 ]);
2190 var myReader = new Roo.data.ArrayReader({
2191     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2192 }, RecordDef);
2193 </code></pre>
2194  * <p>
2195  * This would consume an Array like this:
2196  * <pre><code>
2197 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2198   </code></pre>
2199  
2200  * @constructor
2201  * Create a new JsonReader
2202  * @param {Object} meta Metadata configuration options.
2203  * @param {Object|Array} recordType Either an Array of field definition objects
2204  * 
2205  * @cfg {Array} fields Array of field definition objects
2206  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2207  * as specified to {@link Roo.data.Record#create},
2208  * or an {@link Roo.data.Record} object
2209  *
2210  * 
2211  * created using {@link Roo.data.Record#create}.
2212  */
2213 Roo.data.ArrayReader = function(meta, recordType)
2214 {    
2215     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2216 };
2217
2218 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2219     
2220       /**
2221      * Create a data block containing Roo.data.Records from an XML document.
2222      * @param {Object} o An Array of row objects which represents the dataset.
2223      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2224      * a cache of Roo.data.Records.
2225      */
2226     readRecords : function(o)
2227     {
2228         var sid = this.meta ? this.meta.id : null;
2229         var recordType = this.recordType, fields = recordType.prototype.fields;
2230         var records = [];
2231         var root = o;
2232         for(var i = 0; i < root.length; i++){
2233             var n = root[i];
2234             var values = {};
2235             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2236             for(var j = 0, jlen = fields.length; j < jlen; j++){
2237                 var f = fields.items[j];
2238                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2239                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2240                 v = f.convert(v);
2241                 values[f.name] = v;
2242             }
2243             var record = new recordType(values, id);
2244             record.json = n;
2245             records[records.length] = record;
2246         }
2247         return {
2248             records : records,
2249             totalRecords : records.length
2250         };
2251     },
2252     // used when loading children.. @see loadDataFromChildren
2253     toLoadData: function(rec)
2254     {
2255         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2256         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2257         
2258     }
2259     
2260     
2261 });/*
2262  * Based on:
2263  * Ext JS Library 1.1.1
2264  * Copyright(c) 2006-2007, Ext JS, LLC.
2265  *
2266  * Originally Released Under LGPL - original licence link has changed is not relivant.
2267  *
2268  * Fork - LGPL
2269  * <script type="text/javascript">
2270  */
2271
2272
2273 /**
2274  * @class Roo.data.Tree
2275  * @extends Roo.util.Observable
2276  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2277  * in the tree have most standard DOM functionality.
2278  * @constructor
2279  * @param {Node} root (optional) The root node
2280  */
2281 Roo.data.Tree = function(root){
2282    this.nodeHash = {};
2283    /**
2284     * The root node for this tree
2285     * @type Node
2286     */
2287    this.root = null;
2288    if(root){
2289        this.setRootNode(root);
2290    }
2291    this.addEvents({
2292        /**
2293         * @event append
2294         * Fires when a new child node is appended to a node in this tree.
2295         * @param {Tree} tree The owner tree
2296         * @param {Node} parent The parent node
2297         * @param {Node} node The newly appended node
2298         * @param {Number} index The index of the newly appended node
2299         */
2300        "append" : true,
2301        /**
2302         * @event remove
2303         * Fires when a child node is removed from a node in this tree.
2304         * @param {Tree} tree The owner tree
2305         * @param {Node} parent The parent node
2306         * @param {Node} node The child node removed
2307         */
2308        "remove" : true,
2309        /**
2310         * @event move
2311         * Fires when a node is moved to a new location in the tree
2312         * @param {Tree} tree The owner tree
2313         * @param {Node} node The node moved
2314         * @param {Node} oldParent The old parent of this node
2315         * @param {Node} newParent The new parent of this node
2316         * @param {Number} index The index it was moved to
2317         */
2318        "move" : true,
2319        /**
2320         * @event insert
2321         * Fires when a new child node is inserted in a node in this tree.
2322         * @param {Tree} tree The owner tree
2323         * @param {Node} parent The parent node
2324         * @param {Node} node The child node inserted
2325         * @param {Node} refNode The child node the node was inserted before
2326         */
2327        "insert" : true,
2328        /**
2329         * @event beforeappend
2330         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2331         * @param {Tree} tree The owner tree
2332         * @param {Node} parent The parent node
2333         * @param {Node} node The child node to be appended
2334         */
2335        "beforeappend" : true,
2336        /**
2337         * @event beforeremove
2338         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2339         * @param {Tree} tree The owner tree
2340         * @param {Node} parent The parent node
2341         * @param {Node} node The child node to be removed
2342         */
2343        "beforeremove" : true,
2344        /**
2345         * @event beforemove
2346         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2347         * @param {Tree} tree The owner tree
2348         * @param {Node} node The node being moved
2349         * @param {Node} oldParent The parent of the node
2350         * @param {Node} newParent The new parent the node is moving to
2351         * @param {Number} index The index it is being moved to
2352         */
2353        "beforemove" : true,
2354        /**
2355         * @event beforeinsert
2356         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2357         * @param {Tree} tree The owner tree
2358         * @param {Node} parent The parent node
2359         * @param {Node} node The child node to be inserted
2360         * @param {Node} refNode The child node the node is being inserted before
2361         */
2362        "beforeinsert" : true
2363    });
2364
2365     Roo.data.Tree.superclass.constructor.call(this);
2366 };
2367
2368 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2369     pathSeparator: "/",
2370
2371     proxyNodeEvent : function(){
2372         return this.fireEvent.apply(this, arguments);
2373     },
2374
2375     /**
2376      * Returns the root node for this tree.
2377      * @return {Node}
2378      */
2379     getRootNode : function(){
2380         return this.root;
2381     },
2382
2383     /**
2384      * Sets the root node for this tree.
2385      * @param {Node} node
2386      * @return {Node}
2387      */
2388     setRootNode : function(node){
2389         this.root = node;
2390         node.ownerTree = this;
2391         node.isRoot = true;
2392         this.registerNode(node);
2393         return node;
2394     },
2395
2396     /**
2397      * Gets a node in this tree by its id.
2398      * @param {String} id
2399      * @return {Node}
2400      */
2401     getNodeById : function(id){
2402         return this.nodeHash[id];
2403     },
2404
2405     registerNode : function(node){
2406         this.nodeHash[node.id] = node;
2407     },
2408
2409     unregisterNode : function(node){
2410         delete this.nodeHash[node.id];
2411     },
2412
2413     toString : function(){
2414         return "[Tree"+(this.id?" "+this.id:"")+"]";
2415     }
2416 });
2417
2418 /**
2419  * @class Roo.data.Node
2420  * @extends Roo.util.Observable
2421  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2422  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2423  * @constructor
2424  * @param {Object} attributes The attributes/config for the node
2425  */
2426 Roo.data.Node = function(attributes){
2427     /**
2428      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2429      * @type {Object}
2430      */
2431     this.attributes = attributes || {};
2432     this.leaf = this.attributes.leaf;
2433     /**
2434      * The node id. @type String
2435      */
2436     this.id = this.attributes.id;
2437     if(!this.id){
2438         this.id = Roo.id(null, "ynode-");
2439         this.attributes.id = this.id;
2440     }
2441      
2442     
2443     /**
2444      * All child nodes of this node. @type Array
2445      */
2446     this.childNodes = [];
2447     if(!this.childNodes.indexOf){ // indexOf is a must
2448         this.childNodes.indexOf = function(o){
2449             for(var i = 0, len = this.length; i < len; i++){
2450                 if(this[i] == o) {
2451                     return i;
2452                 }
2453             }
2454             return -1;
2455         };
2456     }
2457     /**
2458      * The parent node for this node. @type Node
2459      */
2460     this.parentNode = null;
2461     /**
2462      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2463      */
2464     this.firstChild = null;
2465     /**
2466      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2467      */
2468     this.lastChild = null;
2469     /**
2470      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2471      */
2472     this.previousSibling = null;
2473     /**
2474      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2475      */
2476     this.nextSibling = null;
2477
2478     this.addEvents({
2479        /**
2480         * @event append
2481         * Fires when a new child node is appended
2482         * @param {Tree} tree The owner tree
2483         * @param {Node} this This node
2484         * @param {Node} node The newly appended node
2485         * @param {Number} index The index of the newly appended node
2486         */
2487        "append" : true,
2488        /**
2489         * @event remove
2490         * Fires when a child node is removed
2491         * @param {Tree} tree The owner tree
2492         * @param {Node} this This node
2493         * @param {Node} node The removed node
2494         */
2495        "remove" : true,
2496        /**
2497         * @event move
2498         * Fires when this node is moved to a new location in the tree
2499         * @param {Tree} tree The owner tree
2500         * @param {Node} this This node
2501         * @param {Node} oldParent The old parent of this node
2502         * @param {Node} newParent The new parent of this node
2503         * @param {Number} index The index it was moved to
2504         */
2505        "move" : true,
2506        /**
2507         * @event insert
2508         * Fires when a new child node is inserted.
2509         * @param {Tree} tree The owner tree
2510         * @param {Node} this This node
2511         * @param {Node} node The child node inserted
2512         * @param {Node} refNode The child node the node was inserted before
2513         */
2514        "insert" : true,
2515        /**
2516         * @event beforeappend
2517         * Fires before a new child is appended, return false to cancel the append.
2518         * @param {Tree} tree The owner tree
2519         * @param {Node} this This node
2520         * @param {Node} node The child node to be appended
2521         */
2522        "beforeappend" : true,
2523        /**
2524         * @event beforeremove
2525         * Fires before a child is removed, return false to cancel the remove.
2526         * @param {Tree} tree The owner tree
2527         * @param {Node} this This node
2528         * @param {Node} node The child node to be removed
2529         */
2530        "beforeremove" : true,
2531        /**
2532         * @event beforemove
2533         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2534         * @param {Tree} tree The owner tree
2535         * @param {Node} this This node
2536         * @param {Node} oldParent The parent of this node
2537         * @param {Node} newParent The new parent this node is moving to
2538         * @param {Number} index The index it is being moved to
2539         */
2540        "beforemove" : true,
2541        /**
2542         * @event beforeinsert
2543         * Fires before a new child is inserted, return false to cancel the insert.
2544         * @param {Tree} tree The owner tree
2545         * @param {Node} this This node
2546         * @param {Node} node The child node to be inserted
2547         * @param {Node} refNode The child node the node is being inserted before
2548         */
2549        "beforeinsert" : true
2550    });
2551     this.listeners = this.attributes.listeners;
2552     Roo.data.Node.superclass.constructor.call(this);
2553 };
2554
2555 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2556     fireEvent : function(evtName){
2557         // first do standard event for this node
2558         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2559             return false;
2560         }
2561         // then bubble it up to the tree if the event wasn't cancelled
2562         var ot = this.getOwnerTree();
2563         if(ot){
2564             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2565                 return false;
2566             }
2567         }
2568         return true;
2569     },
2570
2571     /**
2572      * Returns true if this node is a leaf
2573      * @return {Boolean}
2574      */
2575     isLeaf : function(){
2576         return this.leaf === true;
2577     },
2578
2579     // private
2580     setFirstChild : function(node){
2581         this.firstChild = node;
2582     },
2583
2584     //private
2585     setLastChild : function(node){
2586         this.lastChild = node;
2587     },
2588
2589
2590     /**
2591      * Returns true if this node is the last child of its parent
2592      * @return {Boolean}
2593      */
2594     isLast : function(){
2595        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2596     },
2597
2598     /**
2599      * Returns true if this node is the first child of its parent
2600      * @return {Boolean}
2601      */
2602     isFirst : function(){
2603        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2604     },
2605
2606     hasChildNodes : function(){
2607         return !this.isLeaf() && this.childNodes.length > 0;
2608     },
2609
2610     /**
2611      * Insert node(s) as the last child node of this node.
2612      * @param {Node/Array} node The node or Array of nodes to append
2613      * @return {Node} The appended node if single append, or null if an array was passed
2614      */
2615     appendChild : function(node){
2616         var multi = false;
2617         if(node instanceof Array){
2618             multi = node;
2619         }else if(arguments.length > 1){
2620             multi = arguments;
2621         }
2622         
2623         // if passed an array or multiple args do them one by one
2624         if(multi){
2625             for(var i = 0, len = multi.length; i < len; i++) {
2626                 this.appendChild(multi[i]);
2627             }
2628         }else{
2629             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2630                 return false;
2631             }
2632             var index = this.childNodes.length;
2633             var oldParent = node.parentNode;
2634             // it's a move, make sure we move it cleanly
2635             if(oldParent){
2636                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2637                     return false;
2638                 }
2639                 oldParent.removeChild(node);
2640             }
2641             
2642             index = this.childNodes.length;
2643             if(index == 0){
2644                 this.setFirstChild(node);
2645             }
2646             this.childNodes.push(node);
2647             node.parentNode = this;
2648             var ps = this.childNodes[index-1];
2649             if(ps){
2650                 node.previousSibling = ps;
2651                 ps.nextSibling = node;
2652             }else{
2653                 node.previousSibling = null;
2654             }
2655             node.nextSibling = null;
2656             this.setLastChild(node);
2657             node.setOwnerTree(this.getOwnerTree());
2658             this.fireEvent("append", this.ownerTree, this, node, index);
2659             if(this.ownerTree) {
2660                 this.ownerTree.fireEvent("appendnode", this, node, index);
2661             }
2662             if(oldParent){
2663                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2664             }
2665             return node;
2666         }
2667     },
2668
2669     /**
2670      * Removes a child node from this node.
2671      * @param {Node} node The node to remove
2672      * @return {Node} The removed node
2673      */
2674     removeChild : function(node){
2675         var index = this.childNodes.indexOf(node);
2676         if(index == -1){
2677             return false;
2678         }
2679         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2680             return false;
2681         }
2682
2683         // remove it from childNodes collection
2684         this.childNodes.splice(index, 1);
2685
2686         // update siblings
2687         if(node.previousSibling){
2688             node.previousSibling.nextSibling = node.nextSibling;
2689         }
2690         if(node.nextSibling){
2691             node.nextSibling.previousSibling = node.previousSibling;
2692         }
2693
2694         // update child refs
2695         if(this.firstChild == node){
2696             this.setFirstChild(node.nextSibling);
2697         }
2698         if(this.lastChild == node){
2699             this.setLastChild(node.previousSibling);
2700         }
2701
2702         node.setOwnerTree(null);
2703         // clear any references from the node
2704         node.parentNode = null;
2705         node.previousSibling = null;
2706         node.nextSibling = null;
2707         this.fireEvent("remove", this.ownerTree, this, node);
2708         return node;
2709     },
2710
2711     /**
2712      * Inserts the first node before the second node in this nodes childNodes collection.
2713      * @param {Node} node The node to insert
2714      * @param {Node} refNode The node to insert before (if null the node is appended)
2715      * @return {Node} The inserted node
2716      */
2717     insertBefore : function(node, refNode){
2718         if(!refNode){ // like standard Dom, refNode can be null for append
2719             return this.appendChild(node);
2720         }
2721         // nothing to do
2722         if(node == refNode){
2723             return false;
2724         }
2725
2726         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2727             return false;
2728         }
2729         var index = this.childNodes.indexOf(refNode);
2730         var oldParent = node.parentNode;
2731         var refIndex = index;
2732
2733         // when moving internally, indexes will change after remove
2734         if(oldParent == this && this.childNodes.indexOf(node) < index){
2735             refIndex--;
2736         }
2737
2738         // it's a move, make sure we move it cleanly
2739         if(oldParent){
2740             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2741                 return false;
2742             }
2743             oldParent.removeChild(node);
2744         }
2745         if(refIndex == 0){
2746             this.setFirstChild(node);
2747         }
2748         this.childNodes.splice(refIndex, 0, node);
2749         node.parentNode = this;
2750         var ps = this.childNodes[refIndex-1];
2751         if(ps){
2752             node.previousSibling = ps;
2753             ps.nextSibling = node;
2754         }else{
2755             node.previousSibling = null;
2756         }
2757         node.nextSibling = refNode;
2758         refNode.previousSibling = node;
2759         node.setOwnerTree(this.getOwnerTree());
2760         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2761         if(oldParent){
2762             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2763         }
2764         return node;
2765     },
2766
2767     /**
2768      * Returns the child node at the specified index.
2769      * @param {Number} index
2770      * @return {Node}
2771      */
2772     item : function(index){
2773         return this.childNodes[index];
2774     },
2775
2776     /**
2777      * Replaces one child node in this node with another.
2778      * @param {Node} newChild The replacement node
2779      * @param {Node} oldChild The node to replace
2780      * @return {Node} The replaced node
2781      */
2782     replaceChild : function(newChild, oldChild){
2783         this.insertBefore(newChild, oldChild);
2784         this.removeChild(oldChild);
2785         return oldChild;
2786     },
2787
2788     /**
2789      * Returns the index of a child node
2790      * @param {Node} node
2791      * @return {Number} The index of the node or -1 if it was not found
2792      */
2793     indexOf : function(child){
2794         return this.childNodes.indexOf(child);
2795     },
2796
2797     /**
2798      * Returns the tree this node is in.
2799      * @return {Tree}
2800      */
2801     getOwnerTree : function(){
2802         // if it doesn't have one, look for one
2803         if(!this.ownerTree){
2804             var p = this;
2805             while(p){
2806                 if(p.ownerTree){
2807                     this.ownerTree = p.ownerTree;
2808                     break;
2809                 }
2810                 p = p.parentNode;
2811             }
2812         }
2813         return this.ownerTree;
2814     },
2815
2816     /**
2817      * Returns depth of this node (the root node has a depth of 0)
2818      * @return {Number}
2819      */
2820     getDepth : function(){
2821         var depth = 0;
2822         var p = this;
2823         while(p.parentNode){
2824             ++depth;
2825             p = p.parentNode;
2826         }
2827         return depth;
2828     },
2829
2830     // private
2831     setOwnerTree : function(tree){
2832         // if it's move, we need to update everyone
2833         if(tree != this.ownerTree){
2834             if(this.ownerTree){
2835                 this.ownerTree.unregisterNode(this);
2836             }
2837             this.ownerTree = tree;
2838             var cs = this.childNodes;
2839             for(var i = 0, len = cs.length; i < len; i++) {
2840                 cs[i].setOwnerTree(tree);
2841             }
2842             if(tree){
2843                 tree.registerNode(this);
2844             }
2845         }
2846     },
2847
2848     /**
2849      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2850      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2851      * @return {String} The path
2852      */
2853     getPath : function(attr){
2854         attr = attr || "id";
2855         var p = this.parentNode;
2856         var b = [this.attributes[attr]];
2857         while(p){
2858             b.unshift(p.attributes[attr]);
2859             p = p.parentNode;
2860         }
2861         var sep = this.getOwnerTree().pathSeparator;
2862         return sep + b.join(sep);
2863     },
2864
2865     /**
2866      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2867      * function call will be the scope provided or the current node. The arguments to the function
2868      * will be the args provided or the current node. If the function returns false at any point,
2869      * the bubble is stopped.
2870      * @param {Function} fn The function to call
2871      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2872      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2873      */
2874     bubble : function(fn, scope, args){
2875         var p = this;
2876         while(p){
2877             if(fn.call(scope || p, args || p) === false){
2878                 break;
2879             }
2880             p = p.parentNode;
2881         }
2882     },
2883
2884     /**
2885      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2886      * function call will be the scope provided or the current node. The arguments to the function
2887      * will be the args provided or the current node. If the function returns false at any point,
2888      * the cascade is stopped on that branch.
2889      * @param {Function} fn The function to call
2890      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2891      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2892      */
2893     cascade : function(fn, scope, args){
2894         if(fn.call(scope || this, args || this) !== false){
2895             var cs = this.childNodes;
2896             for(var i = 0, len = cs.length; i < len; i++) {
2897                 cs[i].cascade(fn, scope, args);
2898             }
2899         }
2900     },
2901
2902     /**
2903      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2904      * function call will be the scope provided or the current node. The arguments to the function
2905      * will be the args provided or the current node. If the function returns false at any point,
2906      * the iteration stops.
2907      * @param {Function} fn The function to call
2908      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2909      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2910      */
2911     eachChild : function(fn, scope, args){
2912         var cs = this.childNodes;
2913         for(var i = 0, len = cs.length; i < len; i++) {
2914                 if(fn.call(scope || this, args || cs[i]) === false){
2915                     break;
2916                 }
2917         }
2918     },
2919
2920     /**
2921      * Finds the first child that has the attribute with the specified value.
2922      * @param {String} attribute The attribute name
2923      * @param {Mixed} value The value to search for
2924      * @return {Node} The found child or null if none was found
2925      */
2926     findChild : function(attribute, value){
2927         var cs = this.childNodes;
2928         for(var i = 0, len = cs.length; i < len; i++) {
2929                 if(cs[i].attributes[attribute] == value){
2930                     return cs[i];
2931                 }
2932         }
2933         return null;
2934     },
2935
2936     /**
2937      * Finds the first child by a custom function. The child matches if the function passed
2938      * returns true.
2939      * @param {Function} fn
2940      * @param {Object} scope (optional)
2941      * @return {Node} The found child or null if none was found
2942      */
2943     findChildBy : function(fn, scope){
2944         var cs = this.childNodes;
2945         for(var i = 0, len = cs.length; i < len; i++) {
2946                 if(fn.call(scope||cs[i], cs[i]) === true){
2947                     return cs[i];
2948                 }
2949         }
2950         return null;
2951     },
2952
2953     /**
2954      * Sorts this nodes children using the supplied sort function
2955      * @param {Function} fn
2956      * @param {Object} scope (optional)
2957      */
2958     sort : function(fn, scope){
2959         var cs = this.childNodes;
2960         var len = cs.length;
2961         if(len > 0){
2962             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2963             cs.sort(sortFn);
2964             for(var i = 0; i < len; i++){
2965                 var n = cs[i];
2966                 n.previousSibling = cs[i-1];
2967                 n.nextSibling = cs[i+1];
2968                 if(i == 0){
2969                     this.setFirstChild(n);
2970                 }
2971                 if(i == len-1){
2972                     this.setLastChild(n);
2973                 }
2974             }
2975         }
2976     },
2977
2978     /**
2979      * Returns true if this node is an ancestor (at any point) of the passed node.
2980      * @param {Node} node
2981      * @return {Boolean}
2982      */
2983     contains : function(node){
2984         return node.isAncestor(this);
2985     },
2986
2987     /**
2988      * Returns true if the passed node is an ancestor (at any point) of this node.
2989      * @param {Node} node
2990      * @return {Boolean}
2991      */
2992     isAncestor : function(node){
2993         var p = this.parentNode;
2994         while(p){
2995             if(p == node){
2996                 return true;
2997             }
2998             p = p.parentNode;
2999         }
3000         return false;
3001     },
3002
3003     toString : function(){
3004         return "[Node"+(this.id?" "+this.id:"")+"]";
3005     }
3006 });/*
3007  * Based on:
3008  * Ext JS Library 1.1.1
3009  * Copyright(c) 2006-2007, Ext JS, LLC.
3010  *
3011  * Originally Released Under LGPL - original licence link has changed is not relivant.
3012  *
3013  * Fork - LGPL
3014  * <script type="text/javascript">
3015  */
3016
3017
3018 /**
3019  * @class Roo.Shadow
3020  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3021  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3022  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3023  * @constructor
3024  * Create a new Shadow
3025  * @param {Object} config The config object
3026  */
3027 Roo.Shadow = function(config){
3028     Roo.apply(this, config);
3029     if(typeof this.mode != "string"){
3030         this.mode = this.defaultMode;
3031     }
3032     var o = this.offset, a = {h: 0};
3033     var rad = Math.floor(this.offset/2);
3034     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3035         case "drop":
3036             a.w = 0;
3037             a.l = a.t = o;
3038             a.t -= 1;
3039             if(Roo.isIE){
3040                 a.l -= this.offset + rad;
3041                 a.t -= this.offset + rad;
3042                 a.w -= rad;
3043                 a.h -= rad;
3044                 a.t += 1;
3045             }
3046         break;
3047         case "sides":
3048             a.w = (o*2);
3049             a.l = -o;
3050             a.t = o-1;
3051             if(Roo.isIE){
3052                 a.l -= (this.offset - rad);
3053                 a.t -= this.offset + rad;
3054                 a.l += 1;
3055                 a.w -= (this.offset - rad)*2;
3056                 a.w -= rad + 1;
3057                 a.h -= 1;
3058             }
3059         break;
3060         case "frame":
3061             a.w = a.h = (o*2);
3062             a.l = a.t = -o;
3063             a.t += 1;
3064             a.h -= 2;
3065             if(Roo.isIE){
3066                 a.l -= (this.offset - rad);
3067                 a.t -= (this.offset - rad);
3068                 a.l += 1;
3069                 a.w -= (this.offset + rad + 1);
3070                 a.h -= (this.offset + rad);
3071                 a.h += 1;
3072             }
3073         break;
3074     };
3075
3076     this.adjusts = a;
3077 };
3078
3079 Roo.Shadow.prototype = {
3080     /**
3081      * @cfg {String} mode
3082      * The shadow display mode.  Supports the following options:<br />
3083      * sides: Shadow displays on both sides and bottom only<br />
3084      * frame: Shadow displays equally on all four sides<br />
3085      * drop: Traditional bottom-right drop shadow (default)
3086      */
3087     mode: false,
3088     /**
3089      * @cfg {String} offset
3090      * The number of pixels to offset the shadow from the element (defaults to 4)
3091      */
3092     offset: 4,
3093
3094     // private
3095     defaultMode: "drop",
3096
3097     /**
3098      * Displays the shadow under the target element
3099      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3100      */
3101     show : function(target){
3102         target = Roo.get(target);
3103         if(!this.el){
3104             this.el = Roo.Shadow.Pool.pull();
3105             if(this.el.dom.nextSibling != target.dom){
3106                 this.el.insertBefore(target);
3107             }
3108         }
3109         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3110         if(Roo.isIE){
3111             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3112         }
3113         this.realign(
3114             target.getLeft(true),
3115             target.getTop(true),
3116             target.getWidth(),
3117             target.getHeight()
3118         );
3119         this.el.dom.style.display = "block";
3120     },
3121
3122     /**
3123      * Returns true if the shadow is visible, else false
3124      */
3125     isVisible : function(){
3126         return this.el ? true : false;  
3127     },
3128
3129     /**
3130      * Direct alignment when values are already available. Show must be called at least once before
3131      * calling this method to ensure it is initialized.
3132      * @param {Number} left The target element left position
3133      * @param {Number} top The target element top position
3134      * @param {Number} width The target element width
3135      * @param {Number} height The target element height
3136      */
3137     realign : function(l, t, w, h){
3138         if(!this.el){
3139             return;
3140         }
3141         var a = this.adjusts, d = this.el.dom, s = d.style;
3142         var iea = 0;
3143         s.left = (l+a.l)+"px";
3144         s.top = (t+a.t)+"px";
3145         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3146  
3147         if(s.width != sws || s.height != shs){
3148             s.width = sws;
3149             s.height = shs;
3150             if(!Roo.isIE){
3151                 var cn = d.childNodes;
3152                 var sww = Math.max(0, (sw-12))+"px";
3153                 cn[0].childNodes[1].style.width = sww;
3154                 cn[1].childNodes[1].style.width = sww;
3155                 cn[2].childNodes[1].style.width = sww;
3156                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3157             }
3158         }
3159     },
3160
3161     /**
3162      * Hides this shadow
3163      */
3164     hide : function(){
3165         if(this.el){
3166             this.el.dom.style.display = "none";
3167             Roo.Shadow.Pool.push(this.el);
3168             delete this.el;
3169         }
3170     },
3171
3172     /**
3173      * Adjust the z-index of this shadow
3174      * @param {Number} zindex The new z-index
3175      */
3176     setZIndex : function(z){
3177         this.zIndex = z;
3178         if(this.el){
3179             this.el.setStyle("z-index", z);
3180         }
3181     }
3182 };
3183
3184 // Private utility class that manages the internal Shadow cache
3185 Roo.Shadow.Pool = function(){
3186     var p = [];
3187     var markup = Roo.isIE ?
3188                  '<div class="x-ie-shadow"></div>' :
3189                  '<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>';
3190     return {
3191         pull : function(){
3192             var sh = p.shift();
3193             if(!sh){
3194                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3195                 sh.autoBoxAdjust = false;
3196             }
3197             return sh;
3198         },
3199
3200         push : function(sh){
3201             p.push(sh);
3202         }
3203     };
3204 }();/*
3205  * Based on:
3206  * Ext JS Library 1.1.1
3207  * Copyright(c) 2006-2007, Ext JS, LLC.
3208  *
3209  * Originally Released Under LGPL - original licence link has changed is not relivant.
3210  *
3211  * Fork - LGPL
3212  * <script type="text/javascript">
3213  */
3214
3215
3216 /**
3217  * @class Roo.SplitBar
3218  * @extends Roo.util.Observable
3219  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3220  * <br><br>
3221  * Usage:
3222  * <pre><code>
3223 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3224                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3225 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3226 split.minSize = 100;
3227 split.maxSize = 600;
3228 split.animate = true;
3229 split.on('moved', splitterMoved);
3230 </code></pre>
3231  * @constructor
3232  * Create a new SplitBar
3233  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3234  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3235  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3236  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3237                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3238                         position of the SplitBar).
3239  */
3240 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3241     
3242     /** @private */
3243     this.el = Roo.get(dragElement, true);
3244     this.el.dom.unselectable = "on";
3245     /** @private */
3246     this.resizingEl = Roo.get(resizingElement, true);
3247
3248     /**
3249      * @private
3250      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3251      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3252      * @type Number
3253      */
3254     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3255     
3256     /**
3257      * The minimum size of the resizing element. (Defaults to 0)
3258      * @type Number
3259      */
3260     this.minSize = 0;
3261     
3262     /**
3263      * The maximum size of the resizing element. (Defaults to 2000)
3264      * @type Number
3265      */
3266     this.maxSize = 2000;
3267     
3268     /**
3269      * Whether to animate the transition to the new size
3270      * @type Boolean
3271      */
3272     this.animate = false;
3273     
3274     /**
3275      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3276      * @type Boolean
3277      */
3278     this.useShim = false;
3279     
3280     /** @private */
3281     this.shim = null;
3282     
3283     if(!existingProxy){
3284         /** @private */
3285         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3286     }else{
3287         this.proxy = Roo.get(existingProxy).dom;
3288     }
3289     /** @private */
3290     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3291     
3292     /** @private */
3293     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3294     
3295     /** @private */
3296     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3297     
3298     /** @private */
3299     this.dragSpecs = {};
3300     
3301     /**
3302      * @private The adapter to use to positon and resize elements
3303      */
3304     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3305     this.adapter.init(this);
3306     
3307     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3308         /** @private */
3309         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3310         this.el.addClass("x-splitbar-h");
3311     }else{
3312         /** @private */
3313         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3314         this.el.addClass("x-splitbar-v");
3315     }
3316     
3317     this.addEvents({
3318         /**
3319          * @event resize
3320          * Fires when the splitter is moved (alias for {@link #event-moved})
3321          * @param {Roo.SplitBar} this
3322          * @param {Number} newSize the new width or height
3323          */
3324         "resize" : true,
3325         /**
3326          * @event moved
3327          * Fires when the splitter is moved
3328          * @param {Roo.SplitBar} this
3329          * @param {Number} newSize the new width or height
3330          */
3331         "moved" : true,
3332         /**
3333          * @event beforeresize
3334          * Fires before the splitter is dragged
3335          * @param {Roo.SplitBar} this
3336          */
3337         "beforeresize" : true,
3338
3339         "beforeapply" : true
3340     });
3341
3342     Roo.util.Observable.call(this);
3343 };
3344
3345 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3346     onStartProxyDrag : function(x, y){
3347         this.fireEvent("beforeresize", this);
3348         if(!this.overlay){
3349             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3350             o.unselectable();
3351             o.enableDisplayMode("block");
3352             // all splitbars share the same overlay
3353             Roo.SplitBar.prototype.overlay = o;
3354         }
3355         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3356         this.overlay.show();
3357         Roo.get(this.proxy).setDisplayed("block");
3358         var size = this.adapter.getElementSize(this);
3359         this.activeMinSize = this.getMinimumSize();;
3360         this.activeMaxSize = this.getMaximumSize();;
3361         var c1 = size - this.activeMinSize;
3362         var c2 = Math.max(this.activeMaxSize - size, 0);
3363         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3364             this.dd.resetConstraints();
3365             this.dd.setXConstraint(
3366                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3367                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3368             );
3369             this.dd.setYConstraint(0, 0);
3370         }else{
3371             this.dd.resetConstraints();
3372             this.dd.setXConstraint(0, 0);
3373             this.dd.setYConstraint(
3374                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3375                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3376             );
3377          }
3378         this.dragSpecs.startSize = size;
3379         this.dragSpecs.startPoint = [x, y];
3380         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3381     },
3382     
3383     /** 
3384      * @private Called after the drag operation by the DDProxy
3385      */
3386     onEndProxyDrag : function(e){
3387         Roo.get(this.proxy).setDisplayed(false);
3388         var endPoint = Roo.lib.Event.getXY(e);
3389         if(this.overlay){
3390             this.overlay.hide();
3391         }
3392         var newSize;
3393         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3394             newSize = this.dragSpecs.startSize + 
3395                 (this.placement == Roo.SplitBar.LEFT ?
3396                     endPoint[0] - this.dragSpecs.startPoint[0] :
3397                     this.dragSpecs.startPoint[0] - endPoint[0]
3398                 );
3399         }else{
3400             newSize = this.dragSpecs.startSize + 
3401                 (this.placement == Roo.SplitBar.TOP ?
3402                     endPoint[1] - this.dragSpecs.startPoint[1] :
3403                     this.dragSpecs.startPoint[1] - endPoint[1]
3404                 );
3405         }
3406         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3407         if(newSize != this.dragSpecs.startSize){
3408             if(this.fireEvent('beforeapply', this, newSize) !== false){
3409                 this.adapter.setElementSize(this, newSize);
3410                 this.fireEvent("moved", this, newSize);
3411                 this.fireEvent("resize", this, newSize);
3412             }
3413         }
3414     },
3415     
3416     /**
3417      * Get the adapter this SplitBar uses
3418      * @return The adapter object
3419      */
3420     getAdapter : function(){
3421         return this.adapter;
3422     },
3423     
3424     /**
3425      * Set the adapter this SplitBar uses
3426      * @param {Object} adapter A SplitBar adapter object
3427      */
3428     setAdapter : function(adapter){
3429         this.adapter = adapter;
3430         this.adapter.init(this);
3431     },
3432     
3433     /**
3434      * Gets the minimum size for the resizing element
3435      * @return {Number} The minimum size
3436      */
3437     getMinimumSize : function(){
3438         return this.minSize;
3439     },
3440     
3441     /**
3442      * Sets the minimum size for the resizing element
3443      * @param {Number} minSize The minimum size
3444      */
3445     setMinimumSize : function(minSize){
3446         this.minSize = minSize;
3447     },
3448     
3449     /**
3450      * Gets the maximum size for the resizing element
3451      * @return {Number} The maximum size
3452      */
3453     getMaximumSize : function(){
3454         return this.maxSize;
3455     },
3456     
3457     /**
3458      * Sets the maximum size for the resizing element
3459      * @param {Number} maxSize The maximum size
3460      */
3461     setMaximumSize : function(maxSize){
3462         this.maxSize = maxSize;
3463     },
3464     
3465     /**
3466      * Sets the initialize size for the resizing element
3467      * @param {Number} size The initial size
3468      */
3469     setCurrentSize : function(size){
3470         var oldAnimate = this.animate;
3471         this.animate = false;
3472         this.adapter.setElementSize(this, size);
3473         this.animate = oldAnimate;
3474     },
3475     
3476     /**
3477      * Destroy this splitbar. 
3478      * @param {Boolean} removeEl True to remove the element
3479      */
3480     destroy : function(removeEl){
3481         if(this.shim){
3482             this.shim.remove();
3483         }
3484         this.dd.unreg();
3485         this.proxy.parentNode.removeChild(this.proxy);
3486         if(removeEl){
3487             this.el.remove();
3488         }
3489     }
3490 });
3491
3492 /**
3493  * @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.
3494  */
3495 Roo.SplitBar.createProxy = function(dir){
3496     var proxy = new Roo.Element(document.createElement("div"));
3497     proxy.unselectable();
3498     var cls = 'x-splitbar-proxy';
3499     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3500     document.body.appendChild(proxy.dom);
3501     return proxy.dom;
3502 };
3503
3504 /** 
3505  * @class Roo.SplitBar.BasicLayoutAdapter
3506  * Default Adapter. It assumes the splitter and resizing element are not positioned
3507  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3508  */
3509 Roo.SplitBar.BasicLayoutAdapter = function(){
3510 };
3511
3512 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3513     // do nothing for now
3514     init : function(s){
3515     
3516     },
3517     /**
3518      * Called before drag operations to get the current size of the resizing element. 
3519      * @param {Roo.SplitBar} s The SplitBar using this adapter
3520      */
3521      getElementSize : function(s){
3522         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3523             return s.resizingEl.getWidth();
3524         }else{
3525             return s.resizingEl.getHeight();
3526         }
3527     },
3528     
3529     /**
3530      * Called after drag operations to set the size of the resizing element.
3531      * @param {Roo.SplitBar} s The SplitBar using this adapter
3532      * @param {Number} newSize The new size to set
3533      * @param {Function} onComplete A function to be invoked when resizing is complete
3534      */
3535     setElementSize : function(s, newSize, onComplete){
3536         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3537             if(!s.animate){
3538                 s.resizingEl.setWidth(newSize);
3539                 if(onComplete){
3540                     onComplete(s, newSize);
3541                 }
3542             }else{
3543                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3544             }
3545         }else{
3546             
3547             if(!s.animate){
3548                 s.resizingEl.setHeight(newSize);
3549                 if(onComplete){
3550                     onComplete(s, newSize);
3551                 }
3552             }else{
3553                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3554             }
3555         }
3556     }
3557 };
3558
3559 /** 
3560  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3561  * @extends Roo.SplitBar.BasicLayoutAdapter
3562  * Adapter that  moves the splitter element to align with the resized sizing element. 
3563  * Used with an absolute positioned SplitBar.
3564  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3565  * document.body, make sure you assign an id to the body element.
3566  */
3567 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3568     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3569     this.container = Roo.get(container);
3570 };
3571
3572 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3573     init : function(s){
3574         this.basic.init(s);
3575     },
3576     
3577     getElementSize : function(s){
3578         return this.basic.getElementSize(s);
3579     },
3580     
3581     setElementSize : function(s, newSize, onComplete){
3582         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3583     },
3584     
3585     moveSplitter : function(s){
3586         var yes = Roo.SplitBar;
3587         switch(s.placement){
3588             case yes.LEFT:
3589                 s.el.setX(s.resizingEl.getRight());
3590                 break;
3591             case yes.RIGHT:
3592                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3593                 break;
3594             case yes.TOP:
3595                 s.el.setY(s.resizingEl.getBottom());
3596                 break;
3597             case yes.BOTTOM:
3598                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3599                 break;
3600         }
3601     }
3602 };
3603
3604 /**
3605  * Orientation constant - Create a vertical SplitBar
3606  * @static
3607  * @type Number
3608  */
3609 Roo.SplitBar.VERTICAL = 1;
3610
3611 /**
3612  * Orientation constant - Create a horizontal SplitBar
3613  * @static
3614  * @type Number
3615  */
3616 Roo.SplitBar.HORIZONTAL = 2;
3617
3618 /**
3619  * Placement constant - The resizing element is to the left of the splitter element
3620  * @static
3621  * @type Number
3622  */
3623 Roo.SplitBar.LEFT = 1;
3624
3625 /**
3626  * Placement constant - The resizing element is to the right of the splitter element
3627  * @static
3628  * @type Number
3629  */
3630 Roo.SplitBar.RIGHT = 2;
3631
3632 /**
3633  * Placement constant - The resizing element is positioned above the splitter element
3634  * @static
3635  * @type Number
3636  */
3637 Roo.SplitBar.TOP = 3;
3638
3639 /**
3640  * Placement constant - The resizing element is positioned under splitter element
3641  * @static
3642  * @type Number
3643  */
3644 Roo.SplitBar.BOTTOM = 4;
3645 /*
3646  * Based on:
3647  * Ext JS Library 1.1.1
3648  * Copyright(c) 2006-2007, Ext JS, LLC.
3649  *
3650  * Originally Released Under LGPL - original licence link has changed is not relivant.
3651  *
3652  * Fork - LGPL
3653  * <script type="text/javascript">
3654  */
3655
3656 /**
3657  * @class Roo.View
3658  * @extends Roo.util.Observable
3659  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3660  * This class also supports single and multi selection modes. <br>
3661  * Create a data model bound view:
3662  <pre><code>
3663  var store = new Roo.data.Store(...);
3664
3665  var view = new Roo.View({
3666     el : "my-element",
3667     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3668  
3669     singleSelect: true,
3670     selectedClass: "ydataview-selected",
3671     store: store
3672  });
3673
3674  // listen for node click?
3675  view.on("click", function(vw, index, node, e){
3676  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3677  });
3678
3679  // load XML data
3680  dataModel.load("foobar.xml");
3681  </code></pre>
3682  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3683  * <br><br>
3684  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3685  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3686  * 
3687  * Note: old style constructor is still suported (container, template, config)
3688  * 
3689  * @constructor
3690  * Create a new View
3691  * @param {Object} config The config object
3692  * 
3693  */
3694 Roo.View = function(config, depreciated_tpl, depreciated_config){
3695     
3696     this.parent = false;
3697     
3698     if (typeof(depreciated_tpl) == 'undefined') {
3699         // new way.. - universal constructor.
3700         Roo.apply(this, config);
3701         this.el  = Roo.get(this.el);
3702     } else {
3703         // old format..
3704         this.el  = Roo.get(config);
3705         this.tpl = depreciated_tpl;
3706         Roo.apply(this, depreciated_config);
3707     }
3708     this.wrapEl  = this.el.wrap().wrap();
3709     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3710     
3711     
3712     if(typeof(this.tpl) == "string"){
3713         this.tpl = new Roo.Template(this.tpl);
3714     } else {
3715         // support xtype ctors..
3716         this.tpl = new Roo.factory(this.tpl, Roo);
3717     }
3718     
3719     
3720     this.tpl.compile();
3721     
3722     /** @private */
3723     this.addEvents({
3724         /**
3725          * @event beforeclick
3726          * Fires before a click is processed. Returns false to cancel the default action.
3727          * @param {Roo.View} this
3728          * @param {Number} index The index of the target node
3729          * @param {HTMLElement} node The target node
3730          * @param {Roo.EventObject} e The raw event object
3731          */
3732             "beforeclick" : true,
3733         /**
3734          * @event click
3735          * Fires when a template node is clicked.
3736          * @param {Roo.View} this
3737          * @param {Number} index The index of the target node
3738          * @param {HTMLElement} node The target node
3739          * @param {Roo.EventObject} e The raw event object
3740          */
3741             "click" : true,
3742         /**
3743          * @event dblclick
3744          * Fires when a template node is double clicked.
3745          * @param {Roo.View} this
3746          * @param {Number} index The index of the target node
3747          * @param {HTMLElement} node The target node
3748          * @param {Roo.EventObject} e The raw event object
3749          */
3750             "dblclick" : true,
3751         /**
3752          * @event contextmenu
3753          * Fires when a template node is right clicked.
3754          * @param {Roo.View} this
3755          * @param {Number} index The index of the target node
3756          * @param {HTMLElement} node The target node
3757          * @param {Roo.EventObject} e The raw event object
3758          */
3759             "contextmenu" : true,
3760         /**
3761          * @event selectionchange
3762          * Fires when the selected nodes change.
3763          * @param {Roo.View} this
3764          * @param {Array} selections Array of the selected nodes
3765          */
3766             "selectionchange" : true,
3767     
3768         /**
3769          * @event beforeselect
3770          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3771          * @param {Roo.View} this
3772          * @param {HTMLElement} node The node to be selected
3773          * @param {Array} selections Array of currently selected nodes
3774          */
3775             "beforeselect" : true,
3776         /**
3777          * @event preparedata
3778          * Fires on every row to render, to allow you to change the data.
3779          * @param {Roo.View} this
3780          * @param {Object} data to be rendered (change this)
3781          */
3782           "preparedata" : true
3783           
3784           
3785         });
3786
3787
3788
3789     this.el.on({
3790         "click": this.onClick,
3791         "dblclick": this.onDblClick,
3792         "contextmenu": this.onContextMenu,
3793         scope:this
3794     });
3795
3796     this.selections = [];
3797     this.nodes = [];
3798     this.cmp = new Roo.CompositeElementLite([]);
3799     if(this.store){
3800         this.store = Roo.factory(this.store, Roo.data);
3801         this.setStore(this.store, true);
3802     }
3803     
3804     if ( this.footer && this.footer.xtype) {
3805            
3806          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3807         
3808         this.footer.dataSource = this.store;
3809         this.footer.container = fctr;
3810         this.footer = Roo.factory(this.footer, Roo);
3811         fctr.insertFirst(this.el);
3812         
3813         // this is a bit insane - as the paging toolbar seems to detach the el..
3814 //        dom.parentNode.parentNode.parentNode
3815          // they get detached?
3816     }
3817     
3818     
3819     Roo.View.superclass.constructor.call(this);
3820     
3821     
3822 };
3823
3824 Roo.extend(Roo.View, Roo.util.Observable, {
3825     
3826      /**
3827      * @cfg {Roo.data.Store} store Data store to load data from.
3828      */
3829     store : false,
3830     
3831     /**
3832      * @cfg {String|Roo.Element} el The container element.
3833      */
3834     el : '',
3835     
3836     /**
3837      * @cfg {String|Roo.Template} tpl The template used by this View 
3838      */
3839     tpl : false,
3840     /**
3841      * @cfg {String} dataName the named area of the template to use as the data area
3842      *                          Works with domtemplates roo-name="name"
3843      */
3844     dataName: false,
3845     /**
3846      * @cfg {String} selectedClass The css class to add to selected nodes
3847      */
3848     selectedClass : "x-view-selected",
3849      /**
3850      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3851      */
3852     emptyText : "",
3853     
3854     /**
3855      * @cfg {String} text to display on mask (default Loading)
3856      */
3857     mask : false,
3858     /**
3859      * @cfg {Boolean} multiSelect Allow multiple selection
3860      */
3861     multiSelect : false,
3862     /**
3863      * @cfg {Boolean} singleSelect Allow single selection
3864      */
3865     singleSelect:  false,
3866     
3867     /**
3868      * @cfg {Boolean} toggleSelect - selecting 
3869      */
3870     toggleSelect : false,
3871     
3872     /**
3873      * @cfg {Boolean} tickable - selecting 
3874      */
3875     tickable : false,
3876     
3877     /**
3878      * Returns the element this view is bound to.
3879      * @return {Roo.Element}
3880      */
3881     getEl : function(){
3882         return this.wrapEl;
3883     },
3884     
3885     
3886
3887     /**
3888      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3889      */
3890     refresh : function(){
3891         //Roo.log('refresh');
3892         var t = this.tpl;
3893         
3894         // if we are using something like 'domtemplate', then
3895         // the what gets used is:
3896         // t.applySubtemplate(NAME, data, wrapping data..)
3897         // the outer template then get' applied with
3898         //     the store 'extra data'
3899         // and the body get's added to the
3900         //      roo-name="data" node?
3901         //      <span class='roo-tpl-{name}'></span> ?????
3902         
3903         
3904         
3905         this.clearSelections();
3906         this.el.update("");
3907         var html = [];
3908         var records = this.store.getRange();
3909         if(records.length < 1) {
3910             
3911             // is this valid??  = should it render a template??
3912             
3913             this.el.update(this.emptyText);
3914             return;
3915         }
3916         var el = this.el;
3917         if (this.dataName) {
3918             this.el.update(t.apply(this.store.meta)); //????
3919             el = this.el.child('.roo-tpl-' + this.dataName);
3920         }
3921         
3922         for(var i = 0, len = records.length; i < len; i++){
3923             var data = this.prepareData(records[i].data, i, records[i]);
3924             this.fireEvent("preparedata", this, data, i, records[i]);
3925             
3926             var d = Roo.apply({}, data);
3927             
3928             if(this.tickable){
3929                 Roo.apply(d, {'roo-id' : Roo.id()});
3930                 
3931                 var _this = this;
3932             
3933                 Roo.each(this.parent.item, function(item){
3934                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3935                         return;
3936                     }
3937                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3938                 });
3939             }
3940             
3941             html[html.length] = Roo.util.Format.trim(
3942                 this.dataName ?
3943                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3944                     t.apply(d)
3945             );
3946         }
3947         
3948         
3949         
3950         el.update(html.join(""));
3951         this.nodes = el.dom.childNodes;
3952         this.updateIndexes(0);
3953     },
3954     
3955
3956     /**
3957      * Function to override to reformat the data that is sent to
3958      * the template for each node.
3959      * DEPRICATED - use the preparedata event handler.
3960      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3961      * a JSON object for an UpdateManager bound view).
3962      */
3963     prepareData : function(data, index, record)
3964     {
3965         this.fireEvent("preparedata", this, data, index, record);
3966         return data;
3967     },
3968
3969     onUpdate : function(ds, record){
3970         // Roo.log('on update');   
3971         this.clearSelections();
3972         var index = this.store.indexOf(record);
3973         var n = this.nodes[index];
3974         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3975         n.parentNode.removeChild(n);
3976         this.updateIndexes(index, index);
3977     },
3978
3979     
3980     
3981 // --------- FIXME     
3982     onAdd : function(ds, records, index)
3983     {
3984         //Roo.log(['on Add', ds, records, index] );        
3985         this.clearSelections();
3986         if(this.nodes.length == 0){
3987             this.refresh();
3988             return;
3989         }
3990         var n = this.nodes[index];
3991         for(var i = 0, len = records.length; i < len; i++){
3992             var d = this.prepareData(records[i].data, i, records[i]);
3993             if(n){
3994                 this.tpl.insertBefore(n, d);
3995             }else{
3996                 
3997                 this.tpl.append(this.el, d);
3998             }
3999         }
4000         this.updateIndexes(index);
4001     },
4002
4003     onRemove : function(ds, record, index){
4004        // Roo.log('onRemove');
4005         this.clearSelections();
4006         var el = this.dataName  ?
4007             this.el.child('.roo-tpl-' + this.dataName) :
4008             this.el; 
4009         
4010         el.dom.removeChild(this.nodes[index]);
4011         this.updateIndexes(index);
4012     },
4013
4014     /**
4015      * Refresh an individual node.
4016      * @param {Number} index
4017      */
4018     refreshNode : function(index){
4019         this.onUpdate(this.store, this.store.getAt(index));
4020     },
4021
4022     updateIndexes : function(startIndex, endIndex){
4023         var ns = this.nodes;
4024         startIndex = startIndex || 0;
4025         endIndex = endIndex || ns.length - 1;
4026         for(var i = startIndex; i <= endIndex; i++){
4027             ns[i].nodeIndex = i;
4028         }
4029     },
4030
4031     /**
4032      * Changes the data store this view uses and refresh the view.
4033      * @param {Store} store
4034      */
4035     setStore : function(store, initial){
4036         if(!initial && this.store){
4037             this.store.un("datachanged", this.refresh);
4038             this.store.un("add", this.onAdd);
4039             this.store.un("remove", this.onRemove);
4040             this.store.un("update", this.onUpdate);
4041             this.store.un("clear", this.refresh);
4042             this.store.un("beforeload", this.onBeforeLoad);
4043             this.store.un("load", this.onLoad);
4044             this.store.un("loadexception", this.onLoad);
4045         }
4046         if(store){
4047           
4048             store.on("datachanged", this.refresh, this);
4049             store.on("add", this.onAdd, this);
4050             store.on("remove", this.onRemove, this);
4051             store.on("update", this.onUpdate, this);
4052             store.on("clear", this.refresh, this);
4053             store.on("beforeload", this.onBeforeLoad, this);
4054             store.on("load", this.onLoad, this);
4055             store.on("loadexception", this.onLoad, this);
4056         }
4057         
4058         if(store){
4059             this.refresh();
4060         }
4061     },
4062     /**
4063      * onbeforeLoad - masks the loading area.
4064      *
4065      */
4066     onBeforeLoad : function(store,opts)
4067     {
4068          //Roo.log('onBeforeLoad');   
4069         if (!opts.add) {
4070             this.el.update("");
4071         }
4072         this.el.mask(this.mask ? this.mask : "Loading" ); 
4073     },
4074     onLoad : function ()
4075     {
4076         this.el.unmask();
4077     },
4078     
4079
4080     /**
4081      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4082      * @param {HTMLElement} node
4083      * @return {HTMLElement} The template node
4084      */
4085     findItemFromChild : function(node){
4086         var el = this.dataName  ?
4087             this.el.child('.roo-tpl-' + this.dataName,true) :
4088             this.el.dom; 
4089         
4090         if(!node || node.parentNode == el){
4091                     return node;
4092             }
4093             var p = node.parentNode;
4094             while(p && p != el){
4095             if(p.parentNode == el){
4096                 return p;
4097             }
4098             p = p.parentNode;
4099         }
4100             return null;
4101     },
4102
4103     /** @ignore */
4104     onClick : function(e){
4105         var item = this.findItemFromChild(e.getTarget());
4106         if(item){
4107             var index = this.indexOf(item);
4108             if(this.onItemClick(item, index, e) !== false){
4109                 this.fireEvent("click", this, index, item, e);
4110             }
4111         }else{
4112             this.clearSelections();
4113         }
4114     },
4115
4116     /** @ignore */
4117     onContextMenu : function(e){
4118         var item = this.findItemFromChild(e.getTarget());
4119         if(item){
4120             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4121         }
4122     },
4123
4124     /** @ignore */
4125     onDblClick : function(e){
4126         var item = this.findItemFromChild(e.getTarget());
4127         if(item){
4128             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4129         }
4130     },
4131
4132     onItemClick : function(item, index, e)
4133     {
4134         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4135             return false;
4136         }
4137         if (this.toggleSelect) {
4138             var m = this.isSelected(item) ? 'unselect' : 'select';
4139             //Roo.log(m);
4140             var _t = this;
4141             _t[m](item, true, false);
4142             return true;
4143         }
4144         if(this.multiSelect || this.singleSelect){
4145             if(this.multiSelect && e.shiftKey && this.lastSelection){
4146                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4147             }else{
4148                 this.select(item, this.multiSelect && e.ctrlKey);
4149                 this.lastSelection = item;
4150             }
4151             
4152             if(!this.tickable){
4153                 e.preventDefault();
4154             }
4155             
4156         }
4157         return true;
4158     },
4159
4160     /**
4161      * Get the number of selected nodes.
4162      * @return {Number}
4163      */
4164     getSelectionCount : function(){
4165         return this.selections.length;
4166     },
4167
4168     /**
4169      * Get the currently selected nodes.
4170      * @return {Array} An array of HTMLElements
4171      */
4172     getSelectedNodes : function(){
4173         return this.selections;
4174     },
4175
4176     /**
4177      * Get the indexes of the selected nodes.
4178      * @return {Array}
4179      */
4180     getSelectedIndexes : function(){
4181         var indexes = [], s = this.selections;
4182         for(var i = 0, len = s.length; i < len; i++){
4183             indexes.push(s[i].nodeIndex);
4184         }
4185         return indexes;
4186     },
4187
4188     /**
4189      * Clear all selections
4190      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4191      */
4192     clearSelections : function(suppressEvent){
4193         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4194             this.cmp.elements = this.selections;
4195             this.cmp.removeClass(this.selectedClass);
4196             this.selections = [];
4197             if(!suppressEvent){
4198                 this.fireEvent("selectionchange", this, this.selections);
4199             }
4200         }
4201     },
4202
4203     /**
4204      * Returns true if the passed node is selected
4205      * @param {HTMLElement/Number} node The node or node index
4206      * @return {Boolean}
4207      */
4208     isSelected : function(node){
4209         var s = this.selections;
4210         if(s.length < 1){
4211             return false;
4212         }
4213         node = this.getNode(node);
4214         return s.indexOf(node) !== -1;
4215     },
4216
4217     /**
4218      * Selects nodes.
4219      * @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
4220      * @param {Boolean} keepExisting (optional) true to keep existing selections
4221      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4222      */
4223     select : function(nodeInfo, keepExisting, suppressEvent){
4224         if(nodeInfo instanceof Array){
4225             if(!keepExisting){
4226                 this.clearSelections(true);
4227             }
4228             for(var i = 0, len = nodeInfo.length; i < len; i++){
4229                 this.select(nodeInfo[i], true, true);
4230             }
4231             return;
4232         } 
4233         var node = this.getNode(nodeInfo);
4234         if(!node || this.isSelected(node)){
4235             return; // already selected.
4236         }
4237         if(!keepExisting){
4238             this.clearSelections(true);
4239         }
4240         
4241         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4242             Roo.fly(node).addClass(this.selectedClass);
4243             this.selections.push(node);
4244             if(!suppressEvent){
4245                 this.fireEvent("selectionchange", this, this.selections);
4246             }
4247         }
4248         
4249         
4250     },
4251       /**
4252      * Unselects nodes.
4253      * @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
4254      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4255      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4256      */
4257     unselect : function(nodeInfo, keepExisting, suppressEvent)
4258     {
4259         if(nodeInfo instanceof Array){
4260             Roo.each(this.selections, function(s) {
4261                 this.unselect(s, nodeInfo);
4262             }, this);
4263             return;
4264         }
4265         var node = this.getNode(nodeInfo);
4266         if(!node || !this.isSelected(node)){
4267             //Roo.log("not selected");
4268             return; // not selected.
4269         }
4270         // fireevent???
4271         var ns = [];
4272         Roo.each(this.selections, function(s) {
4273             if (s == node ) {
4274                 Roo.fly(node).removeClass(this.selectedClass);
4275
4276                 return;
4277             }
4278             ns.push(s);
4279         },this);
4280         
4281         this.selections= ns;
4282         this.fireEvent("selectionchange", this, this.selections);
4283     },
4284
4285     /**
4286      * Gets a template node.
4287      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4288      * @return {HTMLElement} The node or null if it wasn't found
4289      */
4290     getNode : function(nodeInfo){
4291         if(typeof nodeInfo == "string"){
4292             return document.getElementById(nodeInfo);
4293         }else if(typeof nodeInfo == "number"){
4294             return this.nodes[nodeInfo];
4295         }
4296         return nodeInfo;
4297     },
4298
4299     /**
4300      * Gets a range template nodes.
4301      * @param {Number} startIndex
4302      * @param {Number} endIndex
4303      * @return {Array} An array of nodes
4304      */
4305     getNodes : function(start, end){
4306         var ns = this.nodes;
4307         start = start || 0;
4308         end = typeof end == "undefined" ? ns.length - 1 : end;
4309         var nodes = [];
4310         if(start <= end){
4311             for(var i = start; i <= end; i++){
4312                 nodes.push(ns[i]);
4313             }
4314         } else{
4315             for(var i = start; i >= end; i--){
4316                 nodes.push(ns[i]);
4317             }
4318         }
4319         return nodes;
4320     },
4321
4322     /**
4323      * Finds the index of the passed node
4324      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4325      * @return {Number} The index of the node or -1
4326      */
4327     indexOf : function(node){
4328         node = this.getNode(node);
4329         if(typeof node.nodeIndex == "number"){
4330             return node.nodeIndex;
4331         }
4332         var ns = this.nodes;
4333         for(var i = 0, len = ns.length; i < len; i++){
4334             if(ns[i] == node){
4335                 return i;
4336             }
4337         }
4338         return -1;
4339     }
4340 });
4341 /*
4342  * Based on:
4343  * Ext JS Library 1.1.1
4344  * Copyright(c) 2006-2007, Ext JS, LLC.
4345  *
4346  * Originally Released Under LGPL - original licence link has changed is not relivant.
4347  *
4348  * Fork - LGPL
4349  * <script type="text/javascript">
4350  */
4351
4352 /**
4353  * @class Roo.JsonView
4354  * @extends Roo.View
4355  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4356 <pre><code>
4357 var view = new Roo.JsonView({
4358     container: "my-element",
4359     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4360     multiSelect: true, 
4361     jsonRoot: "data" 
4362 });
4363
4364 // listen for node click?
4365 view.on("click", function(vw, index, node, e){
4366     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4367 });
4368
4369 // direct load of JSON data
4370 view.load("foobar.php");
4371
4372 // Example from my blog list
4373 var tpl = new Roo.Template(
4374     '&lt;div class="entry"&gt;' +
4375     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4376     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4377     "&lt;/div&gt;&lt;hr /&gt;"
4378 );
4379
4380 var moreView = new Roo.JsonView({
4381     container :  "entry-list", 
4382     template : tpl,
4383     jsonRoot: "posts"
4384 });
4385 moreView.on("beforerender", this.sortEntries, this);
4386 moreView.load({
4387     url: "/blog/get-posts.php",
4388     params: "allposts=true",
4389     text: "Loading Blog Entries..."
4390 });
4391 </code></pre>
4392
4393 * Note: old code is supported with arguments : (container, template, config)
4394
4395
4396  * @constructor
4397  * Create a new JsonView
4398  * 
4399  * @param {Object} config The config object
4400  * 
4401  */
4402 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4403     
4404     
4405     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4406
4407     var um = this.el.getUpdateManager();
4408     um.setRenderer(this);
4409     um.on("update", this.onLoad, this);
4410     um.on("failure", this.onLoadException, this);
4411
4412     /**
4413      * @event beforerender
4414      * Fires before rendering of the downloaded JSON data.
4415      * @param {Roo.JsonView} this
4416      * @param {Object} data The JSON data loaded
4417      */
4418     /**
4419      * @event load
4420      * Fires when data is loaded.
4421      * @param {Roo.JsonView} this
4422      * @param {Object} data The JSON data loaded
4423      * @param {Object} response The raw Connect response object
4424      */
4425     /**
4426      * @event loadexception
4427      * Fires when loading fails.
4428      * @param {Roo.JsonView} this
4429      * @param {Object} response The raw Connect response object
4430      */
4431     this.addEvents({
4432         'beforerender' : true,
4433         'load' : true,
4434         'loadexception' : true
4435     });
4436 };
4437 Roo.extend(Roo.JsonView, Roo.View, {
4438     /**
4439      * @type {String} The root property in the loaded JSON object that contains the data
4440      */
4441     jsonRoot : "",
4442
4443     /**
4444      * Refreshes the view.
4445      */
4446     refresh : function(){
4447         this.clearSelections();
4448         this.el.update("");
4449         var html = [];
4450         var o = this.jsonData;
4451         if(o && o.length > 0){
4452             for(var i = 0, len = o.length; i < len; i++){
4453                 var data = this.prepareData(o[i], i, o);
4454                 html[html.length] = this.tpl.apply(data);
4455             }
4456         }else{
4457             html.push(this.emptyText);
4458         }
4459         this.el.update(html.join(""));
4460         this.nodes = this.el.dom.childNodes;
4461         this.updateIndexes(0);
4462     },
4463
4464     /**
4465      * 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.
4466      * @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:
4467      <pre><code>
4468      view.load({
4469          url: "your-url.php",
4470          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4471          callback: yourFunction,
4472          scope: yourObject, //(optional scope)
4473          discardUrl: false,
4474          nocache: false,
4475          text: "Loading...",
4476          timeout: 30,
4477          scripts: false
4478      });
4479      </code></pre>
4480      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4481      * 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.
4482      * @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}
4483      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4484      * @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.
4485      */
4486     load : function(){
4487         var um = this.el.getUpdateManager();
4488         um.update.apply(um, arguments);
4489     },
4490
4491     // note - render is a standard framework call...
4492     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4493     render : function(el, response){
4494         
4495         this.clearSelections();
4496         this.el.update("");
4497         var o;
4498         try{
4499             if (response != '') {
4500                 o = Roo.util.JSON.decode(response.responseText);
4501                 if(this.jsonRoot){
4502                     
4503                     o = o[this.jsonRoot];
4504                 }
4505             }
4506         } catch(e){
4507         }
4508         /**
4509          * The current JSON data or null
4510          */
4511         this.jsonData = o;
4512         this.beforeRender();
4513         this.refresh();
4514     },
4515
4516 /**
4517  * Get the number of records in the current JSON dataset
4518  * @return {Number}
4519  */
4520     getCount : function(){
4521         return this.jsonData ? this.jsonData.length : 0;
4522     },
4523
4524 /**
4525  * Returns the JSON object for the specified node(s)
4526  * @param {HTMLElement/Array} node The node or an array of nodes
4527  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4528  * you get the JSON object for the node
4529  */
4530     getNodeData : function(node){
4531         if(node instanceof Array){
4532             var data = [];
4533             for(var i = 0, len = node.length; i < len; i++){
4534                 data.push(this.getNodeData(node[i]));
4535             }
4536             return data;
4537         }
4538         return this.jsonData[this.indexOf(node)] || null;
4539     },
4540
4541     beforeRender : function(){
4542         this.snapshot = this.jsonData;
4543         if(this.sortInfo){
4544             this.sort.apply(this, this.sortInfo);
4545         }
4546         this.fireEvent("beforerender", this, this.jsonData);
4547     },
4548
4549     onLoad : function(el, o){
4550         this.fireEvent("load", this, this.jsonData, o);
4551     },
4552
4553     onLoadException : function(el, o){
4554         this.fireEvent("loadexception", this, o);
4555     },
4556
4557 /**
4558  * Filter the data by a specific property.
4559  * @param {String} property A property on your JSON objects
4560  * @param {String/RegExp} value Either string that the property values
4561  * should start with, or a RegExp to test against the property
4562  */
4563     filter : function(property, value){
4564         if(this.jsonData){
4565             var data = [];
4566             var ss = this.snapshot;
4567             if(typeof value == "string"){
4568                 var vlen = value.length;
4569                 if(vlen == 0){
4570                     this.clearFilter();
4571                     return;
4572                 }
4573                 value = value.toLowerCase();
4574                 for(var i = 0, len = ss.length; i < len; i++){
4575                     var o = ss[i];
4576                     if(o[property].substr(0, vlen).toLowerCase() == value){
4577                         data.push(o);
4578                     }
4579                 }
4580             } else if(value.exec){ // regex?
4581                 for(var i = 0, len = ss.length; i < len; i++){
4582                     var o = ss[i];
4583                     if(value.test(o[property])){
4584                         data.push(o);
4585                     }
4586                 }
4587             } else{
4588                 return;
4589             }
4590             this.jsonData = data;
4591             this.refresh();
4592         }
4593     },
4594
4595 /**
4596  * Filter by a function. The passed function will be called with each
4597  * object in the current dataset. If the function returns true the value is kept,
4598  * otherwise it is filtered.
4599  * @param {Function} fn
4600  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4601  */
4602     filterBy : function(fn, scope){
4603         if(this.jsonData){
4604             var data = [];
4605             var ss = this.snapshot;
4606             for(var i = 0, len = ss.length; i < len; i++){
4607                 var o = ss[i];
4608                 if(fn.call(scope || this, o)){
4609                     data.push(o);
4610                 }
4611             }
4612             this.jsonData = data;
4613             this.refresh();
4614         }
4615     },
4616
4617 /**
4618  * Clears the current filter.
4619  */
4620     clearFilter : function(){
4621         if(this.snapshot && this.jsonData != this.snapshot){
4622             this.jsonData = this.snapshot;
4623             this.refresh();
4624         }
4625     },
4626
4627
4628 /**
4629  * Sorts the data for this view and refreshes it.
4630  * @param {String} property A property on your JSON objects to sort on
4631  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4632  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4633  */
4634     sort : function(property, dir, sortType){
4635         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4636         if(this.jsonData){
4637             var p = property;
4638             var dsc = dir && dir.toLowerCase() == "desc";
4639             var f = function(o1, o2){
4640                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4641                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4642                 ;
4643                 if(v1 < v2){
4644                     return dsc ? +1 : -1;
4645                 } else if(v1 > v2){
4646                     return dsc ? -1 : +1;
4647                 } else{
4648                     return 0;
4649                 }
4650             };
4651             this.jsonData.sort(f);
4652             this.refresh();
4653             if(this.jsonData != this.snapshot){
4654                 this.snapshot.sort(f);
4655             }
4656         }
4657     }
4658 });/*
4659  * Based on:
4660  * Ext JS Library 1.1.1
4661  * Copyright(c) 2006-2007, Ext JS, LLC.
4662  *
4663  * Originally Released Under LGPL - original licence link has changed is not relivant.
4664  *
4665  * Fork - LGPL
4666  * <script type="text/javascript">
4667  */
4668  
4669
4670 /**
4671  * @class Roo.ColorPalette
4672  * @extends Roo.Component
4673  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4674  * Here's an example of typical usage:
4675  * <pre><code>
4676 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4677 cp.render('my-div');
4678
4679 cp.on('select', function(palette, selColor){
4680     // do something with selColor
4681 });
4682 </code></pre>
4683  * @constructor
4684  * Create a new ColorPalette
4685  * @param {Object} config The config object
4686  */
4687 Roo.ColorPalette = function(config){
4688     Roo.ColorPalette.superclass.constructor.call(this, config);
4689     this.addEvents({
4690         /**
4691              * @event select
4692              * Fires when a color is selected
4693              * @param {ColorPalette} this
4694              * @param {String} color The 6-digit color hex code (without the # symbol)
4695              */
4696         select: true
4697     });
4698
4699     if(this.handler){
4700         this.on("select", this.handler, this.scope, true);
4701     }
4702 };
4703 Roo.extend(Roo.ColorPalette, Roo.Component, {
4704     /**
4705      * @cfg {String} itemCls
4706      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4707      */
4708     itemCls : "x-color-palette",
4709     /**
4710      * @cfg {String} value
4711      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4712      * the hex codes are case-sensitive.
4713      */
4714     value : null,
4715     clickEvent:'click',
4716     // private
4717     ctype: "Roo.ColorPalette",
4718
4719     /**
4720      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4721      */
4722     allowReselect : false,
4723
4724     /**
4725      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4726      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4727      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4728      * of colors with the width setting until the box is symmetrical.</p>
4729      * <p>You can override individual colors if needed:</p>
4730      * <pre><code>
4731 var cp = new Roo.ColorPalette();
4732 cp.colors[0] = "FF0000";  // change the first box to red
4733 </code></pre>
4734
4735 Or you can provide a custom array of your own for complete control:
4736 <pre><code>
4737 var cp = new Roo.ColorPalette();
4738 cp.colors = ["000000", "993300", "333300"];
4739 </code></pre>
4740      * @type Array
4741      */
4742     colors : [
4743         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4744         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4745         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4746         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4747         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4748     ],
4749
4750     // private
4751     onRender : function(container, position){
4752         var t = new Roo.MasterTemplate(
4753             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4754         );
4755         var c = this.colors;
4756         for(var i = 0, len = c.length; i < len; i++){
4757             t.add([c[i]]);
4758         }
4759         var el = document.createElement("div");
4760         el.className = this.itemCls;
4761         t.overwrite(el);
4762         container.dom.insertBefore(el, position);
4763         this.el = Roo.get(el);
4764         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4765         if(this.clickEvent != 'click'){
4766             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4767         }
4768     },
4769
4770     // private
4771     afterRender : function(){
4772         Roo.ColorPalette.superclass.afterRender.call(this);
4773         if(this.value){
4774             var s = this.value;
4775             this.value = null;
4776             this.select(s);
4777         }
4778     },
4779
4780     // private
4781     handleClick : function(e, t){
4782         e.preventDefault();
4783         if(!this.disabled){
4784             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4785             this.select(c.toUpperCase());
4786         }
4787     },
4788
4789     /**
4790      * Selects the specified color in the palette (fires the select event)
4791      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4792      */
4793     select : function(color){
4794         color = color.replace("#", "");
4795         if(color != this.value || this.allowReselect){
4796             var el = this.el;
4797             if(this.value){
4798                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4799             }
4800             el.child("a.color-"+color).addClass("x-color-palette-sel");
4801             this.value = color;
4802             this.fireEvent("select", this, color);
4803         }
4804     }
4805 });/*
4806  * Based on:
4807  * Ext JS Library 1.1.1
4808  * Copyright(c) 2006-2007, Ext JS, LLC.
4809  *
4810  * Originally Released Under LGPL - original licence link has changed is not relivant.
4811  *
4812  * Fork - LGPL
4813  * <script type="text/javascript">
4814  */
4815  
4816 /**
4817  * @class Roo.DatePicker
4818  * @extends Roo.Component
4819  * Simple date picker class.
4820  * @constructor
4821  * Create a new DatePicker
4822  * @param {Object} config The config object
4823  */
4824 Roo.DatePicker = function(config){
4825     Roo.DatePicker.superclass.constructor.call(this, config);
4826
4827     this.value = config && config.value ?
4828                  config.value.clearTime() : new Date().clearTime();
4829
4830     this.addEvents({
4831         /**
4832              * @event select
4833              * Fires when a date is selected
4834              * @param {DatePicker} this
4835              * @param {Date} date The selected date
4836              */
4837         'select': true,
4838         /**
4839              * @event monthchange
4840              * Fires when the displayed month changes 
4841              * @param {DatePicker} this
4842              * @param {Date} date The selected month
4843              */
4844         'monthchange': true
4845     });
4846
4847     if(this.handler){
4848         this.on("select", this.handler,  this.scope || this);
4849     }
4850     // build the disabledDatesRE
4851     if(!this.disabledDatesRE && this.disabledDates){
4852         var dd = this.disabledDates;
4853         var re = "(?:";
4854         for(var i = 0; i < dd.length; i++){
4855             re += dd[i];
4856             if(i != dd.length-1) {
4857                 re += "|";
4858             }
4859         }
4860         this.disabledDatesRE = new RegExp(re + ")");
4861     }
4862 };
4863
4864 Roo.extend(Roo.DatePicker, Roo.Component, {
4865     /**
4866      * @cfg {String} todayText
4867      * The text to display on the button that selects the current date (defaults to "Today")
4868      */
4869     todayText : "Today",
4870     /**
4871      * @cfg {String} okText
4872      * The text to display on the ok button
4873      */
4874     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4875     /**
4876      * @cfg {String} cancelText
4877      * The text to display on the cancel button
4878      */
4879     cancelText : "Cancel",
4880     /**
4881      * @cfg {String} todayTip
4882      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4883      */
4884     todayTip : "{0} (Spacebar)",
4885     /**
4886      * @cfg {Date} minDate
4887      * Minimum allowable date (JavaScript date object, defaults to null)
4888      */
4889     minDate : null,
4890     /**
4891      * @cfg {Date} maxDate
4892      * Maximum allowable date (JavaScript date object, defaults to null)
4893      */
4894     maxDate : null,
4895     /**
4896      * @cfg {String} minText
4897      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4898      */
4899     minText : "This date is before the minimum date",
4900     /**
4901      * @cfg {String} maxText
4902      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4903      */
4904     maxText : "This date is after the maximum date",
4905     /**
4906      * @cfg {String} format
4907      * The default date format string which can be overriden for localization support.  The format must be
4908      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4909      */
4910     format : "m/d/y",
4911     /**
4912      * @cfg {Array} disabledDays
4913      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4914      */
4915     disabledDays : null,
4916     /**
4917      * @cfg {String} disabledDaysText
4918      * The tooltip to display when the date falls on a disabled day (defaults to "")
4919      */
4920     disabledDaysText : "",
4921     /**
4922      * @cfg {RegExp} disabledDatesRE
4923      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4924      */
4925     disabledDatesRE : null,
4926     /**
4927      * @cfg {String} disabledDatesText
4928      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4929      */
4930     disabledDatesText : "",
4931     /**
4932      * @cfg {Boolean} constrainToViewport
4933      * True to constrain the date picker to the viewport (defaults to true)
4934      */
4935     constrainToViewport : true,
4936     /**
4937      * @cfg {Array} monthNames
4938      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4939      */
4940     monthNames : Date.monthNames,
4941     /**
4942      * @cfg {Array} dayNames
4943      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4944      */
4945     dayNames : Date.dayNames,
4946     /**
4947      * @cfg {String} nextText
4948      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4949      */
4950     nextText: 'Next Month (Control+Right)',
4951     /**
4952      * @cfg {String} prevText
4953      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4954      */
4955     prevText: 'Previous Month (Control+Left)',
4956     /**
4957      * @cfg {String} monthYearText
4958      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4959      */
4960     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4961     /**
4962      * @cfg {Number} startDay
4963      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4964      */
4965     startDay : 0,
4966     /**
4967      * @cfg {Bool} showClear
4968      * Show a clear button (usefull for date form elements that can be blank.)
4969      */
4970     
4971     showClear: false,
4972     
4973     /**
4974      * Sets the value of the date field
4975      * @param {Date} value The date to set
4976      */
4977     setValue : function(value){
4978         var old = this.value;
4979         
4980         if (typeof(value) == 'string') {
4981          
4982             value = Date.parseDate(value, this.format);
4983         }
4984         if (!value) {
4985             value = new Date();
4986         }
4987         
4988         this.value = value.clearTime(true);
4989         if(this.el){
4990             this.update(this.value);
4991         }
4992     },
4993
4994     /**
4995      * Gets the current selected value of the date field
4996      * @return {Date} The selected date
4997      */
4998     getValue : function(){
4999         return this.value;
5000     },
5001
5002     // private
5003     focus : function(){
5004         if(this.el){
5005             this.update(this.activeDate);
5006         }
5007     },
5008
5009     // privateval
5010     onRender : function(container, position){
5011         
5012         var m = [
5013              '<table cellspacing="0">',
5014                 '<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>',
5015                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5016         var dn = this.dayNames;
5017         for(var i = 0; i < 7; i++){
5018             var d = this.startDay+i;
5019             if(d > 6){
5020                 d = d-7;
5021             }
5022             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5023         }
5024         m[m.length] = "</tr></thead><tbody><tr>";
5025         for(var i = 0; i < 42; i++) {
5026             if(i % 7 == 0 && i != 0){
5027                 m[m.length] = "</tr><tr>";
5028             }
5029             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5030         }
5031         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5032             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5033
5034         var el = document.createElement("div");
5035         el.className = "x-date-picker";
5036         el.innerHTML = m.join("");
5037
5038         container.dom.insertBefore(el, position);
5039
5040         this.el = Roo.get(el);
5041         this.eventEl = Roo.get(el.firstChild);
5042
5043         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5044             handler: this.showPrevMonth,
5045             scope: this,
5046             preventDefault:true,
5047             stopDefault:true
5048         });
5049
5050         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5051             handler: this.showNextMonth,
5052             scope: this,
5053             preventDefault:true,
5054             stopDefault:true
5055         });
5056
5057         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5058
5059         this.monthPicker = this.el.down('div.x-date-mp');
5060         this.monthPicker.enableDisplayMode('block');
5061         
5062         var kn = new Roo.KeyNav(this.eventEl, {
5063             "left" : function(e){
5064                 e.ctrlKey ?
5065                     this.showPrevMonth() :
5066                     this.update(this.activeDate.add("d", -1));
5067             },
5068
5069             "right" : function(e){
5070                 e.ctrlKey ?
5071                     this.showNextMonth() :
5072                     this.update(this.activeDate.add("d", 1));
5073             },
5074
5075             "up" : function(e){
5076                 e.ctrlKey ?
5077                     this.showNextYear() :
5078                     this.update(this.activeDate.add("d", -7));
5079             },
5080
5081             "down" : function(e){
5082                 e.ctrlKey ?
5083                     this.showPrevYear() :
5084                     this.update(this.activeDate.add("d", 7));
5085             },
5086
5087             "pageUp" : function(e){
5088                 this.showNextMonth();
5089             },
5090
5091             "pageDown" : function(e){
5092                 this.showPrevMonth();
5093             },
5094
5095             "enter" : function(e){
5096                 e.stopPropagation();
5097                 return true;
5098             },
5099
5100             scope : this
5101         });
5102
5103         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5104
5105         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5106
5107         this.el.unselectable();
5108         
5109         this.cells = this.el.select("table.x-date-inner tbody td");
5110         this.textNodes = this.el.query("table.x-date-inner tbody span");
5111
5112         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5113             text: "&#160;",
5114             tooltip: this.monthYearText
5115         });
5116
5117         this.mbtn.on('click', this.showMonthPicker, this);
5118         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5119
5120
5121         var today = (new Date()).dateFormat(this.format);
5122         
5123         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5124         if (this.showClear) {
5125             baseTb.add( new Roo.Toolbar.Fill());
5126         }
5127         baseTb.add({
5128             text: String.format(this.todayText, today),
5129             tooltip: String.format(this.todayTip, today),
5130             handler: this.selectToday,
5131             scope: this
5132         });
5133         
5134         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5135             
5136         //});
5137         if (this.showClear) {
5138             
5139             baseTb.add( new Roo.Toolbar.Fill());
5140             baseTb.add({
5141                 text: '&#160;',
5142                 cls: 'x-btn-icon x-btn-clear',
5143                 handler: function() {
5144                     //this.value = '';
5145                     this.fireEvent("select", this, '');
5146                 },
5147                 scope: this
5148             });
5149         }
5150         
5151         
5152         if(Roo.isIE){
5153             this.el.repaint();
5154         }
5155         this.update(this.value);
5156     },
5157
5158     createMonthPicker : function(){
5159         if(!this.monthPicker.dom.firstChild){
5160             var buf = ['<table border="0" cellspacing="0">'];
5161             for(var i = 0; i < 6; i++){
5162                 buf.push(
5163                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5164                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5165                     i == 0 ?
5166                     '<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>' :
5167                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5168                 );
5169             }
5170             buf.push(
5171                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5172                     this.okText,
5173                     '</button><button type="button" class="x-date-mp-cancel">',
5174                     this.cancelText,
5175                     '</button></td></tr>',
5176                 '</table>'
5177             );
5178             this.monthPicker.update(buf.join(''));
5179             this.monthPicker.on('click', this.onMonthClick, this);
5180             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5181
5182             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5183             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5184
5185             this.mpMonths.each(function(m, a, i){
5186                 i += 1;
5187                 if((i%2) == 0){
5188                     m.dom.xmonth = 5 + Math.round(i * .5);
5189                 }else{
5190                     m.dom.xmonth = Math.round((i-1) * .5);
5191                 }
5192             });
5193         }
5194     },
5195
5196     showMonthPicker : function(){
5197         this.createMonthPicker();
5198         var size = this.el.getSize();
5199         this.monthPicker.setSize(size);
5200         this.monthPicker.child('table').setSize(size);
5201
5202         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5203         this.updateMPMonth(this.mpSelMonth);
5204         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5205         this.updateMPYear(this.mpSelYear);
5206
5207         this.monthPicker.slideIn('t', {duration:.2});
5208     },
5209
5210     updateMPYear : function(y){
5211         this.mpyear = y;
5212         var ys = this.mpYears.elements;
5213         for(var i = 1; i <= 10; i++){
5214             var td = ys[i-1], y2;
5215             if((i%2) == 0){
5216                 y2 = y + Math.round(i * .5);
5217                 td.firstChild.innerHTML = y2;
5218                 td.xyear = y2;
5219             }else{
5220                 y2 = y - (5-Math.round(i * .5));
5221                 td.firstChild.innerHTML = y2;
5222                 td.xyear = y2;
5223             }
5224             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5225         }
5226     },
5227
5228     updateMPMonth : function(sm){
5229         this.mpMonths.each(function(m, a, i){
5230             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5231         });
5232     },
5233
5234     selectMPMonth: function(m){
5235         
5236     },
5237
5238     onMonthClick : function(e, t){
5239         e.stopEvent();
5240         var el = new Roo.Element(t), pn;
5241         if(el.is('button.x-date-mp-cancel')){
5242             this.hideMonthPicker();
5243         }
5244         else if(el.is('button.x-date-mp-ok')){
5245             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5246             this.hideMonthPicker();
5247         }
5248         else if(pn = el.up('td.x-date-mp-month', 2)){
5249             this.mpMonths.removeClass('x-date-mp-sel');
5250             pn.addClass('x-date-mp-sel');
5251             this.mpSelMonth = pn.dom.xmonth;
5252         }
5253         else if(pn = el.up('td.x-date-mp-year', 2)){
5254             this.mpYears.removeClass('x-date-mp-sel');
5255             pn.addClass('x-date-mp-sel');
5256             this.mpSelYear = pn.dom.xyear;
5257         }
5258         else if(el.is('a.x-date-mp-prev')){
5259             this.updateMPYear(this.mpyear-10);
5260         }
5261         else if(el.is('a.x-date-mp-next')){
5262             this.updateMPYear(this.mpyear+10);
5263         }
5264     },
5265
5266     onMonthDblClick : function(e, t){
5267         e.stopEvent();
5268         var el = new Roo.Element(t), pn;
5269         if(pn = el.up('td.x-date-mp-month', 2)){
5270             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5271             this.hideMonthPicker();
5272         }
5273         else if(pn = el.up('td.x-date-mp-year', 2)){
5274             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5275             this.hideMonthPicker();
5276         }
5277     },
5278
5279     hideMonthPicker : function(disableAnim){
5280         if(this.monthPicker){
5281             if(disableAnim === true){
5282                 this.monthPicker.hide();
5283             }else{
5284                 this.monthPicker.slideOut('t', {duration:.2});
5285             }
5286         }
5287     },
5288
5289     // private
5290     showPrevMonth : function(e){
5291         this.update(this.activeDate.add("mo", -1));
5292     },
5293
5294     // private
5295     showNextMonth : function(e){
5296         this.update(this.activeDate.add("mo", 1));
5297     },
5298
5299     // private
5300     showPrevYear : function(){
5301         this.update(this.activeDate.add("y", -1));
5302     },
5303
5304     // private
5305     showNextYear : function(){
5306         this.update(this.activeDate.add("y", 1));
5307     },
5308
5309     // private
5310     handleMouseWheel : function(e){
5311         var delta = e.getWheelDelta();
5312         if(delta > 0){
5313             this.showPrevMonth();
5314             e.stopEvent();
5315         } else if(delta < 0){
5316             this.showNextMonth();
5317             e.stopEvent();
5318         }
5319     },
5320
5321     // private
5322     handleDateClick : function(e, t){
5323         e.stopEvent();
5324         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5325             this.setValue(new Date(t.dateValue));
5326             this.fireEvent("select", this, this.value);
5327         }
5328     },
5329
5330     // private
5331     selectToday : function(){
5332         this.setValue(new Date().clearTime());
5333         this.fireEvent("select", this, this.value);
5334     },
5335
5336     // private
5337     update : function(date)
5338     {
5339         var vd = this.activeDate;
5340         this.activeDate = date;
5341         if(vd && this.el){
5342             var t = date.getTime();
5343             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5344                 this.cells.removeClass("x-date-selected");
5345                 this.cells.each(function(c){
5346                    if(c.dom.firstChild.dateValue == t){
5347                        c.addClass("x-date-selected");
5348                        setTimeout(function(){
5349                             try{c.dom.firstChild.focus();}catch(e){}
5350                        }, 50);
5351                        return false;
5352                    }
5353                 });
5354                 return;
5355             }
5356         }
5357         
5358         var days = date.getDaysInMonth();
5359         var firstOfMonth = date.getFirstDateOfMonth();
5360         var startingPos = firstOfMonth.getDay()-this.startDay;
5361
5362         if(startingPos <= this.startDay){
5363             startingPos += 7;
5364         }
5365
5366         var pm = date.add("mo", -1);
5367         var prevStart = pm.getDaysInMonth()-startingPos;
5368
5369         var cells = this.cells.elements;
5370         var textEls = this.textNodes;
5371         days += startingPos;
5372
5373         // convert everything to numbers so it's fast
5374         var day = 86400000;
5375         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5376         var today = new Date().clearTime().getTime();
5377         var sel = date.clearTime().getTime();
5378         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5379         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5380         var ddMatch = this.disabledDatesRE;
5381         var ddText = this.disabledDatesText;
5382         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5383         var ddaysText = this.disabledDaysText;
5384         var format = this.format;
5385
5386         var setCellClass = function(cal, cell){
5387             cell.title = "";
5388             var t = d.getTime();
5389             cell.firstChild.dateValue = t;
5390             if(t == today){
5391                 cell.className += " x-date-today";
5392                 cell.title = cal.todayText;
5393             }
5394             if(t == sel){
5395                 cell.className += " x-date-selected";
5396                 setTimeout(function(){
5397                     try{cell.firstChild.focus();}catch(e){}
5398                 }, 50);
5399             }
5400             // disabling
5401             if(t < min) {
5402                 cell.className = " x-date-disabled";
5403                 cell.title = cal.minText;
5404                 return;
5405             }
5406             if(t > max) {
5407                 cell.className = " x-date-disabled";
5408                 cell.title = cal.maxText;
5409                 return;
5410             }
5411             if(ddays){
5412                 if(ddays.indexOf(d.getDay()) != -1){
5413                     cell.title = ddaysText;
5414                     cell.className = " x-date-disabled";
5415                 }
5416             }
5417             if(ddMatch && format){
5418                 var fvalue = d.dateFormat(format);
5419                 if(ddMatch.test(fvalue)){
5420                     cell.title = ddText.replace("%0", fvalue);
5421                     cell.className = " x-date-disabled";
5422                 }
5423             }
5424         };
5425
5426         var i = 0;
5427         for(; i < startingPos; i++) {
5428             textEls[i].innerHTML = (++prevStart);
5429             d.setDate(d.getDate()+1);
5430             cells[i].className = "x-date-prevday";
5431             setCellClass(this, cells[i]);
5432         }
5433         for(; i < days; i++){
5434             intDay = i - startingPos + 1;
5435             textEls[i].innerHTML = (intDay);
5436             d.setDate(d.getDate()+1);
5437             cells[i].className = "x-date-active";
5438             setCellClass(this, cells[i]);
5439         }
5440         var extraDays = 0;
5441         for(; i < 42; i++) {
5442              textEls[i].innerHTML = (++extraDays);
5443              d.setDate(d.getDate()+1);
5444              cells[i].className = "x-date-nextday";
5445              setCellClass(this, cells[i]);
5446         }
5447
5448         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5449         this.fireEvent('monthchange', this, date);
5450         
5451         if(!this.internalRender){
5452             var main = this.el.dom.firstChild;
5453             var w = main.offsetWidth;
5454             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5455             Roo.fly(main).setWidth(w);
5456             this.internalRender = true;
5457             // opera does not respect the auto grow header center column
5458             // then, after it gets a width opera refuses to recalculate
5459             // without a second pass
5460             if(Roo.isOpera && !this.secondPass){
5461                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5462                 this.secondPass = true;
5463                 this.update.defer(10, this, [date]);
5464             }
5465         }
5466         
5467         
5468     }
5469 });        /*
5470  * Based on:
5471  * Ext JS Library 1.1.1
5472  * Copyright(c) 2006-2007, Ext JS, LLC.
5473  *
5474  * Originally Released Under LGPL - original licence link has changed is not relivant.
5475  *
5476  * Fork - LGPL
5477  * <script type="text/javascript">
5478  */
5479 /**
5480  * @class Roo.TabPanel
5481  * @extends Roo.util.Observable
5482  * A lightweight tab container.
5483  * <br><br>
5484  * Usage:
5485  * <pre><code>
5486 // basic tabs 1, built from existing content
5487 var tabs = new Roo.TabPanel("tabs1");
5488 tabs.addTab("script", "View Script");
5489 tabs.addTab("markup", "View Markup");
5490 tabs.activate("script");
5491
5492 // more advanced tabs, built from javascript
5493 var jtabs = new Roo.TabPanel("jtabs");
5494 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5495
5496 // set up the UpdateManager
5497 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5498 var updater = tab2.getUpdateManager();
5499 updater.setDefaultUrl("ajax1.htm");
5500 tab2.on('activate', updater.refresh, updater, true);
5501
5502 // Use setUrl for Ajax loading
5503 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5504 tab3.setUrl("ajax2.htm", null, true);
5505
5506 // Disabled tab
5507 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5508 tab4.disable();
5509
5510 jtabs.activate("jtabs-1");
5511  * </code></pre>
5512  * @constructor
5513  * Create a new TabPanel.
5514  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5515  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5516  */
5517 Roo.TabPanel = function(container, config){
5518     /**
5519     * The container element for this TabPanel.
5520     * @type Roo.Element
5521     */
5522     this.el = Roo.get(container, true);
5523     if(config){
5524         if(typeof config == "boolean"){
5525             this.tabPosition = config ? "bottom" : "top";
5526         }else{
5527             Roo.apply(this, config);
5528         }
5529     }
5530     if(this.tabPosition == "bottom"){
5531         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5532         this.el.addClass("x-tabs-bottom");
5533     }
5534     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5535     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5536     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5537     if(Roo.isIE){
5538         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5539     }
5540     if(this.tabPosition != "bottom"){
5541         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5542          * @type Roo.Element
5543          */
5544         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5545         this.el.addClass("x-tabs-top");
5546     }
5547     this.items = [];
5548
5549     this.bodyEl.setStyle("position", "relative");
5550
5551     this.active = null;
5552     this.activateDelegate = this.activate.createDelegate(this);
5553
5554     this.addEvents({
5555         /**
5556          * @event tabchange
5557          * Fires when the active tab changes
5558          * @param {Roo.TabPanel} this
5559          * @param {Roo.TabPanelItem} activePanel The new active tab
5560          */
5561         "tabchange": true,
5562         /**
5563          * @event beforetabchange
5564          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5565          * @param {Roo.TabPanel} this
5566          * @param {Object} e Set cancel to true on this object to cancel the tab change
5567          * @param {Roo.TabPanelItem} tab The tab being changed to
5568          */
5569         "beforetabchange" : true
5570     });
5571
5572     Roo.EventManager.onWindowResize(this.onResize, this);
5573     this.cpad = this.el.getPadding("lr");
5574     this.hiddenCount = 0;
5575
5576
5577     // toolbar on the tabbar support...
5578     if (this.toolbar) {
5579         var tcfg = this.toolbar;
5580         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5581         this.toolbar = new Roo.Toolbar(tcfg);
5582         if (Roo.isSafari) {
5583             var tbl = tcfg.container.child('table', true);
5584             tbl.setAttribute('width', '100%');
5585         }
5586         
5587     }
5588    
5589
5590
5591     Roo.TabPanel.superclass.constructor.call(this);
5592 };
5593
5594 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5595     /*
5596      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5597      */
5598     tabPosition : "top",
5599     /*
5600      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5601      */
5602     currentTabWidth : 0,
5603     /*
5604      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5605      */
5606     minTabWidth : 40,
5607     /*
5608      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5609      */
5610     maxTabWidth : 250,
5611     /*
5612      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5613      */
5614     preferredTabWidth : 175,
5615     /*
5616      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5617      */
5618     resizeTabs : false,
5619     /*
5620      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5621      */
5622     monitorResize : true,
5623     /*
5624      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5625      */
5626     toolbar : false,
5627
5628     /**
5629      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5630      * @param {String} id The id of the div to use <b>or create</b>
5631      * @param {String} text The text for the tab
5632      * @param {String} content (optional) Content to put in the TabPanelItem body
5633      * @param {Boolean} closable (optional) True to create a close icon on the tab
5634      * @return {Roo.TabPanelItem} The created TabPanelItem
5635      */
5636     addTab : function(id, text, content, closable){
5637         var item = new Roo.TabPanelItem(this, id, text, closable);
5638         this.addTabItem(item);
5639         if(content){
5640             item.setContent(content);
5641         }
5642         return item;
5643     },
5644
5645     /**
5646      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5647      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5648      * @return {Roo.TabPanelItem}
5649      */
5650     getTab : function(id){
5651         return this.items[id];
5652     },
5653
5654     /**
5655      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5656      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5657      */
5658     hideTab : function(id){
5659         var t = this.items[id];
5660         if(!t.isHidden()){
5661            t.setHidden(true);
5662            this.hiddenCount++;
5663            this.autoSizeTabs();
5664         }
5665     },
5666
5667     /**
5668      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5669      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5670      */
5671     unhideTab : function(id){
5672         var t = this.items[id];
5673         if(t.isHidden()){
5674            t.setHidden(false);
5675            this.hiddenCount--;
5676            this.autoSizeTabs();
5677         }
5678     },
5679
5680     /**
5681      * Adds an existing {@link Roo.TabPanelItem}.
5682      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5683      */
5684     addTabItem : function(item){
5685         this.items[item.id] = item;
5686         this.items.push(item);
5687         if(this.resizeTabs){
5688            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5689            this.autoSizeTabs();
5690         }else{
5691             item.autoSize();
5692         }
5693     },
5694
5695     /**
5696      * Removes a {@link Roo.TabPanelItem}.
5697      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5698      */
5699     removeTab : function(id){
5700         var items = this.items;
5701         var tab = items[id];
5702         if(!tab) { return; }
5703         var index = items.indexOf(tab);
5704         if(this.active == tab && items.length > 1){
5705             var newTab = this.getNextAvailable(index);
5706             if(newTab) {
5707                 newTab.activate();
5708             }
5709         }
5710         this.stripEl.dom.removeChild(tab.pnode.dom);
5711         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5712             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5713         }
5714         items.splice(index, 1);
5715         delete this.items[tab.id];
5716         tab.fireEvent("close", tab);
5717         tab.purgeListeners();
5718         this.autoSizeTabs();
5719     },
5720
5721     getNextAvailable : function(start){
5722         var items = this.items;
5723         var index = start;
5724         // look for a next tab that will slide over to
5725         // replace the one being removed
5726         while(index < items.length){
5727             var item = items[++index];
5728             if(item && !item.isHidden()){
5729                 return item;
5730             }
5731         }
5732         // if one isn't found select the previous tab (on the left)
5733         index = start;
5734         while(index >= 0){
5735             var item = items[--index];
5736             if(item && !item.isHidden()){
5737                 return item;
5738             }
5739         }
5740         return null;
5741     },
5742
5743     /**
5744      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5745      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5746      */
5747     disableTab : function(id){
5748         var tab = this.items[id];
5749         if(tab && this.active != tab){
5750             tab.disable();
5751         }
5752     },
5753
5754     /**
5755      * Enables a {@link Roo.TabPanelItem} that is disabled.
5756      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5757      */
5758     enableTab : function(id){
5759         var tab = this.items[id];
5760         tab.enable();
5761     },
5762
5763     /**
5764      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5765      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5766      * @return {Roo.TabPanelItem} The TabPanelItem.
5767      */
5768     activate : function(id){
5769         var tab = this.items[id];
5770         if(!tab){
5771             return null;
5772         }
5773         if(tab == this.active || tab.disabled){
5774             return tab;
5775         }
5776         var e = {};
5777         this.fireEvent("beforetabchange", this, e, tab);
5778         if(e.cancel !== true && !tab.disabled){
5779             if(this.active){
5780                 this.active.hide();
5781             }
5782             this.active = this.items[id];
5783             this.active.show();
5784             this.fireEvent("tabchange", this, this.active);
5785         }
5786         return tab;
5787     },
5788
5789     /**
5790      * Gets the active {@link Roo.TabPanelItem}.
5791      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5792      */
5793     getActiveTab : function(){
5794         return this.active;
5795     },
5796
5797     /**
5798      * Updates the tab body element to fit the height of the container element
5799      * for overflow scrolling
5800      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5801      */
5802     syncHeight : function(targetHeight){
5803         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5804         var bm = this.bodyEl.getMargins();
5805         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5806         this.bodyEl.setHeight(newHeight);
5807         return newHeight;
5808     },
5809
5810     onResize : function(){
5811         if(this.monitorResize){
5812             this.autoSizeTabs();
5813         }
5814     },
5815
5816     /**
5817      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5818      */
5819     beginUpdate : function(){
5820         this.updating = true;
5821     },
5822
5823     /**
5824      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5825      */
5826     endUpdate : function(){
5827         this.updating = false;
5828         this.autoSizeTabs();
5829     },
5830
5831     /**
5832      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5833      */
5834     autoSizeTabs : function(){
5835         var count = this.items.length;
5836         var vcount = count - this.hiddenCount;
5837         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5838             return;
5839         }
5840         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5841         var availWidth = Math.floor(w / vcount);
5842         var b = this.stripBody;
5843         if(b.getWidth() > w){
5844             var tabs = this.items;
5845             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5846             if(availWidth < this.minTabWidth){
5847                 /*if(!this.sleft){    // incomplete scrolling code
5848                     this.createScrollButtons();
5849                 }
5850                 this.showScroll();
5851                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5852             }
5853         }else{
5854             if(this.currentTabWidth < this.preferredTabWidth){
5855                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5856             }
5857         }
5858     },
5859
5860     /**
5861      * Returns the number of tabs in this TabPanel.
5862      * @return {Number}
5863      */
5864      getCount : function(){
5865          return this.items.length;
5866      },
5867
5868     /**
5869      * Resizes all the tabs to the passed width
5870      * @param {Number} The new width
5871      */
5872     setTabWidth : function(width){
5873         this.currentTabWidth = width;
5874         for(var i = 0, len = this.items.length; i < len; i++) {
5875                 if(!this.items[i].isHidden()) {
5876                 this.items[i].setWidth(width);
5877             }
5878         }
5879     },
5880
5881     /**
5882      * Destroys this TabPanel
5883      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5884      */
5885     destroy : function(removeEl){
5886         Roo.EventManager.removeResizeListener(this.onResize, this);
5887         for(var i = 0, len = this.items.length; i < len; i++){
5888             this.items[i].purgeListeners();
5889         }
5890         if(removeEl === true){
5891             this.el.update("");
5892             this.el.remove();
5893         }
5894     }
5895 });
5896
5897 /**
5898  * @class Roo.TabPanelItem
5899  * @extends Roo.util.Observable
5900  * Represents an individual item (tab plus body) in a TabPanel.
5901  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5902  * @param {String} id The id of this TabPanelItem
5903  * @param {String} text The text for the tab of this TabPanelItem
5904  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5905  */
5906 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5907     /**
5908      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5909      * @type Roo.TabPanel
5910      */
5911     this.tabPanel = tabPanel;
5912     /**
5913      * The id for this TabPanelItem
5914      * @type String
5915      */
5916     this.id = id;
5917     /** @private */
5918     this.disabled = false;
5919     /** @private */
5920     this.text = text;
5921     /** @private */
5922     this.loaded = false;
5923     this.closable = closable;
5924
5925     /**
5926      * The body element for this TabPanelItem.
5927      * @type Roo.Element
5928      */
5929     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5930     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5931     this.bodyEl.setStyle("display", "block");
5932     this.bodyEl.setStyle("zoom", "1");
5933     this.hideAction();
5934
5935     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5936     /** @private */
5937     this.el = Roo.get(els.el, true);
5938     this.inner = Roo.get(els.inner, true);
5939     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5940     this.pnode = Roo.get(els.el.parentNode, true);
5941     this.el.on("mousedown", this.onTabMouseDown, this);
5942     this.el.on("click", this.onTabClick, this);
5943     /** @private */
5944     if(closable){
5945         var c = Roo.get(els.close, true);
5946         c.dom.title = this.closeText;
5947         c.addClassOnOver("close-over");
5948         c.on("click", this.closeClick, this);
5949      }
5950
5951     this.addEvents({
5952          /**
5953          * @event activate
5954          * Fires when this tab becomes the active tab.
5955          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5956          * @param {Roo.TabPanelItem} this
5957          */
5958         "activate": true,
5959         /**
5960          * @event beforeclose
5961          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5962          * @param {Roo.TabPanelItem} this
5963          * @param {Object} e Set cancel to true on this object to cancel the close.
5964          */
5965         "beforeclose": true,
5966         /**
5967          * @event close
5968          * Fires when this tab is closed.
5969          * @param {Roo.TabPanelItem} this
5970          */
5971          "close": true,
5972         /**
5973          * @event deactivate
5974          * Fires when this tab is no longer the active tab.
5975          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5976          * @param {Roo.TabPanelItem} this
5977          */
5978          "deactivate" : true
5979     });
5980     this.hidden = false;
5981
5982     Roo.TabPanelItem.superclass.constructor.call(this);
5983 };
5984
5985 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5986     purgeListeners : function(){
5987        Roo.util.Observable.prototype.purgeListeners.call(this);
5988        this.el.removeAllListeners();
5989     },
5990     /**
5991      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5992      */
5993     show : function(){
5994         this.pnode.addClass("on");
5995         this.showAction();
5996         if(Roo.isOpera){
5997             this.tabPanel.stripWrap.repaint();
5998         }
5999         this.fireEvent("activate", this.tabPanel, this);
6000     },
6001
6002     /**
6003      * Returns true if this tab is the active tab.
6004      * @return {Boolean}
6005      */
6006     isActive : function(){
6007         return this.tabPanel.getActiveTab() == this;
6008     },
6009
6010     /**
6011      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6012      */
6013     hide : function(){
6014         this.pnode.removeClass("on");
6015         this.hideAction();
6016         this.fireEvent("deactivate", this.tabPanel, this);
6017     },
6018
6019     hideAction : function(){
6020         this.bodyEl.hide();
6021         this.bodyEl.setStyle("position", "absolute");
6022         this.bodyEl.setLeft("-20000px");
6023         this.bodyEl.setTop("-20000px");
6024     },
6025
6026     showAction : function(){
6027         this.bodyEl.setStyle("position", "relative");
6028         this.bodyEl.setTop("");
6029         this.bodyEl.setLeft("");
6030         this.bodyEl.show();
6031     },
6032
6033     /**
6034      * Set the tooltip for the tab.
6035      * @param {String} tooltip The tab's tooltip
6036      */
6037     setTooltip : function(text){
6038         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6039             this.textEl.dom.qtip = text;
6040             this.textEl.dom.removeAttribute('title');
6041         }else{
6042             this.textEl.dom.title = text;
6043         }
6044     },
6045
6046     onTabClick : function(e){
6047         e.preventDefault();
6048         this.tabPanel.activate(this.id);
6049     },
6050
6051     onTabMouseDown : function(e){
6052         e.preventDefault();
6053         this.tabPanel.activate(this.id);
6054     },
6055
6056     getWidth : function(){
6057         return this.inner.getWidth();
6058     },
6059
6060     setWidth : function(width){
6061         var iwidth = width - this.pnode.getPadding("lr");
6062         this.inner.setWidth(iwidth);
6063         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6064         this.pnode.setWidth(width);
6065     },
6066
6067     /**
6068      * Show or hide the tab
6069      * @param {Boolean} hidden True to hide or false to show.
6070      */
6071     setHidden : function(hidden){
6072         this.hidden = hidden;
6073         this.pnode.setStyle("display", hidden ? "none" : "");
6074     },
6075
6076     /**
6077      * Returns true if this tab is "hidden"
6078      * @return {Boolean}
6079      */
6080     isHidden : function(){
6081         return this.hidden;
6082     },
6083
6084     /**
6085      * Returns the text for this tab
6086      * @return {String}
6087      */
6088     getText : function(){
6089         return this.text;
6090     },
6091
6092     autoSize : function(){
6093         //this.el.beginMeasure();
6094         this.textEl.setWidth(1);
6095         /*
6096          *  #2804 [new] Tabs in Roojs
6097          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6098          */
6099         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6100         //this.el.endMeasure();
6101     },
6102
6103     /**
6104      * Sets the text for the tab (Note: this also sets the tooltip text)
6105      * @param {String} text The tab's text and tooltip
6106      */
6107     setText : function(text){
6108         this.text = text;
6109         this.textEl.update(text);
6110         this.setTooltip(text);
6111         if(!this.tabPanel.resizeTabs){
6112             this.autoSize();
6113         }
6114     },
6115     /**
6116      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6117      */
6118     activate : function(){
6119         this.tabPanel.activate(this.id);
6120     },
6121
6122     /**
6123      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6124      */
6125     disable : function(){
6126         if(this.tabPanel.active != this){
6127             this.disabled = true;
6128             this.pnode.addClass("disabled");
6129         }
6130     },
6131
6132     /**
6133      * Enables this TabPanelItem if it was previously disabled.
6134      */
6135     enable : function(){
6136         this.disabled = false;
6137         this.pnode.removeClass("disabled");
6138     },
6139
6140     /**
6141      * Sets the content for this TabPanelItem.
6142      * @param {String} content The content
6143      * @param {Boolean} loadScripts true to look for and load scripts
6144      */
6145     setContent : function(content, loadScripts){
6146         this.bodyEl.update(content, loadScripts);
6147     },
6148
6149     /**
6150      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6151      * @return {Roo.UpdateManager} The UpdateManager
6152      */
6153     getUpdateManager : function(){
6154         return this.bodyEl.getUpdateManager();
6155     },
6156
6157     /**
6158      * Set a URL to be used to load the content for this TabPanelItem.
6159      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6160      * @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)
6161      * @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)
6162      * @return {Roo.UpdateManager} The UpdateManager
6163      */
6164     setUrl : function(url, params, loadOnce){
6165         if(this.refreshDelegate){
6166             this.un('activate', this.refreshDelegate);
6167         }
6168         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6169         this.on("activate", this.refreshDelegate);
6170         return this.bodyEl.getUpdateManager();
6171     },
6172
6173     /** @private */
6174     _handleRefresh : function(url, params, loadOnce){
6175         if(!loadOnce || !this.loaded){
6176             var updater = this.bodyEl.getUpdateManager();
6177             updater.update(url, params, this._setLoaded.createDelegate(this));
6178         }
6179     },
6180
6181     /**
6182      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6183      *   Will fail silently if the setUrl method has not been called.
6184      *   This does not activate the panel, just updates its content.
6185      */
6186     refresh : function(){
6187         if(this.refreshDelegate){
6188            this.loaded = false;
6189            this.refreshDelegate();
6190         }
6191     },
6192
6193     /** @private */
6194     _setLoaded : function(){
6195         this.loaded = true;
6196     },
6197
6198     /** @private */
6199     closeClick : function(e){
6200         var o = {};
6201         e.stopEvent();
6202         this.fireEvent("beforeclose", this, o);
6203         if(o.cancel !== true){
6204             this.tabPanel.removeTab(this.id);
6205         }
6206     },
6207     /**
6208      * The text displayed in the tooltip for the close icon.
6209      * @type String
6210      */
6211     closeText : "Close this tab"
6212 });
6213
6214 /** @private */
6215 Roo.TabPanel.prototype.createStrip = function(container){
6216     var strip = document.createElement("div");
6217     strip.className = "x-tabs-wrap";
6218     container.appendChild(strip);
6219     return strip;
6220 };
6221 /** @private */
6222 Roo.TabPanel.prototype.createStripList = function(strip){
6223     // div wrapper for retard IE
6224     // returns the "tr" element.
6225     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6226         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6227         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6228     return strip.firstChild.firstChild.firstChild.firstChild;
6229 };
6230 /** @private */
6231 Roo.TabPanel.prototype.createBody = function(container){
6232     var body = document.createElement("div");
6233     Roo.id(body, "tab-body");
6234     Roo.fly(body).addClass("x-tabs-body");
6235     container.appendChild(body);
6236     return body;
6237 };
6238 /** @private */
6239 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6240     var body = Roo.getDom(id);
6241     if(!body){
6242         body = document.createElement("div");
6243         body.id = id;
6244     }
6245     Roo.fly(body).addClass("x-tabs-item-body");
6246     bodyEl.insertBefore(body, bodyEl.firstChild);
6247     return body;
6248 };
6249 /** @private */
6250 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6251     var td = document.createElement("td");
6252     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6253     //stripEl.appendChild(td);
6254     if(closable){
6255         td.className = "x-tabs-closable";
6256         if(!this.closeTpl){
6257             this.closeTpl = new Roo.Template(
6258                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6259                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6260                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6261             );
6262         }
6263         var el = this.closeTpl.overwrite(td, {"text": text});
6264         var close = el.getElementsByTagName("div")[0];
6265         var inner = el.getElementsByTagName("em")[0];
6266         return {"el": el, "close": close, "inner": inner};
6267     } else {
6268         if(!this.tabTpl){
6269             this.tabTpl = new Roo.Template(
6270                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6271                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6272             );
6273         }
6274         var el = this.tabTpl.overwrite(td, {"text": text});
6275         var inner = el.getElementsByTagName("em")[0];
6276         return {"el": el, "inner": inner};
6277     }
6278 };/*
6279  * Based on:
6280  * Ext JS Library 1.1.1
6281  * Copyright(c) 2006-2007, Ext JS, LLC.
6282  *
6283  * Originally Released Under LGPL - original licence link has changed is not relivant.
6284  *
6285  * Fork - LGPL
6286  * <script type="text/javascript">
6287  */
6288
6289 /**
6290  * @class Roo.Button
6291  * @extends Roo.util.Observable
6292  * Simple Button class
6293  * @cfg {String} text The button text
6294  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6295  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6296  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6297  * @cfg {Object} scope The scope of the handler
6298  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6299  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6300  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6301  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6302  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6303  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6304    applies if enableToggle = true)
6305  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6306  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6307   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6308  * @constructor
6309  * Create a new button
6310  * @param {Object} config The config object
6311  */
6312 Roo.Button = function(renderTo, config)
6313 {
6314     if (!config) {
6315         config = renderTo;
6316         renderTo = config.renderTo || false;
6317     }
6318     
6319     Roo.apply(this, config);
6320     this.addEvents({
6321         /**
6322              * @event click
6323              * Fires when this button is clicked
6324              * @param {Button} this
6325              * @param {EventObject} e The click event
6326              */
6327             "click" : true,
6328         /**
6329              * @event toggle
6330              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6331              * @param {Button} this
6332              * @param {Boolean} pressed
6333              */
6334             "toggle" : true,
6335         /**
6336              * @event mouseover
6337              * Fires when the mouse hovers over the button
6338              * @param {Button} this
6339              * @param {Event} e The event object
6340              */
6341         'mouseover' : true,
6342         /**
6343              * @event mouseout
6344              * Fires when the mouse exits the button
6345              * @param {Button} this
6346              * @param {Event} e The event object
6347              */
6348         'mouseout': true,
6349          /**
6350              * @event render
6351              * Fires when the button is rendered
6352              * @param {Button} this
6353              */
6354         'render': true
6355     });
6356     if(this.menu){
6357         this.menu = Roo.menu.MenuMgr.get(this.menu);
6358     }
6359     // register listeners first!!  - so render can be captured..
6360     Roo.util.Observable.call(this);
6361     if(renderTo){
6362         this.render(renderTo);
6363     }
6364     
6365   
6366 };
6367
6368 Roo.extend(Roo.Button, Roo.util.Observable, {
6369     /**
6370      * 
6371      */
6372     
6373     /**
6374      * Read-only. True if this button is hidden
6375      * @type Boolean
6376      */
6377     hidden : false,
6378     /**
6379      * Read-only. True if this button is disabled
6380      * @type Boolean
6381      */
6382     disabled : false,
6383     /**
6384      * Read-only. True if this button is pressed (only if enableToggle = true)
6385      * @type Boolean
6386      */
6387     pressed : false,
6388
6389     /**
6390      * @cfg {Number} tabIndex 
6391      * The DOM tabIndex for this button (defaults to undefined)
6392      */
6393     tabIndex : undefined,
6394
6395     /**
6396      * @cfg {Boolean} enableToggle
6397      * True to enable pressed/not pressed toggling (defaults to false)
6398      */
6399     enableToggle: false,
6400     /**
6401      * @cfg {Roo.menu.Menu} menu
6402      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6403      */
6404     menu : undefined,
6405     /**
6406      * @cfg {String} menuAlign
6407      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6408      */
6409     menuAlign : "tl-bl?",
6410
6411     /**
6412      * @cfg {String} iconCls
6413      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6414      */
6415     iconCls : undefined,
6416     /**
6417      * @cfg {String} type
6418      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6419      */
6420     type : 'button',
6421
6422     // private
6423     menuClassTarget: 'tr',
6424
6425     /**
6426      * @cfg {String} clickEvent
6427      * The type of event to map to the button's event handler (defaults to 'click')
6428      */
6429     clickEvent : 'click',
6430
6431     /**
6432      * @cfg {Boolean} handleMouseEvents
6433      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6434      */
6435     handleMouseEvents : true,
6436
6437     /**
6438      * @cfg {String} tooltipType
6439      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6440      */
6441     tooltipType : 'qtip',
6442
6443     /**
6444      * @cfg {String} cls
6445      * A CSS class to apply to the button's main element.
6446      */
6447     
6448     /**
6449      * @cfg {Roo.Template} template (Optional)
6450      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6451      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6452      * require code modifications if required elements (e.g. a button) aren't present.
6453      */
6454
6455     // private
6456     render : function(renderTo){
6457         var btn;
6458         if(this.hideParent){
6459             this.parentEl = Roo.get(renderTo);
6460         }
6461         if(!this.dhconfig){
6462             if(!this.template){
6463                 if(!Roo.Button.buttonTemplate){
6464                     // hideous table template
6465                     Roo.Button.buttonTemplate = new Roo.Template(
6466                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6467                         '<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>',
6468                         "</tr></tbody></table>");
6469                 }
6470                 this.template = Roo.Button.buttonTemplate;
6471             }
6472             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6473             var btnEl = btn.child("button:first");
6474             btnEl.on('focus', this.onFocus, this);
6475             btnEl.on('blur', this.onBlur, this);
6476             if(this.cls){
6477                 btn.addClass(this.cls);
6478             }
6479             if(this.icon){
6480                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6481             }
6482             if(this.iconCls){
6483                 btnEl.addClass(this.iconCls);
6484                 if(!this.cls){
6485                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6486                 }
6487             }
6488             if(this.tabIndex !== undefined){
6489                 btnEl.dom.tabIndex = this.tabIndex;
6490             }
6491             if(this.tooltip){
6492                 if(typeof this.tooltip == 'object'){
6493                     Roo.QuickTips.tips(Roo.apply({
6494                           target: btnEl.id
6495                     }, this.tooltip));
6496                 } else {
6497                     btnEl.dom[this.tooltipType] = this.tooltip;
6498                 }
6499             }
6500         }else{
6501             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6502         }
6503         this.el = btn;
6504         if(this.id){
6505             this.el.dom.id = this.el.id = this.id;
6506         }
6507         if(this.menu){
6508             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6509             this.menu.on("show", this.onMenuShow, this);
6510             this.menu.on("hide", this.onMenuHide, this);
6511         }
6512         btn.addClass("x-btn");
6513         if(Roo.isIE && !Roo.isIE7){
6514             this.autoWidth.defer(1, this);
6515         }else{
6516             this.autoWidth();
6517         }
6518         if(this.handleMouseEvents){
6519             btn.on("mouseover", this.onMouseOver, this);
6520             btn.on("mouseout", this.onMouseOut, this);
6521             btn.on("mousedown", this.onMouseDown, this);
6522         }
6523         btn.on(this.clickEvent, this.onClick, this);
6524         //btn.on("mouseup", this.onMouseUp, this);
6525         if(this.hidden){
6526             this.hide();
6527         }
6528         if(this.disabled){
6529             this.disable();
6530         }
6531         Roo.ButtonToggleMgr.register(this);
6532         if(this.pressed){
6533             this.el.addClass("x-btn-pressed");
6534         }
6535         if(this.repeat){
6536             var repeater = new Roo.util.ClickRepeater(btn,
6537                 typeof this.repeat == "object" ? this.repeat : {}
6538             );
6539             repeater.on("click", this.onClick,  this);
6540         }
6541         
6542         this.fireEvent('render', this);
6543         
6544     },
6545     /**
6546      * Returns the button's underlying element
6547      * @return {Roo.Element} The element
6548      */
6549     getEl : function(){
6550         return this.el;  
6551     },
6552     
6553     /**
6554      * Destroys this Button and removes any listeners.
6555      */
6556     destroy : function(){
6557         Roo.ButtonToggleMgr.unregister(this);
6558         this.el.removeAllListeners();
6559         this.purgeListeners();
6560         this.el.remove();
6561     },
6562
6563     // private
6564     autoWidth : function(){
6565         if(this.el){
6566             this.el.setWidth("auto");
6567             if(Roo.isIE7 && Roo.isStrict){
6568                 var ib = this.el.child('button');
6569                 if(ib && ib.getWidth() > 20){
6570                     ib.clip();
6571                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6572                 }
6573             }
6574             if(this.minWidth){
6575                 if(this.hidden){
6576                     this.el.beginMeasure();
6577                 }
6578                 if(this.el.getWidth() < this.minWidth){
6579                     this.el.setWidth(this.minWidth);
6580                 }
6581                 if(this.hidden){
6582                     this.el.endMeasure();
6583                 }
6584             }
6585         }
6586     },
6587
6588     /**
6589      * Assigns this button's click handler
6590      * @param {Function} handler The function to call when the button is clicked
6591      * @param {Object} scope (optional) Scope for the function passed in
6592      */
6593     setHandler : function(handler, scope){
6594         this.handler = handler;
6595         this.scope = scope;  
6596     },
6597     
6598     /**
6599      * Sets this button's text
6600      * @param {String} text The button text
6601      */
6602     setText : function(text){
6603         this.text = text;
6604         if(this.el){
6605             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6606         }
6607         this.autoWidth();
6608     },
6609     
6610     /**
6611      * Gets the text for this button
6612      * @return {String} The button text
6613      */
6614     getText : function(){
6615         return this.text;  
6616     },
6617     
6618     /**
6619      * Show this button
6620      */
6621     show: function(){
6622         this.hidden = false;
6623         if(this.el){
6624             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6625         }
6626     },
6627     
6628     /**
6629      * Hide this button
6630      */
6631     hide: function(){
6632         this.hidden = true;
6633         if(this.el){
6634             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6635         }
6636     },
6637     
6638     /**
6639      * Convenience function for boolean show/hide
6640      * @param {Boolean} visible True to show, false to hide
6641      */
6642     setVisible: function(visible){
6643         if(visible) {
6644             this.show();
6645         }else{
6646             this.hide();
6647         }
6648     },
6649     /**
6650          * Similar to toggle, but does not trigger event.
6651          * @param {Boolean} state [required] Force a particular state
6652          */
6653         setPressed : function(state)
6654         {
6655             if(state != this.pressed){
6656             if(state){
6657                 this.el.addClass("x-btn-pressed");
6658                 this.pressed = true;
6659             }else{
6660                 this.el.removeClass("x-btn-pressed");
6661                 this.pressed = false;
6662             }
6663         }
6664         },
6665         
6666     /**
6667      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6668      * @param {Boolean} state (optional) Force a particular state
6669      */
6670     toggle : function(state){
6671         state = state === undefined ? !this.pressed : state;
6672         if(state != this.pressed){
6673             if(state){
6674                 this.el.addClass("x-btn-pressed");
6675                 this.pressed = true;
6676                 this.fireEvent("toggle", this, true);
6677             }else{
6678                 this.el.removeClass("x-btn-pressed");
6679                 this.pressed = false;
6680                 this.fireEvent("toggle", this, false);
6681             }
6682             if(this.toggleHandler){
6683                 this.toggleHandler.call(this.scope || this, this, state);
6684             }
6685         }
6686     },
6687     
6688         
6689         
6690     /**
6691      * Focus the button
6692      */
6693     focus : function(){
6694         this.el.child('button:first').focus();
6695     },
6696     
6697     /**
6698      * Disable this button
6699      */
6700     disable : function(){
6701         if(this.el){
6702             this.el.addClass("x-btn-disabled");
6703         }
6704         this.disabled = true;
6705     },
6706     
6707     /**
6708      * Enable this button
6709      */
6710     enable : function(){
6711         if(this.el){
6712             this.el.removeClass("x-btn-disabled");
6713         }
6714         this.disabled = false;
6715     },
6716
6717     /**
6718      * Convenience function for boolean enable/disable
6719      * @param {Boolean} enabled True to enable, false to disable
6720      */
6721     setDisabled : function(v){
6722         this[v !== true ? "enable" : "disable"]();
6723     },
6724
6725     // private
6726     onClick : function(e)
6727     {
6728         if(e){
6729             e.preventDefault();
6730         }
6731         if(e.button != 0){
6732             return;
6733         }
6734         if(!this.disabled){
6735             if(this.enableToggle){
6736                 this.toggle();
6737             }
6738             if(this.menu && !this.menu.isVisible()){
6739                 this.menu.show(this.el, this.menuAlign);
6740             }
6741             this.fireEvent("click", this, e);
6742             if(this.handler){
6743                 this.el.removeClass("x-btn-over");
6744                 this.handler.call(this.scope || this, this, e);
6745             }
6746         }
6747     },
6748     // private
6749     onMouseOver : function(e){
6750         if(!this.disabled){
6751             this.el.addClass("x-btn-over");
6752             this.fireEvent('mouseover', this, e);
6753         }
6754     },
6755     // private
6756     onMouseOut : function(e){
6757         if(!e.within(this.el,  true)){
6758             this.el.removeClass("x-btn-over");
6759             this.fireEvent('mouseout', this, e);
6760         }
6761     },
6762     // private
6763     onFocus : function(e){
6764         if(!this.disabled){
6765             this.el.addClass("x-btn-focus");
6766         }
6767     },
6768     // private
6769     onBlur : function(e){
6770         this.el.removeClass("x-btn-focus");
6771     },
6772     // private
6773     onMouseDown : function(e){
6774         if(!this.disabled && e.button == 0){
6775             this.el.addClass("x-btn-click");
6776             Roo.get(document).on('mouseup', this.onMouseUp, this);
6777         }
6778     },
6779     // private
6780     onMouseUp : function(e){
6781         if(e.button == 0){
6782             this.el.removeClass("x-btn-click");
6783             Roo.get(document).un('mouseup', this.onMouseUp, this);
6784         }
6785     },
6786     // private
6787     onMenuShow : function(e){
6788         this.el.addClass("x-btn-menu-active");
6789     },
6790     // private
6791     onMenuHide : function(e){
6792         this.el.removeClass("x-btn-menu-active");
6793     }   
6794 });
6795
6796 // Private utility class used by Button
6797 Roo.ButtonToggleMgr = function(){
6798    var groups = {};
6799    
6800    function toggleGroup(btn, state){
6801        if(state){
6802            var g = groups[btn.toggleGroup];
6803            for(var i = 0, l = g.length; i < l; i++){
6804                if(g[i] != btn){
6805                    g[i].toggle(false);
6806                }
6807            }
6808        }
6809    }
6810    
6811    return {
6812        register : function(btn){
6813            if(!btn.toggleGroup){
6814                return;
6815            }
6816            var g = groups[btn.toggleGroup];
6817            if(!g){
6818                g = groups[btn.toggleGroup] = [];
6819            }
6820            g.push(btn);
6821            btn.on("toggle", toggleGroup);
6822        },
6823        
6824        unregister : function(btn){
6825            if(!btn.toggleGroup){
6826                return;
6827            }
6828            var g = groups[btn.toggleGroup];
6829            if(g){
6830                g.remove(btn);
6831                btn.un("toggle", toggleGroup);
6832            }
6833        }
6834    };
6835 }();/*
6836  * Based on:
6837  * Ext JS Library 1.1.1
6838  * Copyright(c) 2006-2007, Ext JS, LLC.
6839  *
6840  * Originally Released Under LGPL - original licence link has changed is not relivant.
6841  *
6842  * Fork - LGPL
6843  * <script type="text/javascript">
6844  */
6845  
6846 /**
6847  * @class Roo.SplitButton
6848  * @extends Roo.Button
6849  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6850  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6851  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6852  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6853  * @cfg {String} arrowTooltip The title attribute of the arrow
6854  * @constructor
6855  * Create a new menu button
6856  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6857  * @param {Object} config The config object
6858  */
6859 Roo.SplitButton = function(renderTo, config){
6860     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6861     /**
6862      * @event arrowclick
6863      * Fires when this button's arrow is clicked
6864      * @param {SplitButton} this
6865      * @param {EventObject} e The click event
6866      */
6867     this.addEvents({"arrowclick":true});
6868 };
6869
6870 Roo.extend(Roo.SplitButton, Roo.Button, {
6871     render : function(renderTo){
6872         // this is one sweet looking template!
6873         var tpl = new Roo.Template(
6874             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6875             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6876             '<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>',
6877             "</tbody></table></td><td>",
6878             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6879             '<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>',
6880             "</tbody></table></td></tr></table>"
6881         );
6882         var btn = tpl.append(renderTo, [this.text, this.type], true);
6883         var btnEl = btn.child("button");
6884         if(this.cls){
6885             btn.addClass(this.cls);
6886         }
6887         if(this.icon){
6888             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6889         }
6890         if(this.iconCls){
6891             btnEl.addClass(this.iconCls);
6892             if(!this.cls){
6893                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6894             }
6895         }
6896         this.el = btn;
6897         if(this.handleMouseEvents){
6898             btn.on("mouseover", this.onMouseOver, this);
6899             btn.on("mouseout", this.onMouseOut, this);
6900             btn.on("mousedown", this.onMouseDown, this);
6901             btn.on("mouseup", this.onMouseUp, this);
6902         }
6903         btn.on(this.clickEvent, this.onClick, this);
6904         if(this.tooltip){
6905             if(typeof this.tooltip == 'object'){
6906                 Roo.QuickTips.tips(Roo.apply({
6907                       target: btnEl.id
6908                 }, this.tooltip));
6909             } else {
6910                 btnEl.dom[this.tooltipType] = this.tooltip;
6911             }
6912         }
6913         if(this.arrowTooltip){
6914             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6915         }
6916         if(this.hidden){
6917             this.hide();
6918         }
6919         if(this.disabled){
6920             this.disable();
6921         }
6922         if(this.pressed){
6923             this.el.addClass("x-btn-pressed");
6924         }
6925         if(Roo.isIE && !Roo.isIE7){
6926             this.autoWidth.defer(1, this);
6927         }else{
6928             this.autoWidth();
6929         }
6930         if(this.menu){
6931             this.menu.on("show", this.onMenuShow, this);
6932             this.menu.on("hide", this.onMenuHide, this);
6933         }
6934         this.fireEvent('render', this);
6935     },
6936
6937     // private
6938     autoWidth : function(){
6939         if(this.el){
6940             var tbl = this.el.child("table:first");
6941             var tbl2 = this.el.child("table:last");
6942             this.el.setWidth("auto");
6943             tbl.setWidth("auto");
6944             if(Roo.isIE7 && Roo.isStrict){
6945                 var ib = this.el.child('button:first');
6946                 if(ib && ib.getWidth() > 20){
6947                     ib.clip();
6948                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6949                 }
6950             }
6951             if(this.minWidth){
6952                 if(this.hidden){
6953                     this.el.beginMeasure();
6954                 }
6955                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6956                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6957                 }
6958                 if(this.hidden){
6959                     this.el.endMeasure();
6960                 }
6961             }
6962             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6963         } 
6964     },
6965     /**
6966      * Sets this button's click handler
6967      * @param {Function} handler The function to call when the button is clicked
6968      * @param {Object} scope (optional) Scope for the function passed above
6969      */
6970     setHandler : function(handler, scope){
6971         this.handler = handler;
6972         this.scope = scope;  
6973     },
6974     
6975     /**
6976      * Sets this button's arrow click handler
6977      * @param {Function} handler The function to call when the arrow is clicked
6978      * @param {Object} scope (optional) Scope for the function passed above
6979      */
6980     setArrowHandler : function(handler, scope){
6981         this.arrowHandler = handler;
6982         this.scope = scope;  
6983     },
6984     
6985     /**
6986      * Focus the button
6987      */
6988     focus : function(){
6989         if(this.el){
6990             this.el.child("button:first").focus();
6991         }
6992     },
6993
6994     // private
6995     onClick : function(e){
6996         e.preventDefault();
6997         if(!this.disabled){
6998             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6999                 if(this.menu && !this.menu.isVisible()){
7000                     this.menu.show(this.el, this.menuAlign);
7001                 }
7002                 this.fireEvent("arrowclick", this, e);
7003                 if(this.arrowHandler){
7004                     this.arrowHandler.call(this.scope || this, this, e);
7005                 }
7006             }else{
7007                 this.fireEvent("click", this, e);
7008                 if(this.handler){
7009                     this.handler.call(this.scope || this, this, e);
7010                 }
7011             }
7012         }
7013     },
7014     // private
7015     onMouseDown : function(e){
7016         if(!this.disabled){
7017             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7018         }
7019     },
7020     // private
7021     onMouseUp : function(e){
7022         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7023     }   
7024 });
7025
7026
7027 // backwards compat
7028 Roo.MenuButton = Roo.SplitButton;/*
7029  * Based on:
7030  * Ext JS Library 1.1.1
7031  * Copyright(c) 2006-2007, Ext JS, LLC.
7032  *
7033  * Originally Released Under LGPL - original licence link has changed is not relivant.
7034  *
7035  * Fork - LGPL
7036  * <script type="text/javascript">
7037  */
7038
7039 /**
7040  * @class Roo.Toolbar
7041  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7042  * Basic Toolbar class.
7043  * @constructor
7044  * Creates a new Toolbar
7045  * @param {Object} container The config object
7046  */ 
7047 Roo.Toolbar = function(container, buttons, config)
7048 {
7049     /// old consturctor format still supported..
7050     if(container instanceof Array){ // omit the container for later rendering
7051         buttons = container;
7052         config = buttons;
7053         container = null;
7054     }
7055     if (typeof(container) == 'object' && container.xtype) {
7056         config = container;
7057         container = config.container;
7058         buttons = config.buttons || []; // not really - use items!!
7059     }
7060     var xitems = [];
7061     if (config && config.items) {
7062         xitems = config.items;
7063         delete config.items;
7064     }
7065     Roo.apply(this, config);
7066     this.buttons = buttons;
7067     
7068     if(container){
7069         this.render(container);
7070     }
7071     this.xitems = xitems;
7072     Roo.each(xitems, function(b) {
7073         this.add(b);
7074     }, this);
7075     
7076 };
7077
7078 Roo.Toolbar.prototype = {
7079     /**
7080      * @cfg {Array} items
7081      * array of button configs or elements to add (will be converted to a MixedCollection)
7082      */
7083     items: false,
7084     /**
7085      * @cfg {String/HTMLElement/Element} container
7086      * The id or element that will contain the toolbar
7087      */
7088     // private
7089     render : function(ct){
7090         this.el = Roo.get(ct);
7091         if(this.cls){
7092             this.el.addClass(this.cls);
7093         }
7094         // using a table allows for vertical alignment
7095         // 100% width is needed by Safari...
7096         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7097         this.tr = this.el.child("tr", true);
7098         var autoId = 0;
7099         this.items = new Roo.util.MixedCollection(false, function(o){
7100             return o.id || ("item" + (++autoId));
7101         });
7102         if(this.buttons){
7103             this.add.apply(this, this.buttons);
7104             delete this.buttons;
7105         }
7106     },
7107
7108     /**
7109      * Adds element(s) to the toolbar -- this function takes a variable number of 
7110      * arguments of mixed type and adds them to the toolbar.
7111      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7112      * <ul>
7113      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7114      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7115      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7116      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7117      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7118      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7119      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7120      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7121      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7122      * </ul>
7123      * @param {Mixed} arg2
7124      * @param {Mixed} etc.
7125      */
7126     add : function(){
7127         var a = arguments, l = a.length;
7128         for(var i = 0; i < l; i++){
7129             this._add(a[i]);
7130         }
7131     },
7132     // private..
7133     _add : function(el) {
7134         
7135         if (el.xtype) {
7136             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7137         }
7138         
7139         if (el.applyTo){ // some kind of form field
7140             return this.addField(el);
7141         } 
7142         if (el.render){ // some kind of Toolbar.Item
7143             return this.addItem(el);
7144         }
7145         if (typeof el == "string"){ // string
7146             if(el == "separator" || el == "-"){
7147                 return this.addSeparator();
7148             }
7149             if (el == " "){
7150                 return this.addSpacer();
7151             }
7152             if(el == "->"){
7153                 return this.addFill();
7154             }
7155             return this.addText(el);
7156             
7157         }
7158         if(el.tagName){ // element
7159             return this.addElement(el);
7160         }
7161         if(typeof el == "object"){ // must be button config?
7162             return this.addButton(el);
7163         }
7164         // and now what?!?!
7165         return false;
7166         
7167     },
7168     
7169     /**
7170      * Add an Xtype element
7171      * @param {Object} xtype Xtype Object
7172      * @return {Object} created Object
7173      */
7174     addxtype : function(e){
7175         return this.add(e);  
7176     },
7177     
7178     /**
7179      * Returns the Element for this toolbar.
7180      * @return {Roo.Element}
7181      */
7182     getEl : function(){
7183         return this.el;  
7184     },
7185     
7186     /**
7187      * Adds a separator
7188      * @return {Roo.Toolbar.Item} The separator item
7189      */
7190     addSeparator : function(){
7191         return this.addItem(new Roo.Toolbar.Separator());
7192     },
7193
7194     /**
7195      * Adds a spacer element
7196      * @return {Roo.Toolbar.Spacer} The spacer item
7197      */
7198     addSpacer : function(){
7199         return this.addItem(new Roo.Toolbar.Spacer());
7200     },
7201
7202     /**
7203      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7204      * @return {Roo.Toolbar.Fill} The fill item
7205      */
7206     addFill : function(){
7207         return this.addItem(new Roo.Toolbar.Fill());
7208     },
7209
7210     /**
7211      * Adds any standard HTML element to the toolbar
7212      * @param {String/HTMLElement/Element} el The element or id of the element to add
7213      * @return {Roo.Toolbar.Item} The element's item
7214      */
7215     addElement : function(el){
7216         return this.addItem(new Roo.Toolbar.Item(el));
7217     },
7218     /**
7219      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7220      * @type Roo.util.MixedCollection  
7221      */
7222     items : false,
7223      
7224     /**
7225      * Adds any Toolbar.Item or subclass
7226      * @param {Roo.Toolbar.Item} item
7227      * @return {Roo.Toolbar.Item} The item
7228      */
7229     addItem : function(item){
7230         var td = this.nextBlock();
7231         item.render(td);
7232         this.items.add(item);
7233         return item;
7234     },
7235     
7236     /**
7237      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7238      * @param {Object/Array} config A button config or array of configs
7239      * @return {Roo.Toolbar.Button/Array}
7240      */
7241     addButton : function(config){
7242         if(config instanceof Array){
7243             var buttons = [];
7244             for(var i = 0, len = config.length; i < len; i++) {
7245                 buttons.push(this.addButton(config[i]));
7246             }
7247             return buttons;
7248         }
7249         var b = config;
7250         if(!(config instanceof Roo.Toolbar.Button)){
7251             b = config.split ?
7252                 new Roo.Toolbar.SplitButton(config) :
7253                 new Roo.Toolbar.Button(config);
7254         }
7255         var td = this.nextBlock();
7256         b.render(td);
7257         this.items.add(b);
7258         return b;
7259     },
7260     
7261     /**
7262      * Adds text to the toolbar
7263      * @param {String} text The text to add
7264      * @return {Roo.Toolbar.Item} The element's item
7265      */
7266     addText : function(text){
7267         return this.addItem(new Roo.Toolbar.TextItem(text));
7268     },
7269     
7270     /**
7271      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7272      * @param {Number} index The index where the item is to be inserted
7273      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7274      * @return {Roo.Toolbar.Button/Item}
7275      */
7276     insertButton : function(index, item){
7277         if(item instanceof Array){
7278             var buttons = [];
7279             for(var i = 0, len = item.length; i < len; i++) {
7280                buttons.push(this.insertButton(index + i, item[i]));
7281             }
7282             return buttons;
7283         }
7284         if (!(item instanceof Roo.Toolbar.Button)){
7285            item = new Roo.Toolbar.Button(item);
7286         }
7287         var td = document.createElement("td");
7288         this.tr.insertBefore(td, this.tr.childNodes[index]);
7289         item.render(td);
7290         this.items.insert(index, item);
7291         return item;
7292     },
7293     
7294     /**
7295      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7296      * @param {Object} config
7297      * @return {Roo.Toolbar.Item} The element's item
7298      */
7299     addDom : function(config, returnEl){
7300         var td = this.nextBlock();
7301         Roo.DomHelper.overwrite(td, config);
7302         var ti = new Roo.Toolbar.Item(td.firstChild);
7303         ti.render(td);
7304         this.items.add(ti);
7305         return ti;
7306     },
7307
7308     /**
7309      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7310      * @type Roo.util.MixedCollection  
7311      */
7312     fields : false,
7313     
7314     /**
7315      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7316      * Note: the field should not have been rendered yet. For a field that has already been
7317      * rendered, use {@link #addElement}.
7318      * @param {Roo.form.Field} field
7319      * @return {Roo.ToolbarItem}
7320      */
7321      
7322       
7323     addField : function(field) {
7324         if (!this.fields) {
7325             var autoId = 0;
7326             this.fields = new Roo.util.MixedCollection(false, function(o){
7327                 return o.id || ("item" + (++autoId));
7328             });
7329
7330         }
7331         
7332         var td = this.nextBlock();
7333         field.render(td);
7334         var ti = new Roo.Toolbar.Item(td.firstChild);
7335         ti.render(td);
7336         this.items.add(ti);
7337         this.fields.add(field);
7338         return ti;
7339     },
7340     /**
7341      * Hide the toolbar
7342      * @method hide
7343      */
7344      
7345       
7346     hide : function()
7347     {
7348         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7349         this.el.child('div').hide();
7350     },
7351     /**
7352      * Show the toolbar
7353      * @method show
7354      */
7355     show : function()
7356     {
7357         this.el.child('div').show();
7358     },
7359       
7360     // private
7361     nextBlock : function(){
7362         var td = document.createElement("td");
7363         this.tr.appendChild(td);
7364         return td;
7365     },
7366
7367     // private
7368     destroy : function(){
7369         if(this.items){ // rendered?
7370             Roo.destroy.apply(Roo, this.items.items);
7371         }
7372         if(this.fields){ // rendered?
7373             Roo.destroy.apply(Roo, this.fields.items);
7374         }
7375         Roo.Element.uncache(this.el, this.tr);
7376     }
7377 };
7378
7379 /**
7380  * @class Roo.Toolbar.Item
7381  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7382  * @constructor
7383  * Creates a new Item
7384  * @param {HTMLElement} el 
7385  */
7386 Roo.Toolbar.Item = function(el){
7387     var cfg = {};
7388     if (typeof (el.xtype) != 'undefined') {
7389         cfg = el;
7390         el = cfg.el;
7391     }
7392     
7393     this.el = Roo.getDom(el);
7394     this.id = Roo.id(this.el);
7395     this.hidden = false;
7396     
7397     this.addEvents({
7398          /**
7399              * @event render
7400              * Fires when the button is rendered
7401              * @param {Button} this
7402              */
7403         'render': true
7404     });
7405     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7406 };
7407 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7408 //Roo.Toolbar.Item.prototype = {
7409     
7410     /**
7411      * Get this item's HTML Element
7412      * @return {HTMLElement}
7413      */
7414     getEl : function(){
7415        return this.el;  
7416     },
7417
7418     // private
7419     render : function(td){
7420         
7421          this.td = td;
7422         td.appendChild(this.el);
7423         
7424         this.fireEvent('render', this);
7425     },
7426     
7427     /**
7428      * Removes and destroys this item.
7429      */
7430     destroy : function(){
7431         this.td.parentNode.removeChild(this.td);
7432     },
7433     
7434     /**
7435      * Shows this item.
7436      */
7437     show: function(){
7438         this.hidden = false;
7439         this.td.style.display = "";
7440     },
7441     
7442     /**
7443      * Hides this item.
7444      */
7445     hide: function(){
7446         this.hidden = true;
7447         this.td.style.display = "none";
7448     },
7449     
7450     /**
7451      * Convenience function for boolean show/hide.
7452      * @param {Boolean} visible true to show/false to hide
7453      */
7454     setVisible: function(visible){
7455         if(visible) {
7456             this.show();
7457         }else{
7458             this.hide();
7459         }
7460     },
7461     
7462     /**
7463      * Try to focus this item.
7464      */
7465     focus : function(){
7466         Roo.fly(this.el).focus();
7467     },
7468     
7469     /**
7470      * Disables this item.
7471      */
7472     disable : function(){
7473         Roo.fly(this.td).addClass("x-item-disabled");
7474         this.disabled = true;
7475         this.el.disabled = true;
7476     },
7477     
7478     /**
7479      * Enables this item.
7480      */
7481     enable : function(){
7482         Roo.fly(this.td).removeClass("x-item-disabled");
7483         this.disabled = false;
7484         this.el.disabled = false;
7485     }
7486 });
7487
7488
7489 /**
7490  * @class Roo.Toolbar.Separator
7491  * @extends Roo.Toolbar.Item
7492  * A simple toolbar separator class
7493  * @constructor
7494  * Creates a new Separator
7495  */
7496 Roo.Toolbar.Separator = function(cfg){
7497     
7498     var s = document.createElement("span");
7499     s.className = "ytb-sep";
7500     if (cfg) {
7501         cfg.el = s;
7502     }
7503     
7504     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7505 };
7506 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7507     enable:Roo.emptyFn,
7508     disable:Roo.emptyFn,
7509     focus:Roo.emptyFn
7510 });
7511
7512 /**
7513  * @class Roo.Toolbar.Spacer
7514  * @extends Roo.Toolbar.Item
7515  * A simple element that adds extra horizontal space to a toolbar.
7516  * @constructor
7517  * Creates a new Spacer
7518  */
7519 Roo.Toolbar.Spacer = function(cfg){
7520     var s = document.createElement("div");
7521     s.className = "ytb-spacer";
7522     if (cfg) {
7523         cfg.el = s;
7524     }
7525     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7526 };
7527 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7528     enable:Roo.emptyFn,
7529     disable:Roo.emptyFn,
7530     focus:Roo.emptyFn
7531 });
7532
7533 /**
7534  * @class Roo.Toolbar.Fill
7535  * @extends Roo.Toolbar.Spacer
7536  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7537  * @constructor
7538  * Creates a new Spacer
7539  */
7540 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7541     // private
7542     render : function(td){
7543         td.style.width = '100%';
7544         Roo.Toolbar.Fill.superclass.render.call(this, td);
7545     }
7546 });
7547
7548 /**
7549  * @class Roo.Toolbar.TextItem
7550  * @extends Roo.Toolbar.Item
7551  * A simple class that renders text directly into a toolbar.
7552  * @constructor
7553  * Creates a new TextItem
7554  * @cfg {string} text 
7555  */
7556 Roo.Toolbar.TextItem = function(cfg){
7557     var  text = cfg || "";
7558     if (typeof(cfg) == 'object') {
7559         text = cfg.text || "";
7560     }  else {
7561         cfg = null;
7562     }
7563     var s = document.createElement("span");
7564     s.className = "ytb-text";
7565     s.innerHTML = text;
7566     if (cfg) {
7567         cfg.el  = s;
7568     }
7569     
7570     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7571 };
7572 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7573     
7574      
7575     enable:Roo.emptyFn,
7576     disable:Roo.emptyFn,
7577     focus:Roo.emptyFn,
7578      /**
7579      * Shows this button
7580      */
7581     show: function(){
7582         this.hidden = false;
7583         this.el.style.display = "";
7584     },
7585     
7586     /**
7587      * Hides this button
7588      */
7589     hide: function(){
7590         this.hidden = true;
7591         this.el.style.display = "none";
7592     }
7593     
7594 });
7595
7596 /**
7597  * @class Roo.Toolbar.Button
7598  * @extends Roo.Button
7599  * A button that renders into a toolbar.
7600  * @constructor
7601  * Creates a new Button
7602  * @param {Object} config A standard {@link Roo.Button} config object
7603  */
7604 Roo.Toolbar.Button = function(config){
7605     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7606 };
7607 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7608 {
7609     
7610     
7611     render : function(td){
7612         this.td = td;
7613         Roo.Toolbar.Button.superclass.render.call(this, td);
7614     },
7615     
7616     /**
7617      * Removes and destroys this button
7618      */
7619     destroy : function(){
7620         Roo.Toolbar.Button.superclass.destroy.call(this);
7621         this.td.parentNode.removeChild(this.td);
7622     },
7623     
7624     /**
7625      * Shows this button
7626      */
7627     show: function(){
7628         this.hidden = false;
7629         this.td.style.display = "";
7630     },
7631     
7632     /**
7633      * Hides this button
7634      */
7635     hide: function(){
7636         this.hidden = true;
7637         this.td.style.display = "none";
7638     },
7639
7640     /**
7641      * Disables this item
7642      */
7643     disable : function(){
7644         Roo.fly(this.td).addClass("x-item-disabled");
7645         this.disabled = true;
7646     },
7647
7648     /**
7649      * Enables this item
7650      */
7651     enable : function(){
7652         Roo.fly(this.td).removeClass("x-item-disabled");
7653         this.disabled = false;
7654     }
7655 });
7656 // backwards compat
7657 Roo.ToolbarButton = Roo.Toolbar.Button;
7658
7659 /**
7660  * @class Roo.Toolbar.SplitButton
7661  * @extends Roo.SplitButton
7662  * A menu button that renders into a toolbar.
7663  * @constructor
7664  * Creates a new SplitButton
7665  * @param {Object} config A standard {@link Roo.SplitButton} config object
7666  */
7667 Roo.Toolbar.SplitButton = function(config){
7668     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7669 };
7670 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7671     render : function(td){
7672         this.td = td;
7673         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7674     },
7675     
7676     /**
7677      * Removes and destroys this button
7678      */
7679     destroy : function(){
7680         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7681         this.td.parentNode.removeChild(this.td);
7682     },
7683     
7684     /**
7685      * Shows this button
7686      */
7687     show: function(){
7688         this.hidden = false;
7689         this.td.style.display = "";
7690     },
7691     
7692     /**
7693      * Hides this button
7694      */
7695     hide: function(){
7696         this.hidden = true;
7697         this.td.style.display = "none";
7698     }
7699 });
7700
7701 // backwards compat
7702 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7703  * Based on:
7704  * Ext JS Library 1.1.1
7705  * Copyright(c) 2006-2007, Ext JS, LLC.
7706  *
7707  * Originally Released Under LGPL - original licence link has changed is not relivant.
7708  *
7709  * Fork - LGPL
7710  * <script type="text/javascript">
7711  */
7712  
7713 /**
7714  * @class Roo.PagingToolbar
7715  * @extends Roo.Toolbar
7716  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7717  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7718  * @constructor
7719  * Create a new PagingToolbar
7720  * @param {Object} config The config object
7721  */
7722 Roo.PagingToolbar = function(el, ds, config)
7723 {
7724     // old args format still supported... - xtype is prefered..
7725     if (typeof(el) == 'object' && el.xtype) {
7726         // created from xtype...
7727         config = el;
7728         ds = el.dataSource;
7729         el = config.container;
7730     }
7731     var items = [];
7732     if (config.items) {
7733         items = config.items;
7734         config.items = [];
7735     }
7736     
7737     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7738     this.ds = ds;
7739     this.cursor = 0;
7740     this.renderButtons(this.el);
7741     this.bind(ds);
7742     
7743     // supprot items array.
7744    
7745     Roo.each(items, function(e) {
7746         this.add(Roo.factory(e));
7747     },this);
7748     
7749 };
7750
7751 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7752    
7753     /**
7754      * @cfg {String/HTMLElement/Element} container
7755      * container The id or element that will contain the toolbar
7756      */
7757     /**
7758      * @cfg {Boolean} displayInfo
7759      * True to display the displayMsg (defaults to false)
7760      */
7761     
7762     
7763     /**
7764      * @cfg {Number} pageSize
7765      * The number of records to display per page (defaults to 20)
7766      */
7767     pageSize: 20,
7768     /**
7769      * @cfg {String} displayMsg
7770      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7771      */
7772     displayMsg : 'Displaying {0} - {1} of {2}',
7773     /**
7774      * @cfg {String} emptyMsg
7775      * The message to display when no records are found (defaults to "No data to display")
7776      */
7777     emptyMsg : 'No data to display',
7778     /**
7779      * Customizable piece of the default paging text (defaults to "Page")
7780      * @type String
7781      */
7782     beforePageText : "Page",
7783     /**
7784      * Customizable piece of the default paging text (defaults to "of %0")
7785      * @type String
7786      */
7787     afterPageText : "of {0}",
7788     /**
7789      * Customizable piece of the default paging text (defaults to "First Page")
7790      * @type String
7791      */
7792     firstText : "First Page",
7793     /**
7794      * Customizable piece of the default paging text (defaults to "Previous Page")
7795      * @type String
7796      */
7797     prevText : "Previous Page",
7798     /**
7799      * Customizable piece of the default paging text (defaults to "Next Page")
7800      * @type String
7801      */
7802     nextText : "Next Page",
7803     /**
7804      * Customizable piece of the default paging text (defaults to "Last Page")
7805      * @type String
7806      */
7807     lastText : "Last Page",
7808     /**
7809      * Customizable piece of the default paging text (defaults to "Refresh")
7810      * @type String
7811      */
7812     refreshText : "Refresh",
7813
7814     // private
7815     renderButtons : function(el){
7816         Roo.PagingToolbar.superclass.render.call(this, el);
7817         this.first = this.addButton({
7818             tooltip: this.firstText,
7819             cls: "x-btn-icon x-grid-page-first",
7820             disabled: true,
7821             handler: this.onClick.createDelegate(this, ["first"])
7822         });
7823         this.prev = this.addButton({
7824             tooltip: this.prevText,
7825             cls: "x-btn-icon x-grid-page-prev",
7826             disabled: true,
7827             handler: this.onClick.createDelegate(this, ["prev"])
7828         });
7829         //this.addSeparator();
7830         this.add(this.beforePageText);
7831         this.field = Roo.get(this.addDom({
7832            tag: "input",
7833            type: "text",
7834            size: "3",
7835            value: "1",
7836            cls: "x-grid-page-number"
7837         }).el);
7838         this.field.on("keydown", this.onPagingKeydown, this);
7839         this.field.on("focus", function(){this.dom.select();});
7840         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7841         this.field.setHeight(18);
7842         //this.addSeparator();
7843         this.next = this.addButton({
7844             tooltip: this.nextText,
7845             cls: "x-btn-icon x-grid-page-next",
7846             disabled: true,
7847             handler: this.onClick.createDelegate(this, ["next"])
7848         });
7849         this.last = this.addButton({
7850             tooltip: this.lastText,
7851             cls: "x-btn-icon x-grid-page-last",
7852             disabled: true,
7853             handler: this.onClick.createDelegate(this, ["last"])
7854         });
7855         //this.addSeparator();
7856         this.loading = this.addButton({
7857             tooltip: this.refreshText,
7858             cls: "x-btn-icon x-grid-loading",
7859             handler: this.onClick.createDelegate(this, ["refresh"])
7860         });
7861
7862         if(this.displayInfo){
7863             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7864         }
7865     },
7866
7867     // private
7868     updateInfo : function(){
7869         if(this.displayEl){
7870             var count = this.ds.getCount();
7871             var msg = count == 0 ?
7872                 this.emptyMsg :
7873                 String.format(
7874                     this.displayMsg,
7875                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7876                 );
7877             this.displayEl.update(msg);
7878         }
7879     },
7880
7881     // private
7882     onLoad : function(ds, r, o){
7883        this.cursor = o.params ? o.params.start : 0;
7884        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7885
7886        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7887        this.field.dom.value = ap;
7888        this.first.setDisabled(ap == 1);
7889        this.prev.setDisabled(ap == 1);
7890        this.next.setDisabled(ap == ps);
7891        this.last.setDisabled(ap == ps);
7892        this.loading.enable();
7893        this.updateInfo();
7894     },
7895
7896     // private
7897     getPageData : function(){
7898         var total = this.ds.getTotalCount();
7899         return {
7900             total : total,
7901             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7902             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7903         };
7904     },
7905
7906     // private
7907     onLoadError : function(){
7908         this.loading.enable();
7909     },
7910
7911     // private
7912     onPagingKeydown : function(e){
7913         var k = e.getKey();
7914         var d = this.getPageData();
7915         if(k == e.RETURN){
7916             var v = this.field.dom.value, pageNum;
7917             if(!v || isNaN(pageNum = parseInt(v, 10))){
7918                 this.field.dom.value = d.activePage;
7919                 return;
7920             }
7921             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7922             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7923             e.stopEvent();
7924         }
7925         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))
7926         {
7927           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7928           this.field.dom.value = pageNum;
7929           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7930           e.stopEvent();
7931         }
7932         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7933         {
7934           var v = this.field.dom.value, pageNum; 
7935           var increment = (e.shiftKey) ? 10 : 1;
7936           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7937             increment *= -1;
7938           }
7939           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7940             this.field.dom.value = d.activePage;
7941             return;
7942           }
7943           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7944           {
7945             this.field.dom.value = parseInt(v, 10) + increment;
7946             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7947             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7948           }
7949           e.stopEvent();
7950         }
7951     },
7952
7953     // private
7954     beforeLoad : function(){
7955         if(this.loading){
7956             this.loading.disable();
7957         }
7958     },
7959     /**
7960      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
7961      * @param {String} which (first|prev|next|last|refresh)  which button to press.
7962      *
7963      */
7964     // private
7965     onClick : function(which){
7966         var ds = this.ds;
7967         switch(which){
7968             case "first":
7969                 ds.load({params:{start: 0, limit: this.pageSize}});
7970             break;
7971             case "prev":
7972                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7973             break;
7974             case "next":
7975                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7976             break;
7977             case "last":
7978                 var total = ds.getTotalCount();
7979                 var extra = total % this.pageSize;
7980                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7981                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7982             break;
7983             case "refresh":
7984                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7985             break;
7986         }
7987     },
7988
7989     /**
7990      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7991      * @param {Roo.data.Store} store The data store to unbind
7992      */
7993     unbind : function(ds){
7994         ds.un("beforeload", this.beforeLoad, this);
7995         ds.un("load", this.onLoad, this);
7996         ds.un("loadexception", this.onLoadError, this);
7997         ds.un("remove", this.updateInfo, this);
7998         ds.un("add", this.updateInfo, this);
7999         this.ds = undefined;
8000     },
8001
8002     /**
8003      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8004      * @param {Roo.data.Store} store The data store to bind
8005      */
8006     bind : function(ds){
8007         ds.on("beforeload", this.beforeLoad, this);
8008         ds.on("load", this.onLoad, this);
8009         ds.on("loadexception", this.onLoadError, this);
8010         ds.on("remove", this.updateInfo, this);
8011         ds.on("add", this.updateInfo, this);
8012         this.ds = ds;
8013     }
8014 });/*
8015  * Based on:
8016  * Ext JS Library 1.1.1
8017  * Copyright(c) 2006-2007, Ext JS, LLC.
8018  *
8019  * Originally Released Under LGPL - original licence link has changed is not relivant.
8020  *
8021  * Fork - LGPL
8022  * <script type="text/javascript">
8023  */
8024
8025 /**
8026  * @class Roo.Resizable
8027  * @extends Roo.util.Observable
8028  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8029  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8030  * 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
8031  * the element will be wrapped for you automatically.</p>
8032  * <p>Here is the list of valid resize handles:</p>
8033  * <pre>
8034 Value   Description
8035 ------  -------------------
8036  'n'     north
8037  's'     south
8038  'e'     east
8039  'w'     west
8040  'nw'    northwest
8041  'sw'    southwest
8042  'se'    southeast
8043  'ne'    northeast
8044  'hd'    horizontal drag
8045  'all'   all
8046 </pre>
8047  * <p>Here's an example showing the creation of a typical Resizable:</p>
8048  * <pre><code>
8049 var resizer = new Roo.Resizable("element-id", {
8050     handles: 'all',
8051     minWidth: 200,
8052     minHeight: 100,
8053     maxWidth: 500,
8054     maxHeight: 400,
8055     pinned: true
8056 });
8057 resizer.on("resize", myHandler);
8058 </code></pre>
8059  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8060  * resizer.east.setDisplayed(false);</p>
8061  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8062  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8063  * resize operation's new size (defaults to [0, 0])
8064  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8065  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8066  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8067  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8068  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8069  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8070  * @cfg {Number} width The width of the element in pixels (defaults to null)
8071  * @cfg {Number} height The height of the element in pixels (defaults to null)
8072  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8073  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8074  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8075  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8076  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8077  * in favor of the handles config option (defaults to false)
8078  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8079  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8080  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8081  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8082  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8083  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8084  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8085  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8086  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8087  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8088  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8089  * @constructor
8090  * Create a new resizable component
8091  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8092  * @param {Object} config configuration options
8093   */
8094 Roo.Resizable = function(el, config)
8095 {
8096     this.el = Roo.get(el);
8097
8098     if(config && config.wrap){
8099         config.resizeChild = this.el;
8100         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8101         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8102         this.el.setStyle("overflow", "hidden");
8103         this.el.setPositioning(config.resizeChild.getPositioning());
8104         config.resizeChild.clearPositioning();
8105         if(!config.width || !config.height){
8106             var csize = config.resizeChild.getSize();
8107             this.el.setSize(csize.width, csize.height);
8108         }
8109         if(config.pinned && !config.adjustments){
8110             config.adjustments = "auto";
8111         }
8112     }
8113
8114     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8115     this.proxy.unselectable();
8116     this.proxy.enableDisplayMode('block');
8117
8118     Roo.apply(this, config);
8119
8120     if(this.pinned){
8121         this.disableTrackOver = true;
8122         this.el.addClass("x-resizable-pinned");
8123     }
8124     // if the element isn't positioned, make it relative
8125     var position = this.el.getStyle("position");
8126     if(position != "absolute" && position != "fixed"){
8127         this.el.setStyle("position", "relative");
8128     }
8129     if(!this.handles){ // no handles passed, must be legacy style
8130         this.handles = 's,e,se';
8131         if(this.multiDirectional){
8132             this.handles += ',n,w';
8133         }
8134     }
8135     if(this.handles == "all"){
8136         this.handles = "n s e w ne nw se sw";
8137     }
8138     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8139     var ps = Roo.Resizable.positions;
8140     for(var i = 0, len = hs.length; i < len; i++){
8141         if(hs[i] && ps[hs[i]]){
8142             var pos = ps[hs[i]];
8143             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8144         }
8145     }
8146     // legacy
8147     this.corner = this.southeast;
8148     
8149     // updateBox = the box can move..
8150     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8151         this.updateBox = true;
8152     }
8153
8154     this.activeHandle = null;
8155
8156     if(this.resizeChild){
8157         if(typeof this.resizeChild == "boolean"){
8158             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8159         }else{
8160             this.resizeChild = Roo.get(this.resizeChild, true);
8161         }
8162     }
8163     
8164     if(this.adjustments == "auto"){
8165         var rc = this.resizeChild;
8166         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8167         if(rc && (hw || hn)){
8168             rc.position("relative");
8169             rc.setLeft(hw ? hw.el.getWidth() : 0);
8170             rc.setTop(hn ? hn.el.getHeight() : 0);
8171         }
8172         this.adjustments = [
8173             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8174             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8175         ];
8176     }
8177
8178     if(this.draggable){
8179         this.dd = this.dynamic ?
8180             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8181         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8182     }
8183
8184     // public events
8185     this.addEvents({
8186         /**
8187          * @event beforeresize
8188          * Fired before resize is allowed. Set enabled to false to cancel resize.
8189          * @param {Roo.Resizable} this
8190          * @param {Roo.EventObject} e The mousedown event
8191          */
8192         "beforeresize" : true,
8193         /**
8194          * @event resizing
8195          * Fired a resizing.
8196          * @param {Roo.Resizable} this
8197          * @param {Number} x The new x position
8198          * @param {Number} y The new y position
8199          * @param {Number} w The new w width
8200          * @param {Number} h The new h hight
8201          * @param {Roo.EventObject} e The mouseup event
8202          */
8203         "resizing" : true,
8204         /**
8205          * @event resize
8206          * Fired after a resize.
8207          * @param {Roo.Resizable} this
8208          * @param {Number} width The new width
8209          * @param {Number} height The new height
8210          * @param {Roo.EventObject} e The mouseup event
8211          */
8212         "resize" : true
8213     });
8214
8215     if(this.width !== null && this.height !== null){
8216         this.resizeTo(this.width, this.height);
8217     }else{
8218         this.updateChildSize();
8219     }
8220     if(Roo.isIE){
8221         this.el.dom.style.zoom = 1;
8222     }
8223     Roo.Resizable.superclass.constructor.call(this);
8224 };
8225
8226 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8227         resizeChild : false,
8228         adjustments : [0, 0],
8229         minWidth : 5,
8230         minHeight : 5,
8231         maxWidth : 10000,
8232         maxHeight : 10000,
8233         enabled : true,
8234         animate : false,
8235         duration : .35,
8236         dynamic : false,
8237         handles : false,
8238         multiDirectional : false,
8239         disableTrackOver : false,
8240         easing : 'easeOutStrong',
8241         widthIncrement : 0,
8242         heightIncrement : 0,
8243         pinned : false,
8244         width : null,
8245         height : null,
8246         preserveRatio : false,
8247         transparent: false,
8248         minX: 0,
8249         minY: 0,
8250         draggable: false,
8251
8252         /**
8253          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8254          */
8255         constrainTo: undefined,
8256         /**
8257          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8258          */
8259         resizeRegion: undefined,
8260
8261
8262     /**
8263      * Perform a manual resize
8264      * @param {Number} width
8265      * @param {Number} height
8266      */
8267     resizeTo : function(width, height){
8268         this.el.setSize(width, height);
8269         this.updateChildSize();
8270         this.fireEvent("resize", this, width, height, null);
8271     },
8272
8273     // private
8274     startSizing : function(e, handle){
8275         this.fireEvent("beforeresize", this, e);
8276         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8277
8278             if(!this.overlay){
8279                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8280                 this.overlay.unselectable();
8281                 this.overlay.enableDisplayMode("block");
8282                 this.overlay.on("mousemove", this.onMouseMove, this);
8283                 this.overlay.on("mouseup", this.onMouseUp, this);
8284             }
8285             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8286
8287             this.resizing = true;
8288             this.startBox = this.el.getBox();
8289             this.startPoint = e.getXY();
8290             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8291                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8292
8293             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8294             this.overlay.show();
8295
8296             if(this.constrainTo) {
8297                 var ct = Roo.get(this.constrainTo);
8298                 this.resizeRegion = ct.getRegion().adjust(
8299                     ct.getFrameWidth('t'),
8300                     ct.getFrameWidth('l'),
8301                     -ct.getFrameWidth('b'),
8302                     -ct.getFrameWidth('r')
8303                 );
8304             }
8305
8306             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8307             this.proxy.show();
8308             this.proxy.setBox(this.startBox);
8309             if(!this.dynamic){
8310                 this.proxy.setStyle('visibility', 'visible');
8311             }
8312         }
8313     },
8314
8315     // private
8316     onMouseDown : function(handle, e){
8317         if(this.enabled){
8318             e.stopEvent();
8319             this.activeHandle = handle;
8320             this.startSizing(e, handle);
8321         }
8322     },
8323
8324     // private
8325     onMouseUp : function(e){
8326         var size = this.resizeElement();
8327         this.resizing = false;
8328         this.handleOut();
8329         this.overlay.hide();
8330         this.proxy.hide();
8331         this.fireEvent("resize", this, size.width, size.height, e);
8332     },
8333
8334     // private
8335     updateChildSize : function(){
8336         
8337         if(this.resizeChild){
8338             var el = this.el;
8339             var child = this.resizeChild;
8340             var adj = this.adjustments;
8341             if(el.dom.offsetWidth){
8342                 var b = el.getSize(true);
8343                 child.setSize(b.width+adj[0], b.height+adj[1]);
8344             }
8345             // Second call here for IE
8346             // The first call enables instant resizing and
8347             // the second call corrects scroll bars if they
8348             // exist
8349             if(Roo.isIE){
8350                 setTimeout(function(){
8351                     if(el.dom.offsetWidth){
8352                         var b = el.getSize(true);
8353                         child.setSize(b.width+adj[0], b.height+adj[1]);
8354                     }
8355                 }, 10);
8356             }
8357         }
8358     },
8359
8360     // private
8361     snap : function(value, inc, min){
8362         if(!inc || !value) {
8363             return value;
8364         }
8365         var newValue = value;
8366         var m = value % inc;
8367         if(m > 0){
8368             if(m > (inc/2)){
8369                 newValue = value + (inc-m);
8370             }else{
8371                 newValue = value - m;
8372             }
8373         }
8374         return Math.max(min, newValue);
8375     },
8376
8377     // private
8378     resizeElement : function(){
8379         var box = this.proxy.getBox();
8380         if(this.updateBox){
8381             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8382         }else{
8383             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8384         }
8385         this.updateChildSize();
8386         if(!this.dynamic){
8387             this.proxy.hide();
8388         }
8389         return box;
8390     },
8391
8392     // private
8393     constrain : function(v, diff, m, mx){
8394         if(v - diff < m){
8395             diff = v - m;
8396         }else if(v - diff > mx){
8397             diff = mx - v;
8398         }
8399         return diff;
8400     },
8401
8402     // private
8403     onMouseMove : function(e){
8404         
8405         if(this.enabled){
8406             try{// try catch so if something goes wrong the user doesn't get hung
8407
8408             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8409                 return;
8410             }
8411
8412             //var curXY = this.startPoint;
8413             var curSize = this.curSize || this.startBox;
8414             var x = this.startBox.x, y = this.startBox.y;
8415             var ox = x, oy = y;
8416             var w = curSize.width, h = curSize.height;
8417             var ow = w, oh = h;
8418             var mw = this.minWidth, mh = this.minHeight;
8419             var mxw = this.maxWidth, mxh = this.maxHeight;
8420             var wi = this.widthIncrement;
8421             var hi = this.heightIncrement;
8422
8423             var eventXY = e.getXY();
8424             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8425             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8426
8427             var pos = this.activeHandle.position;
8428
8429             switch(pos){
8430                 case "east":
8431                     w += diffX;
8432                     w = Math.min(Math.max(mw, w), mxw);
8433                     break;
8434              
8435                 case "south":
8436                     h += diffY;
8437                     h = Math.min(Math.max(mh, h), mxh);
8438                     break;
8439                 case "southeast":
8440                     w += diffX;
8441                     h += diffY;
8442                     w = Math.min(Math.max(mw, w), mxw);
8443                     h = Math.min(Math.max(mh, h), mxh);
8444                     break;
8445                 case "north":
8446                     diffY = this.constrain(h, diffY, mh, mxh);
8447                     y += diffY;
8448                     h -= diffY;
8449                     break;
8450                 case "hdrag":
8451                     
8452                     if (wi) {
8453                         var adiffX = Math.abs(diffX);
8454                         var sub = (adiffX % wi); // how much 
8455                         if (sub > (wi/2)) { // far enough to snap
8456                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8457                         } else {
8458                             // remove difference.. 
8459                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8460                         }
8461                     }
8462                     x += diffX;
8463                     x = Math.max(this.minX, x);
8464                     break;
8465                 case "west":
8466                     diffX = this.constrain(w, diffX, mw, mxw);
8467                     x += diffX;
8468                     w -= diffX;
8469                     break;
8470                 case "northeast":
8471                     w += diffX;
8472                     w = Math.min(Math.max(mw, w), mxw);
8473                     diffY = this.constrain(h, diffY, mh, mxh);
8474                     y += diffY;
8475                     h -= diffY;
8476                     break;
8477                 case "northwest":
8478                     diffX = this.constrain(w, diffX, mw, mxw);
8479                     diffY = this.constrain(h, diffY, mh, mxh);
8480                     y += diffY;
8481                     h -= diffY;
8482                     x += diffX;
8483                     w -= diffX;
8484                     break;
8485                case "southwest":
8486                     diffX = this.constrain(w, diffX, mw, mxw);
8487                     h += diffY;
8488                     h = Math.min(Math.max(mh, h), mxh);
8489                     x += diffX;
8490                     w -= diffX;
8491                     break;
8492             }
8493
8494             var sw = this.snap(w, wi, mw);
8495             var sh = this.snap(h, hi, mh);
8496             if(sw != w || sh != h){
8497                 switch(pos){
8498                     case "northeast":
8499                         y -= sh - h;
8500                     break;
8501                     case "north":
8502                         y -= sh - h;
8503                         break;
8504                     case "southwest":
8505                         x -= sw - w;
8506                     break;
8507                     case "west":
8508                         x -= sw - w;
8509                         break;
8510                     case "northwest":
8511                         x -= sw - w;
8512                         y -= sh - h;
8513                     break;
8514                 }
8515                 w = sw;
8516                 h = sh;
8517             }
8518
8519             if(this.preserveRatio){
8520                 switch(pos){
8521                     case "southeast":
8522                     case "east":
8523                         h = oh * (w/ow);
8524                         h = Math.min(Math.max(mh, h), mxh);
8525                         w = ow * (h/oh);
8526                        break;
8527                     case "south":
8528                         w = ow * (h/oh);
8529                         w = Math.min(Math.max(mw, w), mxw);
8530                         h = oh * (w/ow);
8531                         break;
8532                     case "northeast":
8533                         w = ow * (h/oh);
8534                         w = Math.min(Math.max(mw, w), mxw);
8535                         h = oh * (w/ow);
8536                     break;
8537                     case "north":
8538                         var tw = w;
8539                         w = ow * (h/oh);
8540                         w = Math.min(Math.max(mw, w), mxw);
8541                         h = oh * (w/ow);
8542                         x += (tw - w) / 2;
8543                         break;
8544                     case "southwest":
8545                         h = oh * (w/ow);
8546                         h = Math.min(Math.max(mh, h), mxh);
8547                         var tw = w;
8548                         w = ow * (h/oh);
8549                         x += tw - w;
8550                         break;
8551                     case "west":
8552                         var th = h;
8553                         h = oh * (w/ow);
8554                         h = Math.min(Math.max(mh, h), mxh);
8555                         y += (th - h) / 2;
8556                         var tw = w;
8557                         w = ow * (h/oh);
8558                         x += tw - w;
8559                        break;
8560                     case "northwest":
8561                         var tw = w;
8562                         var th = h;
8563                         h = oh * (w/ow);
8564                         h = Math.min(Math.max(mh, h), mxh);
8565                         w = ow * (h/oh);
8566                         y += th - h;
8567                         x += tw - w;
8568                        break;
8569
8570                 }
8571             }
8572             if (pos == 'hdrag') {
8573                 w = ow;
8574             }
8575             this.proxy.setBounds(x, y, w, h);
8576             if(this.dynamic){
8577                 this.resizeElement();
8578             }
8579             }catch(e){}
8580         }
8581         this.fireEvent("resizing", this, x, y, w, h, e);
8582     },
8583
8584     // private
8585     handleOver : function(){
8586         if(this.enabled){
8587             this.el.addClass("x-resizable-over");
8588         }
8589     },
8590
8591     // private
8592     handleOut : function(){
8593         if(!this.resizing){
8594             this.el.removeClass("x-resizable-over");
8595         }
8596     },
8597
8598     /**
8599      * Returns the element this component is bound to.
8600      * @return {Roo.Element}
8601      */
8602     getEl : function(){
8603         return this.el;
8604     },
8605
8606     /**
8607      * Returns the resizeChild element (or null).
8608      * @return {Roo.Element}
8609      */
8610     getResizeChild : function(){
8611         return this.resizeChild;
8612     },
8613     groupHandler : function()
8614     {
8615         
8616     },
8617     /**
8618      * Destroys this resizable. If the element was wrapped and
8619      * removeEl is not true then the element remains.
8620      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8621      */
8622     destroy : function(removeEl){
8623         this.proxy.remove();
8624         if(this.overlay){
8625             this.overlay.removeAllListeners();
8626             this.overlay.remove();
8627         }
8628         var ps = Roo.Resizable.positions;
8629         for(var k in ps){
8630             if(typeof ps[k] != "function" && this[ps[k]]){
8631                 var h = this[ps[k]];
8632                 h.el.removeAllListeners();
8633                 h.el.remove();
8634             }
8635         }
8636         if(removeEl){
8637             this.el.update("");
8638             this.el.remove();
8639         }
8640     }
8641 });
8642
8643 // private
8644 // hash to map config positions to true positions
8645 Roo.Resizable.positions = {
8646     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8647     hd: "hdrag"
8648 };
8649
8650 // private
8651 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8652     if(!this.tpl){
8653         // only initialize the template if resizable is used
8654         var tpl = Roo.DomHelper.createTemplate(
8655             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8656         );
8657         tpl.compile();
8658         Roo.Resizable.Handle.prototype.tpl = tpl;
8659     }
8660     this.position = pos;
8661     this.rz = rz;
8662     // show north drag fro topdra
8663     var handlepos = pos == 'hdrag' ? 'north' : pos;
8664     
8665     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8666     if (pos == 'hdrag') {
8667         this.el.setStyle('cursor', 'pointer');
8668     }
8669     this.el.unselectable();
8670     if(transparent){
8671         this.el.setOpacity(0);
8672     }
8673     this.el.on("mousedown", this.onMouseDown, this);
8674     if(!disableTrackOver){
8675         this.el.on("mouseover", this.onMouseOver, this);
8676         this.el.on("mouseout", this.onMouseOut, this);
8677     }
8678 };
8679
8680 // private
8681 Roo.Resizable.Handle.prototype = {
8682     afterResize : function(rz){
8683         Roo.log('after?');
8684         // do nothing
8685     },
8686     // private
8687     onMouseDown : function(e){
8688         this.rz.onMouseDown(this, e);
8689     },
8690     // private
8691     onMouseOver : function(e){
8692         this.rz.handleOver(this, e);
8693     },
8694     // private
8695     onMouseOut : function(e){
8696         this.rz.handleOut(this, e);
8697     }
8698 };/*
8699  * Based on:
8700  * Ext JS Library 1.1.1
8701  * Copyright(c) 2006-2007, Ext JS, LLC.
8702  *
8703  * Originally Released Under LGPL - original licence link has changed is not relivant.
8704  *
8705  * Fork - LGPL
8706  * <script type="text/javascript">
8707  */
8708
8709 /**
8710  * @class Roo.Editor
8711  * @extends Roo.Component
8712  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8713  * @constructor
8714  * Create a new Editor
8715  * @param {Roo.form.Field} field The Field object (or descendant)
8716  * @param {Object} config The config object
8717  */
8718 Roo.Editor = function(field, config){
8719     Roo.Editor.superclass.constructor.call(this, config);
8720     this.field = field;
8721     this.addEvents({
8722         /**
8723              * @event beforestartedit
8724              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8725              * false from the handler of this event.
8726              * @param {Editor} this
8727              * @param {Roo.Element} boundEl The underlying element bound to this editor
8728              * @param {Mixed} value The field value being set
8729              */
8730         "beforestartedit" : true,
8731         /**
8732              * @event startedit
8733              * Fires when this editor is displayed
8734              * @param {Roo.Element} boundEl The underlying element bound to this editor
8735              * @param {Mixed} value The starting field value
8736              */
8737         "startedit" : true,
8738         /**
8739              * @event beforecomplete
8740              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8741              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8742              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8743              * event will not fire since no edit actually occurred.
8744              * @param {Editor} this
8745              * @param {Mixed} value The current field value
8746              * @param {Mixed} startValue The original field value
8747              */
8748         "beforecomplete" : true,
8749         /**
8750              * @event complete
8751              * Fires after editing is complete and any changed value has been written to the underlying field.
8752              * @param {Editor} this
8753              * @param {Mixed} value The current field value
8754              * @param {Mixed} startValue The original field value
8755              */
8756         "complete" : true,
8757         /**
8758          * @event specialkey
8759          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8760          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8761          * @param {Roo.form.Field} this
8762          * @param {Roo.EventObject} e The event object
8763          */
8764         "specialkey" : true
8765     });
8766 };
8767
8768 Roo.extend(Roo.Editor, Roo.Component, {
8769     /**
8770      * @cfg {Boolean/String} autosize
8771      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8772      * or "height" to adopt the height only (defaults to false)
8773      */
8774     /**
8775      * @cfg {Boolean} revertInvalid
8776      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8777      * validation fails (defaults to true)
8778      */
8779     /**
8780      * @cfg {Boolean} ignoreNoChange
8781      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8782      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8783      * will never be ignored.
8784      */
8785     /**
8786      * @cfg {Boolean} hideEl
8787      * False to keep the bound element visible while the editor is displayed (defaults to true)
8788      */
8789     /**
8790      * @cfg {Mixed} value
8791      * The data value of the underlying field (defaults to "")
8792      */
8793     value : "",
8794     /**
8795      * @cfg {String} alignment
8796      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8797      */
8798     alignment: "c-c?",
8799     /**
8800      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8801      * for bottom-right shadow (defaults to "frame")
8802      */
8803     shadow : "frame",
8804     /**
8805      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8806      */
8807     constrain : false,
8808     /**
8809      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8810      */
8811     completeOnEnter : false,
8812     /**
8813      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8814      */
8815     cancelOnEsc : false,
8816     /**
8817      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8818      */
8819     updateEl : false,
8820
8821     // private
8822     onRender : function(ct, position){
8823         this.el = new Roo.Layer({
8824             shadow: this.shadow,
8825             cls: "x-editor",
8826             parentEl : ct,
8827             shim : this.shim,
8828             shadowOffset:4,
8829             id: this.id,
8830             constrain: this.constrain
8831         });
8832         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8833         if(this.field.msgTarget != 'title'){
8834             this.field.msgTarget = 'qtip';
8835         }
8836         this.field.render(this.el);
8837         if(Roo.isGecko){
8838             this.field.el.dom.setAttribute('autocomplete', 'off');
8839         }
8840         this.field.on("specialkey", this.onSpecialKey, this);
8841         if(this.swallowKeys){
8842             this.field.el.swallowEvent(['keydown','keypress']);
8843         }
8844         this.field.show();
8845         this.field.on("blur", this.onBlur, this);
8846         if(this.field.grow){
8847             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8848         }
8849     },
8850
8851     onSpecialKey : function(field, e)
8852     {
8853         //Roo.log('editor onSpecialKey');
8854         if(this.completeOnEnter && e.getKey() == e.ENTER){
8855             e.stopEvent();
8856             this.completeEdit();
8857             return;
8858         }
8859         // do not fire special key otherwise it might hide close the editor...
8860         if(e.getKey() == e.ENTER){    
8861             return;
8862         }
8863         if(this.cancelOnEsc && e.getKey() == e.ESC){
8864             this.cancelEdit();
8865             return;
8866         } 
8867         this.fireEvent('specialkey', field, e);
8868     
8869     },
8870
8871     /**
8872      * Starts the editing process and shows the editor.
8873      * @param {String/HTMLElement/Element} el The element to edit
8874      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8875       * to the innerHTML of el.
8876      */
8877     startEdit : function(el, value){
8878         if(this.editing){
8879             this.completeEdit();
8880         }
8881         this.boundEl = Roo.get(el);
8882         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8883         if(!this.rendered){
8884             this.render(this.parentEl || document.body);
8885         }
8886         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8887             return;
8888         }
8889         this.startValue = v;
8890         this.field.setValue(v);
8891         if(this.autoSize){
8892             var sz = this.boundEl.getSize();
8893             switch(this.autoSize){
8894                 case "width":
8895                 this.setSize(sz.width,  "");
8896                 break;
8897                 case "height":
8898                 this.setSize("",  sz.height);
8899                 break;
8900                 default:
8901                 this.setSize(sz.width,  sz.height);
8902             }
8903         }
8904         this.el.alignTo(this.boundEl, this.alignment);
8905         this.editing = true;
8906         if(Roo.QuickTips){
8907             Roo.QuickTips.disable();
8908         }
8909         this.show();
8910     },
8911
8912     /**
8913      * Sets the height and width of this editor.
8914      * @param {Number} width The new width
8915      * @param {Number} height The new height
8916      */
8917     setSize : function(w, h){
8918         this.field.setSize(w, h);
8919         if(this.el){
8920             this.el.sync();
8921         }
8922     },
8923
8924     /**
8925      * Realigns the editor to the bound field based on the current alignment config value.
8926      */
8927     realign : function(){
8928         this.el.alignTo(this.boundEl, this.alignment);
8929     },
8930
8931     /**
8932      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8933      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8934      */
8935     completeEdit : function(remainVisible){
8936         if(!this.editing){
8937             return;
8938         }
8939         var v = this.getValue();
8940         if(this.revertInvalid !== false && !this.field.isValid()){
8941             v = this.startValue;
8942             this.cancelEdit(true);
8943         }
8944         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8945             this.editing = false;
8946             this.hide();
8947             return;
8948         }
8949         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8950             this.editing = false;
8951             if(this.updateEl && this.boundEl){
8952                 this.boundEl.update(v);
8953             }
8954             if(remainVisible !== true){
8955                 this.hide();
8956             }
8957             this.fireEvent("complete", this, v, this.startValue);
8958         }
8959     },
8960
8961     // private
8962     onShow : function(){
8963         this.el.show();
8964         if(this.hideEl !== false){
8965             this.boundEl.hide();
8966         }
8967         this.field.show();
8968         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8969             this.fixIEFocus = true;
8970             this.deferredFocus.defer(50, this);
8971         }else{
8972             this.field.focus();
8973         }
8974         this.fireEvent("startedit", this.boundEl, this.startValue);
8975     },
8976
8977     deferredFocus : function(){
8978         if(this.editing){
8979             this.field.focus();
8980         }
8981     },
8982
8983     /**
8984      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8985      * reverted to the original starting value.
8986      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8987      * cancel (defaults to false)
8988      */
8989     cancelEdit : function(remainVisible){
8990         if(this.editing){
8991             this.setValue(this.startValue);
8992             if(remainVisible !== true){
8993                 this.hide();
8994             }
8995         }
8996     },
8997
8998     // private
8999     onBlur : function(){
9000         if(this.allowBlur !== true && this.editing){
9001             this.completeEdit();
9002         }
9003     },
9004
9005     // private
9006     onHide : function(){
9007         if(this.editing){
9008             this.completeEdit();
9009             return;
9010         }
9011         this.field.blur();
9012         if(this.field.collapse){
9013             this.field.collapse();
9014         }
9015         this.el.hide();
9016         if(this.hideEl !== false){
9017             this.boundEl.show();
9018         }
9019         if(Roo.QuickTips){
9020             Roo.QuickTips.enable();
9021         }
9022     },
9023
9024     /**
9025      * Sets the data value of the editor
9026      * @param {Mixed} value Any valid value supported by the underlying field
9027      */
9028     setValue : function(v){
9029         this.field.setValue(v);
9030     },
9031
9032     /**
9033      * Gets the data value of the editor
9034      * @return {Mixed} The data value
9035      */
9036     getValue : function(){
9037         return this.field.getValue();
9038     }
9039 });/*
9040  * Based on:
9041  * Ext JS Library 1.1.1
9042  * Copyright(c) 2006-2007, Ext JS, LLC.
9043  *
9044  * Originally Released Under LGPL - original licence link has changed is not relivant.
9045  *
9046  * Fork - LGPL
9047  * <script type="text/javascript">
9048  */
9049  
9050 /**
9051  * @class Roo.BasicDialog
9052  * @extends Roo.util.Observable
9053  * @parent none builder
9054  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9055  * <pre><code>
9056 var dlg = new Roo.BasicDialog("my-dlg", {
9057     height: 200,
9058     width: 300,
9059     minHeight: 100,
9060     minWidth: 150,
9061     modal: true,
9062     proxyDrag: true,
9063     shadow: true
9064 });
9065 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9066 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9067 dlg.addButton('Cancel', dlg.hide, dlg);
9068 dlg.show();
9069 </code></pre>
9070   <b>A Dialog should always be a direct child of the body element.</b>
9071  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9072  * @cfg {String} title Default text to display in the title bar (defaults to null)
9073  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9074  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9075  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9076  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9077  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9078  * (defaults to null with no animation)
9079  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9080  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9081  * property for valid values (defaults to 'all')
9082  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9083  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9084  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9085  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9086  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9087  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9088  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9089  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9090  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9091  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9092  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9093  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9094  * draggable = true (defaults to false)
9095  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9096  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9097  * shadow (defaults to false)
9098  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9099  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9100  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9101  * @cfg {Array} buttons Array of buttons
9102  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9103  * @constructor
9104  * Create a new BasicDialog.
9105  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9106  * @param {Object} config Configuration options
9107  */
9108 Roo.BasicDialog = function(el, config){
9109     this.el = Roo.get(el);
9110     var dh = Roo.DomHelper;
9111     if(!this.el && config && config.autoCreate){
9112         if(typeof config.autoCreate == "object"){
9113             if(!config.autoCreate.id){
9114                 config.autoCreate.id = el;
9115             }
9116             this.el = dh.append(document.body,
9117                         config.autoCreate, true);
9118         }else{
9119             this.el = dh.append(document.body,
9120                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9121         }
9122     }
9123     el = this.el;
9124     el.setDisplayed(true);
9125     el.hide = this.hideAction;
9126     this.id = el.id;
9127     el.addClass("x-dlg");
9128
9129     Roo.apply(this, config);
9130
9131     this.proxy = el.createProxy("x-dlg-proxy");
9132     this.proxy.hide = this.hideAction;
9133     this.proxy.setOpacity(.5);
9134     this.proxy.hide();
9135
9136     if(config.width){
9137         el.setWidth(config.width);
9138     }
9139     if(config.height){
9140         el.setHeight(config.height);
9141     }
9142     this.size = el.getSize();
9143     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9144         this.xy = [config.x,config.y];
9145     }else{
9146         this.xy = el.getCenterXY(true);
9147     }
9148     /** The header element @type Roo.Element */
9149     this.header = el.child("> .x-dlg-hd");
9150     /** The body element @type Roo.Element */
9151     this.body = el.child("> .x-dlg-bd");
9152     /** The footer element @type Roo.Element */
9153     this.footer = el.child("> .x-dlg-ft");
9154
9155     if(!this.header){
9156         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9157     }
9158     if(!this.body){
9159         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9160     }
9161
9162     this.header.unselectable();
9163     if(this.title){
9164         this.header.update(this.title);
9165     }
9166     // this element allows the dialog to be focused for keyboard event
9167     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9168     this.focusEl.swallowEvent("click", true);
9169
9170     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9171
9172     // wrap the body and footer for special rendering
9173     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9174     if(this.footer){
9175         this.bwrap.dom.appendChild(this.footer.dom);
9176     }
9177
9178     this.bg = this.el.createChild({
9179         tag: "div", cls:"x-dlg-bg",
9180         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9181     });
9182     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9183
9184
9185     if(this.autoScroll !== false && !this.autoTabs){
9186         this.body.setStyle("overflow", "auto");
9187     }
9188
9189     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9190
9191     if(this.closable !== false){
9192         this.el.addClass("x-dlg-closable");
9193         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9194         this.close.on("click", this.closeClick, this);
9195         this.close.addClassOnOver("x-dlg-close-over");
9196     }
9197     if(this.collapsible !== false){
9198         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9199         this.collapseBtn.on("click", this.collapseClick, this);
9200         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9201         this.header.on("dblclick", this.collapseClick, this);
9202     }
9203     if(this.resizable !== false){
9204         this.el.addClass("x-dlg-resizable");
9205         this.resizer = new Roo.Resizable(el, {
9206             minWidth: this.minWidth || 80,
9207             minHeight:this.minHeight || 80,
9208             handles: this.resizeHandles || "all",
9209             pinned: true
9210         });
9211         this.resizer.on("beforeresize", this.beforeResize, this);
9212         this.resizer.on("resize", this.onResize, this);
9213     }
9214     if(this.draggable !== false){
9215         el.addClass("x-dlg-draggable");
9216         if (!this.proxyDrag) {
9217             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9218         }
9219         else {
9220             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9221         }
9222         dd.setHandleElId(this.header.id);
9223         dd.endDrag = this.endMove.createDelegate(this);
9224         dd.startDrag = this.startMove.createDelegate(this);
9225         dd.onDrag = this.onDrag.createDelegate(this);
9226         dd.scroll = false;
9227         this.dd = dd;
9228     }
9229     if(this.modal){
9230         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9231         this.mask.enableDisplayMode("block");
9232         this.mask.hide();
9233         this.el.addClass("x-dlg-modal");
9234     }
9235     if(this.shadow){
9236         this.shadow = new Roo.Shadow({
9237             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9238             offset : this.shadowOffset
9239         });
9240     }else{
9241         this.shadowOffset = 0;
9242     }
9243     if(Roo.useShims && this.shim !== false){
9244         this.shim = this.el.createShim();
9245         this.shim.hide = this.hideAction;
9246         this.shim.hide();
9247     }else{
9248         this.shim = false;
9249     }
9250     if(this.autoTabs){
9251         this.initTabs();
9252     }
9253     if (this.buttons) { 
9254         var bts= this.buttons;
9255         this.buttons = [];
9256         Roo.each(bts, function(b) {
9257             this.addButton(b);
9258         }, this);
9259     }
9260     
9261     
9262     this.addEvents({
9263         /**
9264          * @event keydown
9265          * Fires when a key is pressed
9266          * @param {Roo.BasicDialog} this
9267          * @param {Roo.EventObject} e
9268          */
9269         "keydown" : true,
9270         /**
9271          * @event move
9272          * Fires when this dialog is moved by the user.
9273          * @param {Roo.BasicDialog} this
9274          * @param {Number} x The new page X
9275          * @param {Number} y The new page Y
9276          */
9277         "move" : true,
9278         /**
9279          * @event resize
9280          * Fires when this dialog is resized by the user.
9281          * @param {Roo.BasicDialog} this
9282          * @param {Number} width The new width
9283          * @param {Number} height The new height
9284          */
9285         "resize" : true,
9286         /**
9287          * @event beforehide
9288          * Fires before this dialog is hidden.
9289          * @param {Roo.BasicDialog} this
9290          */
9291         "beforehide" : true,
9292         /**
9293          * @event hide
9294          * Fires when this dialog is hidden.
9295          * @param {Roo.BasicDialog} this
9296          */
9297         "hide" : true,
9298         /**
9299          * @event beforeshow
9300          * Fires before this dialog is shown.
9301          * @param {Roo.BasicDialog} this
9302          */
9303         "beforeshow" : true,
9304         /**
9305          * @event show
9306          * Fires when this dialog is shown.
9307          * @param {Roo.BasicDialog} this
9308          */
9309         "show" : true
9310     });
9311     el.on("keydown", this.onKeyDown, this);
9312     el.on("mousedown", this.toFront, this);
9313     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9314     this.el.hide();
9315     Roo.DialogManager.register(this);
9316     Roo.BasicDialog.superclass.constructor.call(this);
9317 };
9318
9319 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9320     shadowOffset: Roo.isIE ? 6 : 5,
9321     minHeight: 80,
9322     minWidth: 200,
9323     minButtonWidth: 75,
9324     defaultButton: null,
9325     buttonAlign: "right",
9326     tabTag: 'div',
9327     firstShow: true,
9328
9329     /**
9330      * Sets the dialog title text
9331      * @param {String} text The title text to display
9332      * @return {Roo.BasicDialog} this
9333      */
9334     setTitle : function(text){
9335         this.header.update(text);
9336         return this;
9337     },
9338
9339     // private
9340     closeClick : function(){
9341         this.hide();
9342     },
9343
9344     // private
9345     collapseClick : function(){
9346         this[this.collapsed ? "expand" : "collapse"]();
9347     },
9348
9349     /**
9350      * Collapses the dialog to its minimized state (only the title bar is visible).
9351      * Equivalent to the user clicking the collapse dialog button.
9352      */
9353     collapse : function(){
9354         if(!this.collapsed){
9355             this.collapsed = true;
9356             this.el.addClass("x-dlg-collapsed");
9357             this.restoreHeight = this.el.getHeight();
9358             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9359         }
9360     },
9361
9362     /**
9363      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9364      * clicking the expand dialog button.
9365      */
9366     expand : function(){
9367         if(this.collapsed){
9368             this.collapsed = false;
9369             this.el.removeClass("x-dlg-collapsed");
9370             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9371         }
9372     },
9373
9374     /**
9375      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9376      * @return {Roo.TabPanel} The tabs component
9377      */
9378     initTabs : function(){
9379         var tabs = this.getTabs();
9380         while(tabs.getTab(0)){
9381             tabs.removeTab(0);
9382         }
9383         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9384             var dom = el.dom;
9385             tabs.addTab(Roo.id(dom), dom.title);
9386             dom.title = "";
9387         });
9388         tabs.activate(0);
9389         return tabs;
9390     },
9391
9392     // private
9393     beforeResize : function(){
9394         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9395     },
9396
9397     // private
9398     onResize : function(){
9399         this.refreshSize();
9400         this.syncBodyHeight();
9401         this.adjustAssets();
9402         this.focus();
9403         this.fireEvent("resize", this, this.size.width, this.size.height);
9404     },
9405
9406     // private
9407     onKeyDown : function(e){
9408         if(this.isVisible()){
9409             this.fireEvent("keydown", this, e);
9410         }
9411     },
9412
9413     /**
9414      * Resizes the dialog.
9415      * @param {Number} width
9416      * @param {Number} height
9417      * @return {Roo.BasicDialog} this
9418      */
9419     resizeTo : function(width, height){
9420         this.el.setSize(width, height);
9421         this.size = {width: width, height: height};
9422         this.syncBodyHeight();
9423         if(this.fixedcenter){
9424             this.center();
9425         }
9426         if(this.isVisible()){
9427             this.constrainXY();
9428             this.adjustAssets();
9429         }
9430         this.fireEvent("resize", this, width, height);
9431         return this;
9432     },
9433
9434
9435     /**
9436      * Resizes the dialog to fit the specified content size.
9437      * @param {Number} width
9438      * @param {Number} height
9439      * @return {Roo.BasicDialog} this
9440      */
9441     setContentSize : function(w, h){
9442         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9443         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9444         //if(!this.el.isBorderBox()){
9445             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9446             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9447         //}
9448         if(this.tabs){
9449             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9450             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9451         }
9452         this.resizeTo(w, h);
9453         return this;
9454     },
9455
9456     /**
9457      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9458      * executed in response to a particular key being pressed while the dialog is active.
9459      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9460      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9461      * @param {Function} fn The function to call
9462      * @param {Object} scope (optional) The scope of the function
9463      * @return {Roo.BasicDialog} this
9464      */
9465     addKeyListener : function(key, fn, scope){
9466         var keyCode, shift, ctrl, alt;
9467         if(typeof key == "object" && !(key instanceof Array)){
9468             keyCode = key["key"];
9469             shift = key["shift"];
9470             ctrl = key["ctrl"];
9471             alt = key["alt"];
9472         }else{
9473             keyCode = key;
9474         }
9475         var handler = function(dlg, e){
9476             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9477                 var k = e.getKey();
9478                 if(keyCode instanceof Array){
9479                     for(var i = 0, len = keyCode.length; i < len; i++){
9480                         if(keyCode[i] == k){
9481                           fn.call(scope || window, dlg, k, e);
9482                           return;
9483                         }
9484                     }
9485                 }else{
9486                     if(k == keyCode){
9487                         fn.call(scope || window, dlg, k, e);
9488                     }
9489                 }
9490             }
9491         };
9492         this.on("keydown", handler);
9493         return this;
9494     },
9495
9496     /**
9497      * Returns the TabPanel component (creates it if it doesn't exist).
9498      * Note: If you wish to simply check for the existence of tabs without creating them,
9499      * check for a null 'tabs' property.
9500      * @return {Roo.TabPanel} The tabs component
9501      */
9502     getTabs : function(){
9503         if(!this.tabs){
9504             this.el.addClass("x-dlg-auto-tabs");
9505             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9506             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9507         }
9508         return this.tabs;
9509     },
9510
9511     /**
9512      * Adds a button to the footer section of the dialog.
9513      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9514      * object or a valid Roo.DomHelper element config
9515      * @param {Function} handler The function called when the button is clicked
9516      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9517      * @return {Roo.Button} The new button
9518      */
9519     addButton : function(config, handler, scope){
9520         var dh = Roo.DomHelper;
9521         if(!this.footer){
9522             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9523         }
9524         if(!this.btnContainer){
9525             var tb = this.footer.createChild({
9526
9527                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9528                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9529             }, null, true);
9530             this.btnContainer = tb.firstChild.firstChild.firstChild;
9531         }
9532         var bconfig = {
9533             handler: handler,
9534             scope: scope,
9535             minWidth: this.minButtonWidth,
9536             hideParent:true
9537         };
9538         if(typeof config == "string"){
9539             bconfig.text = config;
9540         }else{
9541             if(config.tag){
9542                 bconfig.dhconfig = config;
9543             }else{
9544                 Roo.apply(bconfig, config);
9545             }
9546         }
9547         var fc = false;
9548         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9549             bconfig.position = Math.max(0, bconfig.position);
9550             fc = this.btnContainer.childNodes[bconfig.position];
9551         }
9552          
9553         var btn = new Roo.Button(
9554             fc ? 
9555                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9556                 : this.btnContainer.appendChild(document.createElement("td")),
9557             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9558             bconfig
9559         );
9560         this.syncBodyHeight();
9561         if(!this.buttons){
9562             /**
9563              * Array of all the buttons that have been added to this dialog via addButton
9564              * @type Array
9565              */
9566             this.buttons = [];
9567         }
9568         this.buttons.push(btn);
9569         return btn;
9570     },
9571
9572     /**
9573      * Sets the default button to be focused when the dialog is displayed.
9574      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9575      * @return {Roo.BasicDialog} this
9576      */
9577     setDefaultButton : function(btn){
9578         this.defaultButton = btn;
9579         return this;
9580     },
9581
9582     // private
9583     getHeaderFooterHeight : function(safe){
9584         var height = 0;
9585         if(this.header){
9586            height += this.header.getHeight();
9587         }
9588         if(this.footer){
9589            var fm = this.footer.getMargins();
9590             height += (this.footer.getHeight()+fm.top+fm.bottom);
9591         }
9592         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9593         height += this.centerBg.getPadding("tb");
9594         return height;
9595     },
9596
9597     // private
9598     syncBodyHeight : function()
9599     {
9600         var bd = this.body, // the text
9601             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9602             bw = this.bwrap;
9603         var height = this.size.height - this.getHeaderFooterHeight(false);
9604         bd.setHeight(height-bd.getMargins("tb"));
9605         var hh = this.header.getHeight();
9606         var h = this.size.height-hh;
9607         cb.setHeight(h);
9608         
9609         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9610         bw.setHeight(h-cb.getPadding("tb"));
9611         
9612         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9613         bd.setWidth(bw.getWidth(true));
9614         if(this.tabs){
9615             this.tabs.syncHeight();
9616             if(Roo.isIE){
9617                 this.tabs.el.repaint();
9618             }
9619         }
9620     },
9621
9622     /**
9623      * Restores the previous state of the dialog if Roo.state is configured.
9624      * @return {Roo.BasicDialog} this
9625      */
9626     restoreState : function(){
9627         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9628         if(box && box.width){
9629             this.xy = [box.x, box.y];
9630             this.resizeTo(box.width, box.height);
9631         }
9632         return this;
9633     },
9634
9635     // private
9636     beforeShow : function(){
9637         this.expand();
9638         if(this.fixedcenter){
9639             this.xy = this.el.getCenterXY(true);
9640         }
9641         if(this.modal){
9642             Roo.get(document.body).addClass("x-body-masked");
9643             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9644             this.mask.show();
9645         }
9646         this.constrainXY();
9647     },
9648
9649     // private
9650     animShow : function(){
9651         var b = Roo.get(this.animateTarget).getBox();
9652         this.proxy.setSize(b.width, b.height);
9653         this.proxy.setLocation(b.x, b.y);
9654         this.proxy.show();
9655         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9656                     true, .35, this.showEl.createDelegate(this));
9657     },
9658
9659     /**
9660      * Shows the dialog.
9661      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9662      * @return {Roo.BasicDialog} this
9663      */
9664     show : function(animateTarget){
9665         if (this.fireEvent("beforeshow", this) === false){
9666             return;
9667         }
9668         if(this.syncHeightBeforeShow){
9669             this.syncBodyHeight();
9670         }else if(this.firstShow){
9671             this.firstShow = false;
9672             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9673         }
9674         this.animateTarget = animateTarget || this.animateTarget;
9675         if(!this.el.isVisible()){
9676             this.beforeShow();
9677             if(this.animateTarget && Roo.get(this.animateTarget)){
9678                 this.animShow();
9679             }else{
9680                 this.showEl();
9681             }
9682         }
9683         return this;
9684     },
9685
9686     // private
9687     showEl : function(){
9688         this.proxy.hide();
9689         this.el.setXY(this.xy);
9690         this.el.show();
9691         this.adjustAssets(true);
9692         this.toFront();
9693         this.focus();
9694         // IE peekaboo bug - fix found by Dave Fenwick
9695         if(Roo.isIE){
9696             this.el.repaint();
9697         }
9698         this.fireEvent("show", this);
9699     },
9700
9701     /**
9702      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9703      * dialog itself will receive focus.
9704      */
9705     focus : function(){
9706         if(this.defaultButton){
9707             this.defaultButton.focus();
9708         }else{
9709             this.focusEl.focus();
9710         }
9711     },
9712
9713     // private
9714     constrainXY : function(){
9715         if(this.constraintoviewport !== false){
9716             if(!this.viewSize){
9717                 if(this.container){
9718                     var s = this.container.getSize();
9719                     this.viewSize = [s.width, s.height];
9720                 }else{
9721                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9722                 }
9723             }
9724             var s = Roo.get(this.container||document).getScroll();
9725
9726             var x = this.xy[0], y = this.xy[1];
9727             var w = this.size.width, h = this.size.height;
9728             var vw = this.viewSize[0], vh = this.viewSize[1];
9729             // only move it if it needs it
9730             var moved = false;
9731             // first validate right/bottom
9732             if(x + w > vw+s.left){
9733                 x = vw - w;
9734                 moved = true;
9735             }
9736             if(y + h > vh+s.top){
9737                 y = vh - h;
9738                 moved = true;
9739             }
9740             // then make sure top/left isn't negative
9741             if(x < s.left){
9742                 x = s.left;
9743                 moved = true;
9744             }
9745             if(y < s.top){
9746                 y = s.top;
9747                 moved = true;
9748             }
9749             if(moved){
9750                 // cache xy
9751                 this.xy = [x, y];
9752                 if(this.isVisible()){
9753                     this.el.setLocation(x, y);
9754                     this.adjustAssets();
9755                 }
9756             }
9757         }
9758     },
9759
9760     // private
9761     onDrag : function(){
9762         if(!this.proxyDrag){
9763             this.xy = this.el.getXY();
9764             this.adjustAssets();
9765         }
9766     },
9767
9768     // private
9769     adjustAssets : function(doShow){
9770         var x = this.xy[0], y = this.xy[1];
9771         var w = this.size.width, h = this.size.height;
9772         if(doShow === true){
9773             if(this.shadow){
9774                 this.shadow.show(this.el);
9775             }
9776             if(this.shim){
9777                 this.shim.show();
9778             }
9779         }
9780         if(this.shadow && this.shadow.isVisible()){
9781             this.shadow.show(this.el);
9782         }
9783         if(this.shim && this.shim.isVisible()){
9784             this.shim.setBounds(x, y, w, h);
9785         }
9786     },
9787
9788     // private
9789     adjustViewport : function(w, h){
9790         if(!w || !h){
9791             w = Roo.lib.Dom.getViewWidth();
9792             h = Roo.lib.Dom.getViewHeight();
9793         }
9794         // cache the size
9795         this.viewSize = [w, h];
9796         if(this.modal && this.mask.isVisible()){
9797             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9798             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9799         }
9800         if(this.isVisible()){
9801             this.constrainXY();
9802         }
9803     },
9804
9805     /**
9806      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9807      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9808      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9809      */
9810     destroy : function(removeEl){
9811         if(this.isVisible()){
9812             this.animateTarget = null;
9813             this.hide();
9814         }
9815         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9816         if(this.tabs){
9817             this.tabs.destroy(removeEl);
9818         }
9819         Roo.destroy(
9820              this.shim,
9821              this.proxy,
9822              this.resizer,
9823              this.close,
9824              this.mask
9825         );
9826         if(this.dd){
9827             this.dd.unreg();
9828         }
9829         if(this.buttons){
9830            for(var i = 0, len = this.buttons.length; i < len; i++){
9831                this.buttons[i].destroy();
9832            }
9833         }
9834         this.el.removeAllListeners();
9835         if(removeEl === true){
9836             this.el.update("");
9837             this.el.remove();
9838         }
9839         Roo.DialogManager.unregister(this);
9840     },
9841
9842     // private
9843     startMove : function(){
9844         if(this.proxyDrag){
9845             this.proxy.show();
9846         }
9847         if(this.constraintoviewport !== false){
9848             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9849         }
9850     },
9851
9852     // private
9853     endMove : function(){
9854         if(!this.proxyDrag){
9855             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9856         }else{
9857             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9858             this.proxy.hide();
9859         }
9860         this.refreshSize();
9861         this.adjustAssets();
9862         this.focus();
9863         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9864     },
9865
9866     /**
9867      * Brings this dialog to the front of any other visible dialogs
9868      * @return {Roo.BasicDialog} this
9869      */
9870     toFront : function(){
9871         Roo.DialogManager.bringToFront(this);
9872         return this;
9873     },
9874
9875     /**
9876      * Sends this dialog to the back (under) of any other visible dialogs
9877      * @return {Roo.BasicDialog} this
9878      */
9879     toBack : function(){
9880         Roo.DialogManager.sendToBack(this);
9881         return this;
9882     },
9883
9884     /**
9885      * Centers this dialog in the viewport
9886      * @return {Roo.BasicDialog} this
9887      */
9888     center : function(){
9889         var xy = this.el.getCenterXY(true);
9890         this.moveTo(xy[0], xy[1]);
9891         return this;
9892     },
9893
9894     /**
9895      * Moves the dialog's top-left corner to the specified point
9896      * @param {Number} x
9897      * @param {Number} y
9898      * @return {Roo.BasicDialog} this
9899      */
9900     moveTo : function(x, y){
9901         this.xy = [x,y];
9902         if(this.isVisible()){
9903             this.el.setXY(this.xy);
9904             this.adjustAssets();
9905         }
9906         return this;
9907     },
9908
9909     /**
9910      * Aligns the dialog to the specified element
9911      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9912      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9913      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9914      * @return {Roo.BasicDialog} this
9915      */
9916     alignTo : function(element, position, offsets){
9917         this.xy = this.el.getAlignToXY(element, position, offsets);
9918         if(this.isVisible()){
9919             this.el.setXY(this.xy);
9920             this.adjustAssets();
9921         }
9922         return this;
9923     },
9924
9925     /**
9926      * Anchors an element to another element and realigns it when the window is resized.
9927      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9928      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9929      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9930      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9931      * is a number, it is used as the buffer delay (defaults to 50ms).
9932      * @return {Roo.BasicDialog} this
9933      */
9934     anchorTo : function(el, alignment, offsets, monitorScroll){
9935         var action = function(){
9936             this.alignTo(el, alignment, offsets);
9937         };
9938         Roo.EventManager.onWindowResize(action, this);
9939         var tm = typeof monitorScroll;
9940         if(tm != 'undefined'){
9941             Roo.EventManager.on(window, 'scroll', action, this,
9942                 {buffer: tm == 'number' ? monitorScroll : 50});
9943         }
9944         action.call(this);
9945         return this;
9946     },
9947
9948     /**
9949      * Returns true if the dialog is visible
9950      * @return {Boolean}
9951      */
9952     isVisible : function(){
9953         return this.el.isVisible();
9954     },
9955
9956     // private
9957     animHide : function(callback){
9958         var b = Roo.get(this.animateTarget).getBox();
9959         this.proxy.show();
9960         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9961         this.el.hide();
9962         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9963                     this.hideEl.createDelegate(this, [callback]));
9964     },
9965
9966     /**
9967      * Hides the dialog.
9968      * @param {Function} callback (optional) Function to call when the dialog is hidden
9969      * @return {Roo.BasicDialog} this
9970      */
9971     hide : function(callback){
9972         if (this.fireEvent("beforehide", this) === false){
9973             return;
9974         }
9975         if(this.shadow){
9976             this.shadow.hide();
9977         }
9978         if(this.shim) {
9979           this.shim.hide();
9980         }
9981         // sometimes animateTarget seems to get set.. causing problems...
9982         // this just double checks..
9983         if(this.animateTarget && Roo.get(this.animateTarget)) {
9984            this.animHide(callback);
9985         }else{
9986             this.el.hide();
9987             this.hideEl(callback);
9988         }
9989         return this;
9990     },
9991
9992     // private
9993     hideEl : function(callback){
9994         this.proxy.hide();
9995         if(this.modal){
9996             this.mask.hide();
9997             Roo.get(document.body).removeClass("x-body-masked");
9998         }
9999         this.fireEvent("hide", this);
10000         if(typeof callback == "function"){
10001             callback();
10002         }
10003     },
10004
10005     // private
10006     hideAction : function(){
10007         this.setLeft("-10000px");
10008         this.setTop("-10000px");
10009         this.setStyle("visibility", "hidden");
10010     },
10011
10012     // private
10013     refreshSize : function(){
10014         this.size = this.el.getSize();
10015         this.xy = this.el.getXY();
10016         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10017     },
10018
10019     // private
10020     // z-index is managed by the DialogManager and may be overwritten at any time
10021     setZIndex : function(index){
10022         if(this.modal){
10023             this.mask.setStyle("z-index", index);
10024         }
10025         if(this.shim){
10026             this.shim.setStyle("z-index", ++index);
10027         }
10028         if(this.shadow){
10029             this.shadow.setZIndex(++index);
10030         }
10031         this.el.setStyle("z-index", ++index);
10032         if(this.proxy){
10033             this.proxy.setStyle("z-index", ++index);
10034         }
10035         if(this.resizer){
10036             this.resizer.proxy.setStyle("z-index", ++index);
10037         }
10038
10039         this.lastZIndex = index;
10040     },
10041
10042     /**
10043      * Returns the element for this dialog
10044      * @return {Roo.Element} The underlying dialog Element
10045      */
10046     getEl : function(){
10047         return this.el;
10048     }
10049 });
10050
10051 /**
10052  * @class Roo.DialogManager
10053  * Provides global access to BasicDialogs that have been created and
10054  * support for z-indexing (layering) multiple open dialogs.
10055  */
10056 Roo.DialogManager = function(){
10057     var list = {};
10058     var accessList = [];
10059     var front = null;
10060
10061     // private
10062     var sortDialogs = function(d1, d2){
10063         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10064     };
10065
10066     // private
10067     var orderDialogs = function(){
10068         accessList.sort(sortDialogs);
10069         var seed = Roo.DialogManager.zseed;
10070         for(var i = 0, len = accessList.length; i < len; i++){
10071             var dlg = accessList[i];
10072             if(dlg){
10073                 dlg.setZIndex(seed + (i*10));
10074             }
10075         }
10076     };
10077
10078     return {
10079         /**
10080          * The starting z-index for BasicDialogs (defaults to 9000)
10081          * @type Number The z-index value
10082          */
10083         zseed : 9000,
10084
10085         // private
10086         register : function(dlg){
10087             list[dlg.id] = dlg;
10088             accessList.push(dlg);
10089         },
10090
10091         // private
10092         unregister : function(dlg){
10093             delete list[dlg.id];
10094             var i=0;
10095             var len=0;
10096             if(!accessList.indexOf){
10097                 for(  i = 0, len = accessList.length; i < len; i++){
10098                     if(accessList[i] == dlg){
10099                         accessList.splice(i, 1);
10100                         return;
10101                     }
10102                 }
10103             }else{
10104                  i = accessList.indexOf(dlg);
10105                 if(i != -1){
10106                     accessList.splice(i, 1);
10107                 }
10108             }
10109         },
10110
10111         /**
10112          * Gets a registered dialog by id
10113          * @param {String/Object} id The id of the dialog or a dialog
10114          * @return {Roo.BasicDialog} this
10115          */
10116         get : function(id){
10117             return typeof id == "object" ? id : list[id];
10118         },
10119
10120         /**
10121          * Brings the specified dialog to the front
10122          * @param {String/Object} dlg The id of the dialog or a dialog
10123          * @return {Roo.BasicDialog} this
10124          */
10125         bringToFront : function(dlg){
10126             dlg = this.get(dlg);
10127             if(dlg != front){
10128                 front = dlg;
10129                 dlg._lastAccess = new Date().getTime();
10130                 orderDialogs();
10131             }
10132             return dlg;
10133         },
10134
10135         /**
10136          * Sends the specified dialog to the back
10137          * @param {String/Object} dlg The id of the dialog or a dialog
10138          * @return {Roo.BasicDialog} this
10139          */
10140         sendToBack : function(dlg){
10141             dlg = this.get(dlg);
10142             dlg._lastAccess = -(new Date().getTime());
10143             orderDialogs();
10144             return dlg;
10145         },
10146
10147         /**
10148          * Hides all dialogs
10149          */
10150         hideAll : function(){
10151             for(var id in list){
10152                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10153                     list[id].hide();
10154                 }
10155             }
10156         }
10157     };
10158 }();
10159
10160 /**
10161  * @class Roo.LayoutDialog
10162  * @extends Roo.BasicDialog
10163  * @children Roo.ContentPanel
10164  * @parent builder none
10165  * Dialog which provides adjustments for working with a layout in a Dialog.
10166  * Add your necessary layout config options to the dialog's config.<br>
10167  * Example usage (including a nested layout):
10168  * <pre><code>
10169 if(!dialog){
10170     dialog = new Roo.LayoutDialog("download-dlg", {
10171         modal: true,
10172         width:600,
10173         height:450,
10174         shadow:true,
10175         minWidth:500,
10176         minHeight:350,
10177         autoTabs:true,
10178         proxyDrag:true,
10179         // layout config merges with the dialog config
10180         center:{
10181             tabPosition: "top",
10182             alwaysShowTabs: true
10183         }
10184     });
10185     dialog.addKeyListener(27, dialog.hide, dialog);
10186     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10187     dialog.addButton("Build It!", this.getDownload, this);
10188
10189     // we can even add nested layouts
10190     var innerLayout = new Roo.BorderLayout("dl-inner", {
10191         east: {
10192             initialSize: 200,
10193             autoScroll:true,
10194             split:true
10195         },
10196         center: {
10197             autoScroll:true
10198         }
10199     });
10200     innerLayout.beginUpdate();
10201     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10202     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10203     innerLayout.endUpdate(true);
10204
10205     var layout = dialog.getLayout();
10206     layout.beginUpdate();
10207     layout.add("center", new Roo.ContentPanel("standard-panel",
10208                         {title: "Download the Source", fitToFrame:true}));
10209     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10210                {title: "Build your own roo.js"}));
10211     layout.getRegion("center").showPanel(sp);
10212     layout.endUpdate();
10213 }
10214 </code></pre>
10215     * @constructor
10216     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10217     * @param {Object} config configuration options
10218   */
10219 Roo.LayoutDialog = function(el, cfg){
10220     
10221     var config=  cfg;
10222     if (typeof(cfg) == 'undefined') {
10223         config = Roo.apply({}, el);
10224         // not sure why we use documentElement here.. - it should always be body.
10225         // IE7 borks horribly if we use documentElement.
10226         // webkit also does not like documentElement - it creates a body element...
10227         el = Roo.get( document.body || document.documentElement ).createChild();
10228         //config.autoCreate = true;
10229     }
10230     
10231     
10232     config.autoTabs = false;
10233     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10234     this.body.setStyle({overflow:"hidden", position:"relative"});
10235     this.layout = new Roo.BorderLayout(this.body.dom, config);
10236     this.layout.monitorWindowResize = false;
10237     this.el.addClass("x-dlg-auto-layout");
10238     // fix case when center region overwrites center function
10239     this.center = Roo.BasicDialog.prototype.center;
10240     this.on("show", this.layout.layout, this.layout, true);
10241     if (config.items) {
10242         var xitems = config.items;
10243         delete config.items;
10244         Roo.each(xitems, this.addxtype, this);
10245     }
10246     
10247     
10248 };
10249 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10250     
10251     
10252     /**
10253      * @cfg {Roo.LayoutRegion} east  
10254      */
10255     /**
10256      * @cfg {Roo.LayoutRegion} west
10257      */
10258     /**
10259      * @cfg {Roo.LayoutRegion} south
10260      */
10261     /**
10262      * @cfg {Roo.LayoutRegion} north
10263      */
10264     /**
10265      * @cfg {Roo.LayoutRegion} center
10266      */
10267     /**
10268      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10269      */
10270     
10271     
10272     /**
10273      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10274      * @deprecated
10275      */
10276     endUpdate : function(){
10277         this.layout.endUpdate();
10278     },
10279
10280     /**
10281      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10282      *  @deprecated
10283      */
10284     beginUpdate : function(){
10285         this.layout.beginUpdate();
10286     },
10287
10288     /**
10289      * Get the BorderLayout for this dialog
10290      * @return {Roo.BorderLayout}
10291      */
10292     getLayout : function(){
10293         return this.layout;
10294     },
10295
10296     showEl : function(){
10297         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10298         if(Roo.isIE7){
10299             this.layout.layout();
10300         }
10301     },
10302
10303     // private
10304     // Use the syncHeightBeforeShow config option to control this automatically
10305     syncBodyHeight : function(){
10306         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10307         if(this.layout){this.layout.layout();}
10308     },
10309     
10310       /**
10311      * Add an xtype element (actually adds to the layout.)
10312      * @return {Object} xdata xtype object data.
10313      */
10314     
10315     addxtype : function(c) {
10316         return this.layout.addxtype(c);
10317     }
10318 });/*
10319  * Based on:
10320  * Ext JS Library 1.1.1
10321  * Copyright(c) 2006-2007, Ext JS, LLC.
10322  *
10323  * Originally Released Under LGPL - original licence link has changed is not relivant.
10324  *
10325  * Fork - LGPL
10326  * <script type="text/javascript">
10327  */
10328  
10329 /**
10330  * @class Roo.MessageBox
10331  * @static
10332  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10333  * Example usage:
10334  *<pre><code>
10335 // Basic alert:
10336 Roo.Msg.alert('Status', 'Changes saved successfully.');
10337
10338 // Prompt for user data:
10339 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10340     if (btn == 'ok'){
10341         // process text value...
10342     }
10343 });
10344
10345 // Show a dialog using config options:
10346 Roo.Msg.show({
10347    title:'Save Changes?',
10348    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10349    buttons: Roo.Msg.YESNOCANCEL,
10350    fn: processResult,
10351    animEl: 'elId'
10352 });
10353 </code></pre>
10354  * @static
10355  */
10356 Roo.MessageBox = function(){
10357     var dlg, opt, mask, waitTimer;
10358     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10359     var buttons, activeTextEl, bwidth;
10360
10361     // private
10362     var handleButton = function(button){
10363         dlg.hide();
10364         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10365     };
10366
10367     // private
10368     var handleHide = function(){
10369         if(opt && opt.cls){
10370             dlg.el.removeClass(opt.cls);
10371         }
10372         if(waitTimer){
10373             Roo.TaskMgr.stop(waitTimer);
10374             waitTimer = null;
10375         }
10376     };
10377
10378     // private
10379     var updateButtons = function(b){
10380         var width = 0;
10381         if(!b){
10382             buttons["ok"].hide();
10383             buttons["cancel"].hide();
10384             buttons["yes"].hide();
10385             buttons["no"].hide();
10386             dlg.footer.dom.style.display = 'none';
10387             return width;
10388         }
10389         dlg.footer.dom.style.display = '';
10390         for(var k in buttons){
10391             if(typeof buttons[k] != "function"){
10392                 if(b[k]){
10393                     buttons[k].show();
10394                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10395                     width += buttons[k].el.getWidth()+15;
10396                 }else{
10397                     buttons[k].hide();
10398                 }
10399             }
10400         }
10401         return width;
10402     };
10403
10404     // private
10405     var handleEsc = function(d, k, e){
10406         if(opt && opt.closable !== false){
10407             dlg.hide();
10408         }
10409         if(e){
10410             e.stopEvent();
10411         }
10412     };
10413
10414     return {
10415         /**
10416          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10417          * @return {Roo.BasicDialog} The BasicDialog element
10418          */
10419         getDialog : function(){
10420            if(!dlg){
10421                 dlg = new Roo.BasicDialog("x-msg-box", {
10422                     autoCreate : true,
10423                     shadow: true,
10424                     draggable: true,
10425                     resizable:false,
10426                     constraintoviewport:false,
10427                     fixedcenter:true,
10428                     collapsible : false,
10429                     shim:true,
10430                     modal: true,
10431                     width:400, height:100,
10432                     buttonAlign:"center",
10433                     closeClick : function(){
10434                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10435                             handleButton("no");
10436                         }else{
10437                             handleButton("cancel");
10438                         }
10439                     }
10440                 });
10441               
10442                 dlg.on("hide", handleHide);
10443                 mask = dlg.mask;
10444                 dlg.addKeyListener(27, handleEsc);
10445                 buttons = {};
10446                 var bt = this.buttonText;
10447                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10448                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10449                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10450                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10451                 bodyEl = dlg.body.createChild({
10452
10453                     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>'
10454                 });
10455                 msgEl = bodyEl.dom.firstChild;
10456                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10457                 textboxEl.enableDisplayMode();
10458                 textboxEl.addKeyListener([10,13], function(){
10459                     if(dlg.isVisible() && opt && opt.buttons){
10460                         if(opt.buttons.ok){
10461                             handleButton("ok");
10462                         }else if(opt.buttons.yes){
10463                             handleButton("yes");
10464                         }
10465                     }
10466                 });
10467                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10468                 textareaEl.enableDisplayMode();
10469                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10470                 progressEl.enableDisplayMode();
10471                 var pf = progressEl.dom.firstChild;
10472                 if (pf) {
10473                     pp = Roo.get(pf.firstChild);
10474                     pp.setHeight(pf.offsetHeight);
10475                 }
10476                 
10477             }
10478             return dlg;
10479         },
10480
10481         /**
10482          * Updates the message box body text
10483          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10484          * the XHTML-compliant non-breaking space character '&amp;#160;')
10485          * @return {Roo.MessageBox} This message box
10486          */
10487         updateText : function(text){
10488             if(!dlg.isVisible() && !opt.width){
10489                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10490             }
10491             msgEl.innerHTML = text || '&#160;';
10492       
10493             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10494             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10495             var w = Math.max(
10496                     Math.min(opt.width || cw , this.maxWidth), 
10497                     Math.max(opt.minWidth || this.minWidth, bwidth)
10498             );
10499             if(opt.prompt){
10500                 activeTextEl.setWidth(w);
10501             }
10502             if(dlg.isVisible()){
10503                 dlg.fixedcenter = false;
10504             }
10505             // to big, make it scroll. = But as usual stupid IE does not support
10506             // !important..
10507             
10508             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10509                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10510                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10511             } else {
10512                 bodyEl.dom.style.height = '';
10513                 bodyEl.dom.style.overflowY = '';
10514             }
10515             if (cw > w) {
10516                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10517             } else {
10518                 bodyEl.dom.style.overflowX = '';
10519             }
10520             
10521             dlg.setContentSize(w, bodyEl.getHeight());
10522             if(dlg.isVisible()){
10523                 dlg.fixedcenter = true;
10524             }
10525             return this;
10526         },
10527
10528         /**
10529          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10530          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10531          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10532          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10533          * @return {Roo.MessageBox} This message box
10534          */
10535         updateProgress : function(value, text){
10536             if(text){
10537                 this.updateText(text);
10538             }
10539             if (pp) { // weird bug on my firefox - for some reason this is not defined
10540                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10541             }
10542             return this;
10543         },        
10544
10545         /**
10546          * Returns true if the message box is currently displayed
10547          * @return {Boolean} True if the message box is visible, else false
10548          */
10549         isVisible : function(){
10550             return dlg && dlg.isVisible();  
10551         },
10552
10553         /**
10554          * Hides the message box if it is displayed
10555          */
10556         hide : function(){
10557             if(this.isVisible()){
10558                 dlg.hide();
10559             }  
10560         },
10561
10562         /**
10563          * Displays a new message box, or reinitializes an existing message box, based on the config options
10564          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10565          * The following config object properties are supported:
10566          * <pre>
10567 Property    Type             Description
10568 ----------  ---------------  ------------------------------------------------------------------------------------
10569 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10570                                    closes (defaults to undefined)
10571 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10572                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10573 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10574                                    progress and wait dialogs will ignore this property and always hide the
10575                                    close button as they can only be closed programmatically.
10576 cls               String           A custom CSS class to apply to the message box element
10577 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10578                                    displayed (defaults to 75)
10579 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10580                                    function will be btn (the name of the button that was clicked, if applicable,
10581                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10582                                    Progress and wait dialogs will ignore this option since they do not respond to
10583                                    user actions and can only be closed programmatically, so any required function
10584                                    should be called by the same code after it closes the dialog.
10585 icon              String           A CSS class that provides a background image to be used as an icon for
10586                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10587 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10588 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10589 modal             Boolean          False to allow user interaction with the page while the message box is
10590                                    displayed (defaults to true)
10591 msg               String           A string that will replace the existing message box body text (defaults
10592                                    to the XHTML-compliant non-breaking space character '&#160;')
10593 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10594 progress          Boolean          True to display a progress bar (defaults to false)
10595 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10596 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10597 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10598 title             String           The title text
10599 value             String           The string value to set into the active textbox element if displayed
10600 wait              Boolean          True to display a progress bar (defaults to false)
10601 width             Number           The width of the dialog in pixels
10602 </pre>
10603          *
10604          * Example usage:
10605          * <pre><code>
10606 Roo.Msg.show({
10607    title: 'Address',
10608    msg: 'Please enter your address:',
10609    width: 300,
10610    buttons: Roo.MessageBox.OKCANCEL,
10611    multiline: true,
10612    fn: saveAddress,
10613    animEl: 'addAddressBtn'
10614 });
10615 </code></pre>
10616          * @param {Object} config Configuration options
10617          * @return {Roo.MessageBox} This message box
10618          */
10619         show : function(options)
10620         {
10621             
10622             // this causes nightmares if you show one dialog after another
10623             // especially on callbacks..
10624              
10625             if(this.isVisible()){
10626                 
10627                 this.hide();
10628                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10629                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10630                 Roo.log("New Dialog Message:" +  options.msg )
10631                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10632                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10633                 
10634             }
10635             var d = this.getDialog();
10636             opt = options;
10637             d.setTitle(opt.title || "&#160;");
10638             d.close.setDisplayed(opt.closable !== false);
10639             activeTextEl = textboxEl;
10640             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10641             if(opt.prompt){
10642                 if(opt.multiline){
10643                     textboxEl.hide();
10644                     textareaEl.show();
10645                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10646                         opt.multiline : this.defaultTextHeight);
10647                     activeTextEl = textareaEl;
10648                 }else{
10649                     textboxEl.show();
10650                     textareaEl.hide();
10651                 }
10652             }else{
10653                 textboxEl.hide();
10654                 textareaEl.hide();
10655             }
10656             progressEl.setDisplayed(opt.progress === true);
10657             this.updateProgress(0);
10658             activeTextEl.dom.value = opt.value || "";
10659             if(opt.prompt){
10660                 dlg.setDefaultButton(activeTextEl);
10661             }else{
10662                 var bs = opt.buttons;
10663                 var db = null;
10664                 if(bs && bs.ok){
10665                     db = buttons["ok"];
10666                 }else if(bs && bs.yes){
10667                     db = buttons["yes"];
10668                 }
10669                 dlg.setDefaultButton(db);
10670             }
10671             bwidth = updateButtons(opt.buttons);
10672             this.updateText(opt.msg);
10673             if(opt.cls){
10674                 d.el.addClass(opt.cls);
10675             }
10676             d.proxyDrag = opt.proxyDrag === true;
10677             d.modal = opt.modal !== false;
10678             d.mask = opt.modal !== false ? mask : false;
10679             if(!d.isVisible()){
10680                 // force it to the end of the z-index stack so it gets a cursor in FF
10681                 document.body.appendChild(dlg.el.dom);
10682                 d.animateTarget = null;
10683                 d.show(options.animEl);
10684             }
10685             dlg.toFront();
10686             return this;
10687         },
10688
10689         /**
10690          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10691          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10692          * and closing the message box when the process is complete.
10693          * @param {String} title The title bar text
10694          * @param {String} msg The message box body text
10695          * @return {Roo.MessageBox} This message box
10696          */
10697         progress : function(title, msg){
10698             this.show({
10699                 title : title,
10700                 msg : msg,
10701                 buttons: false,
10702                 progress:true,
10703                 closable:false,
10704                 minWidth: this.minProgressWidth,
10705                 modal : true
10706             });
10707             return this;
10708         },
10709
10710         /**
10711          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10712          * If a callback function is passed it will be called after the user clicks the button, and the
10713          * id of the button that was clicked will be passed as the only parameter to the callback
10714          * (could also be the top-right close button).
10715          * @param {String} title The title bar text
10716          * @param {String} msg The message box body text
10717          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10718          * @param {Object} scope (optional) The scope of the callback function
10719          * @return {Roo.MessageBox} This message box
10720          */
10721         alert : function(title, msg, fn, scope){
10722             this.show({
10723                 title : title,
10724                 msg : msg,
10725                 buttons: this.OK,
10726                 fn: fn,
10727                 scope : scope,
10728                 modal : true
10729             });
10730             return this;
10731         },
10732
10733         /**
10734          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10735          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10736          * You are responsible for closing the message box when the process is complete.
10737          * @param {String} msg The message box body text
10738          * @param {String} title (optional) The title bar text
10739          * @return {Roo.MessageBox} This message box
10740          */
10741         wait : function(msg, title){
10742             this.show({
10743                 title : title,
10744                 msg : msg,
10745                 buttons: false,
10746                 closable:false,
10747                 progress:true,
10748                 modal:true,
10749                 width:300,
10750                 wait:true
10751             });
10752             waitTimer = Roo.TaskMgr.start({
10753                 run: function(i){
10754                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10755                 },
10756                 interval: 1000
10757             });
10758             return this;
10759         },
10760
10761         /**
10762          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10763          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10764          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10765          * @param {String} title The title bar text
10766          * @param {String} msg The message box body text
10767          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10768          * @param {Object} scope (optional) The scope of the callback function
10769          * @return {Roo.MessageBox} This message box
10770          */
10771         confirm : function(title, msg, fn, scope){
10772             this.show({
10773                 title : title,
10774                 msg : msg,
10775                 buttons: this.YESNO,
10776                 fn: fn,
10777                 scope : scope,
10778                 modal : true
10779             });
10780             return this;
10781         },
10782
10783         /**
10784          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10785          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10786          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10787          * (could also be the top-right close button) and the text that was entered will be passed as the two
10788          * parameters to the callback.
10789          * @param {String} title The title bar text
10790          * @param {String} msg The message box body text
10791          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10792          * @param {Object} scope (optional) The scope of the callback function
10793          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10794          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10795          * @return {Roo.MessageBox} This message box
10796          */
10797         prompt : function(title, msg, fn, scope, multiline){
10798             this.show({
10799                 title : title,
10800                 msg : msg,
10801                 buttons: this.OKCANCEL,
10802                 fn: fn,
10803                 minWidth:250,
10804                 scope : scope,
10805                 prompt:true,
10806                 multiline: multiline,
10807                 modal : true
10808             });
10809             return this;
10810         },
10811
10812         /**
10813          * Button config that displays a single OK button
10814          * @type Object
10815          */
10816         OK : {ok:true},
10817         /**
10818          * Button config that displays Yes and No buttons
10819          * @type Object
10820          */
10821         YESNO : {yes:true, no:true},
10822         /**
10823          * Button config that displays OK and Cancel buttons
10824          * @type Object
10825          */
10826         OKCANCEL : {ok:true, cancel:true},
10827         /**
10828          * Button config that displays Yes, No and Cancel buttons
10829          * @type Object
10830          */
10831         YESNOCANCEL : {yes:true, no:true, cancel:true},
10832
10833         /**
10834          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10835          * @type Number
10836          */
10837         defaultTextHeight : 75,
10838         /**
10839          * The maximum width in pixels of the message box (defaults to 600)
10840          * @type Number
10841          */
10842         maxWidth : 600,
10843         /**
10844          * The minimum width in pixels of the message box (defaults to 100)
10845          * @type Number
10846          */
10847         minWidth : 100,
10848         /**
10849          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10850          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10851          * @type Number
10852          */
10853         minProgressWidth : 250,
10854         /**
10855          * An object containing the default button text strings that can be overriden for localized language support.
10856          * Supported properties are: ok, cancel, yes and no.
10857          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10858          * @type Object
10859          */
10860         buttonText : {
10861             ok : "OK",
10862             cancel : "Cancel",
10863             yes : "Yes",
10864             no : "No"
10865         }
10866     };
10867 }();
10868
10869 /**
10870  * Shorthand for {@link Roo.MessageBox}
10871  */
10872 Roo.Msg = Roo.MessageBox;/*
10873  * Based on:
10874  * Ext JS Library 1.1.1
10875  * Copyright(c) 2006-2007, Ext JS, LLC.
10876  *
10877  * Originally Released Under LGPL - original licence link has changed is not relivant.
10878  *
10879  * Fork - LGPL
10880  * <script type="text/javascript">
10881  */
10882 /**
10883  * @class Roo.QuickTips
10884  * Provides attractive and customizable tooltips for any element.
10885  * @static
10886  */
10887 Roo.QuickTips = function(){
10888     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10889     var ce, bd, xy, dd;
10890     var visible = false, disabled = true, inited = false;
10891     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10892     
10893     var onOver = function(e){
10894         if(disabled){
10895             return;
10896         }
10897         var t = e.getTarget();
10898         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10899             return;
10900         }
10901         if(ce && t == ce.el){
10902             clearTimeout(hideProc);
10903             return;
10904         }
10905         if(t && tagEls[t.id]){
10906             tagEls[t.id].el = t;
10907             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10908             return;
10909         }
10910         var ttp, et = Roo.fly(t);
10911         var ns = cfg.namespace;
10912         if(tm.interceptTitles && t.title){
10913             ttp = t.title;
10914             t.qtip = ttp;
10915             t.removeAttribute("title");
10916             e.preventDefault();
10917         }else{
10918             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10919         }
10920         if(ttp){
10921             showProc = show.defer(tm.showDelay, tm, [{
10922                 el: t, 
10923                 text: ttp.replace(/\\n/g,'<br/>'),
10924                 width: et.getAttributeNS(ns, cfg.width),
10925                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10926                 title: et.getAttributeNS(ns, cfg.title),
10927                     cls: et.getAttributeNS(ns, cfg.cls)
10928             }]);
10929         }
10930     };
10931     
10932     var onOut = function(e){
10933         clearTimeout(showProc);
10934         var t = e.getTarget();
10935         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10936             hideProc = setTimeout(hide, tm.hideDelay);
10937         }
10938     };
10939     
10940     var onMove = function(e){
10941         if(disabled){
10942             return;
10943         }
10944         xy = e.getXY();
10945         xy[1] += 18;
10946         if(tm.trackMouse && ce){
10947             el.setXY(xy);
10948         }
10949     };
10950     
10951     var onDown = function(e){
10952         clearTimeout(showProc);
10953         clearTimeout(hideProc);
10954         if(!e.within(el)){
10955             if(tm.hideOnClick){
10956                 hide();
10957                 tm.disable();
10958                 tm.enable.defer(100, tm);
10959             }
10960         }
10961     };
10962     
10963     var getPad = function(){
10964         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10965     };
10966
10967     var show = function(o){
10968         if(disabled){
10969             return;
10970         }
10971         clearTimeout(dismissProc);
10972         ce = o;
10973         if(removeCls){ // in case manually hidden
10974             el.removeClass(removeCls);
10975             removeCls = null;
10976         }
10977         if(ce.cls){
10978             el.addClass(ce.cls);
10979             removeCls = ce.cls;
10980         }
10981         if(ce.title){
10982             tipTitle.update(ce.title);
10983             tipTitle.show();
10984         }else{
10985             tipTitle.update('');
10986             tipTitle.hide();
10987         }
10988         el.dom.style.width  = tm.maxWidth+'px';
10989         //tipBody.dom.style.width = '';
10990         tipBodyText.update(o.text);
10991         var p = getPad(), w = ce.width;
10992         if(!w){
10993             var td = tipBodyText.dom;
10994             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10995             if(aw > tm.maxWidth){
10996                 w = tm.maxWidth;
10997             }else if(aw < tm.minWidth){
10998                 w = tm.minWidth;
10999             }else{
11000                 w = aw;
11001             }
11002         }
11003         //tipBody.setWidth(w);
11004         el.setWidth(parseInt(w, 10) + p);
11005         if(ce.autoHide === false){
11006             close.setDisplayed(true);
11007             if(dd){
11008                 dd.unlock();
11009             }
11010         }else{
11011             close.setDisplayed(false);
11012             if(dd){
11013                 dd.lock();
11014             }
11015         }
11016         if(xy){
11017             el.avoidY = xy[1]-18;
11018             el.setXY(xy);
11019         }
11020         if(tm.animate){
11021             el.setOpacity(.1);
11022             el.setStyle("visibility", "visible");
11023             el.fadeIn({callback: afterShow});
11024         }else{
11025             afterShow();
11026         }
11027     };
11028     
11029     var afterShow = function(){
11030         if(ce){
11031             el.show();
11032             esc.enable();
11033             if(tm.autoDismiss && ce.autoHide !== false){
11034                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11035             }
11036         }
11037     };
11038     
11039     var hide = function(noanim){
11040         clearTimeout(dismissProc);
11041         clearTimeout(hideProc);
11042         ce = null;
11043         if(el.isVisible()){
11044             esc.disable();
11045             if(noanim !== true && tm.animate){
11046                 el.fadeOut({callback: afterHide});
11047             }else{
11048                 afterHide();
11049             } 
11050         }
11051     };
11052     
11053     var afterHide = function(){
11054         el.hide();
11055         if(removeCls){
11056             el.removeClass(removeCls);
11057             removeCls = null;
11058         }
11059     };
11060     
11061     return {
11062         /**
11063         * @cfg {Number} minWidth
11064         * The minimum width of the quick tip (defaults to 40)
11065         */
11066        minWidth : 40,
11067         /**
11068         * @cfg {Number} maxWidth
11069         * The maximum width of the quick tip (defaults to 300)
11070         */
11071        maxWidth : 300,
11072         /**
11073         * @cfg {Boolean} interceptTitles
11074         * True to automatically use the element's DOM title value if available (defaults to false)
11075         */
11076        interceptTitles : false,
11077         /**
11078         * @cfg {Boolean} trackMouse
11079         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11080         */
11081        trackMouse : false,
11082         /**
11083         * @cfg {Boolean} hideOnClick
11084         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11085         */
11086        hideOnClick : true,
11087         /**
11088         * @cfg {Number} showDelay
11089         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11090         */
11091        showDelay : 500,
11092         /**
11093         * @cfg {Number} hideDelay
11094         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11095         */
11096        hideDelay : 200,
11097         /**
11098         * @cfg {Boolean} autoHide
11099         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11100         * Used in conjunction with hideDelay.
11101         */
11102        autoHide : true,
11103         /**
11104         * @cfg {Boolean}
11105         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11106         * (defaults to true).  Used in conjunction with autoDismissDelay.
11107         */
11108        autoDismiss : true,
11109         /**
11110         * @cfg {Number}
11111         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11112         */
11113        autoDismissDelay : 5000,
11114        /**
11115         * @cfg {Boolean} animate
11116         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11117         */
11118        animate : false,
11119
11120        /**
11121         * @cfg {String} title
11122         * Title text to display (defaults to '').  This can be any valid HTML markup.
11123         */
11124         title: '',
11125        /**
11126         * @cfg {String} text
11127         * Body text to display (defaults to '').  This can be any valid HTML markup.
11128         */
11129         text : '',
11130        /**
11131         * @cfg {String} cls
11132         * A CSS class to apply to the base quick tip element (defaults to '').
11133         */
11134         cls : '',
11135        /**
11136         * @cfg {Number} width
11137         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11138         * minWidth or maxWidth.
11139         */
11140         width : null,
11141
11142     /**
11143      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11144      * or display QuickTips in a page.
11145      */
11146        init : function(){
11147           tm = Roo.QuickTips;
11148           cfg = tm.tagConfig;
11149           if(!inited){
11150               if(!Roo.isReady){ // allow calling of init() before onReady
11151                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11152                   return;
11153               }
11154               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11155               el.fxDefaults = {stopFx: true};
11156               // maximum custom styling
11157               //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>');
11158               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>');              
11159               tipTitle = el.child('h3');
11160               tipTitle.enableDisplayMode("block");
11161               tipBody = el.child('div.x-tip-bd');
11162               tipBodyText = el.child('div.x-tip-bd-inner');
11163               //bdLeft = el.child('div.x-tip-bd-left');
11164               //bdRight = el.child('div.x-tip-bd-right');
11165               close = el.child('div.x-tip-close');
11166               close.enableDisplayMode("block");
11167               close.on("click", hide);
11168               var d = Roo.get(document);
11169               d.on("mousedown", onDown);
11170               d.on("mouseover", onOver);
11171               d.on("mouseout", onOut);
11172               d.on("mousemove", onMove);
11173               esc = d.addKeyListener(27, hide);
11174               esc.disable();
11175               if(Roo.dd.DD){
11176                   dd = el.initDD("default", null, {
11177                       onDrag : function(){
11178                           el.sync();  
11179                       }
11180                   });
11181                   dd.setHandleElId(tipTitle.id);
11182                   dd.lock();
11183               }
11184               inited = true;
11185           }
11186           this.enable(); 
11187        },
11188
11189     /**
11190      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11191      * are supported:
11192      * <pre>
11193 Property    Type                   Description
11194 ----------  ---------------------  ------------------------------------------------------------------------
11195 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11196      * </ul>
11197      * @param {Object} config The config object
11198      */
11199        register : function(config){
11200            var cs = config instanceof Array ? config : arguments;
11201            for(var i = 0, len = cs.length; i < len; i++) {
11202                var c = cs[i];
11203                var target = c.target;
11204                if(target){
11205                    if(target instanceof Array){
11206                        for(var j = 0, jlen = target.length; j < jlen; j++){
11207                            tagEls[target[j]] = c;
11208                        }
11209                    }else{
11210                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11211                    }
11212                }
11213            }
11214        },
11215
11216     /**
11217      * Removes this quick tip from its element and destroys it.
11218      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11219      */
11220        unregister : function(el){
11221            delete tagEls[Roo.id(el)];
11222        },
11223
11224     /**
11225      * Enable this quick tip.
11226      */
11227        enable : function(){
11228            if(inited && disabled){
11229                locks.pop();
11230                if(locks.length < 1){
11231                    disabled = false;
11232                }
11233            }
11234        },
11235
11236     /**
11237      * Disable this quick tip.
11238      */
11239        disable : function(){
11240           disabled = true;
11241           clearTimeout(showProc);
11242           clearTimeout(hideProc);
11243           clearTimeout(dismissProc);
11244           if(ce){
11245               hide(true);
11246           }
11247           locks.push(1);
11248        },
11249
11250     /**
11251      * Returns true if the quick tip is enabled, else false.
11252      */
11253        isEnabled : function(){
11254             return !disabled;
11255        },
11256
11257         // private
11258        tagConfig : {
11259            namespace : "roo", // was ext?? this may break..
11260            alt_namespace : "ext",
11261            attribute : "qtip",
11262            width : "width",
11263            target : "target",
11264            title : "qtitle",
11265            hide : "hide",
11266            cls : "qclass"
11267        }
11268    };
11269 }();
11270
11271 // backwards compat
11272 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11273  * Based on:
11274  * Ext JS Library 1.1.1
11275  * Copyright(c) 2006-2007, Ext JS, LLC.
11276  *
11277  * Originally Released Under LGPL - original licence link has changed is not relivant.
11278  *
11279  * Fork - LGPL
11280  * <script type="text/javascript">
11281  */
11282  
11283
11284 /**
11285  * @class Roo.tree.TreePanel
11286  * @extends Roo.data.Tree
11287  * @cfg {Roo.tree.TreeNode} root The root node
11288  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11289  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11290  * @cfg {Boolean} enableDD true to enable drag and drop
11291  * @cfg {Boolean} enableDrag true to enable just drag
11292  * @cfg {Boolean} enableDrop true to enable just drop
11293  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11294  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11295  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11296  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11297  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11298  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11299  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11300  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11301  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11302  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11303  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11304  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11305  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11306  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11307  * @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>
11308  * @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>
11309  * 
11310  * @constructor
11311  * @param {String/HTMLElement/Element} el The container element
11312  * @param {Object} config
11313  */
11314 Roo.tree.TreePanel = function(el, config){
11315     var root = false;
11316     var loader = false;
11317     if (config.root) {
11318         root = config.root;
11319         delete config.root;
11320     }
11321     if (config.loader) {
11322         loader = config.loader;
11323         delete config.loader;
11324     }
11325     
11326     Roo.apply(this, config);
11327     Roo.tree.TreePanel.superclass.constructor.call(this);
11328     this.el = Roo.get(el);
11329     this.el.addClass('x-tree');
11330     //console.log(root);
11331     if (root) {
11332         this.setRootNode( Roo.factory(root, Roo.tree));
11333     }
11334     if (loader) {
11335         this.loader = Roo.factory(loader, Roo.tree);
11336     }
11337    /**
11338     * Read-only. The id of the container element becomes this TreePanel's id.
11339     */
11340     this.id = this.el.id;
11341     this.addEvents({
11342         /**
11343         * @event beforeload
11344         * Fires before a node is loaded, return false to cancel
11345         * @param {Node} node The node being loaded
11346         */
11347         "beforeload" : true,
11348         /**
11349         * @event load
11350         * Fires when a node is loaded
11351         * @param {Node} node The node that was loaded
11352         */
11353         "load" : true,
11354         /**
11355         * @event textchange
11356         * Fires when the text for a node is changed
11357         * @param {Node} node The node
11358         * @param {String} text The new text
11359         * @param {String} oldText The old text
11360         */
11361         "textchange" : true,
11362         /**
11363         * @event beforeexpand
11364         * Fires before a node is expanded, return false to cancel.
11365         * @param {Node} node The node
11366         * @param {Boolean} deep
11367         * @param {Boolean} anim
11368         */
11369         "beforeexpand" : true,
11370         /**
11371         * @event beforecollapse
11372         * Fires before a node is collapsed, return false to cancel.
11373         * @param {Node} node The node
11374         * @param {Boolean} deep
11375         * @param {Boolean} anim
11376         */
11377         "beforecollapse" : true,
11378         /**
11379         * @event expand
11380         * Fires when a node is expanded
11381         * @param {Node} node The node
11382         */
11383         "expand" : true,
11384         /**
11385         * @event disabledchange
11386         * Fires when the disabled status of a node changes
11387         * @param {Node} node The node
11388         * @param {Boolean} disabled
11389         */
11390         "disabledchange" : true,
11391         /**
11392         * @event collapse
11393         * Fires when a node is collapsed
11394         * @param {Node} node The node
11395         */
11396         "collapse" : true,
11397         /**
11398         * @event beforeclick
11399         * Fires before click processing on a node. Return false to cancel the default action.
11400         * @param {Node} node The node
11401         * @param {Roo.EventObject} e The event object
11402         */
11403         "beforeclick":true,
11404         /**
11405         * @event checkchange
11406         * Fires when a node with a checkbox's checked property changes
11407         * @param {Node} this This node
11408         * @param {Boolean} checked
11409         */
11410         "checkchange":true,
11411         /**
11412         * @event click
11413         * Fires when a node is clicked
11414         * @param {Node} node The node
11415         * @param {Roo.EventObject} e The event object
11416         */
11417         "click":true,
11418         /**
11419         * @event dblclick
11420         * Fires when a node is double clicked
11421         * @param {Node} node The node
11422         * @param {Roo.EventObject} e The event object
11423         */
11424         "dblclick":true,
11425         /**
11426         * @event contextmenu
11427         * Fires when a node is right clicked
11428         * @param {Node} node The node
11429         * @param {Roo.EventObject} e The event object
11430         */
11431         "contextmenu":true,
11432         /**
11433         * @event beforechildrenrendered
11434         * Fires right before the child nodes for a node are rendered
11435         * @param {Node} node The node
11436         */
11437         "beforechildrenrendered":true,
11438         /**
11439         * @event startdrag
11440         * Fires when a node starts being dragged
11441         * @param {Roo.tree.TreePanel} this
11442         * @param {Roo.tree.TreeNode} node
11443         * @param {event} e The raw browser event
11444         */ 
11445        "startdrag" : true,
11446        /**
11447         * @event enddrag
11448         * Fires when a drag operation is complete
11449         * @param {Roo.tree.TreePanel} this
11450         * @param {Roo.tree.TreeNode} node
11451         * @param {event} e The raw browser event
11452         */
11453        "enddrag" : true,
11454        /**
11455         * @event dragdrop
11456         * Fires when a dragged node is dropped on a valid DD target
11457         * @param {Roo.tree.TreePanel} this
11458         * @param {Roo.tree.TreeNode} node
11459         * @param {DD} dd The dd it was dropped on
11460         * @param {event} e The raw browser event
11461         */
11462        "dragdrop" : true,
11463        /**
11464         * @event beforenodedrop
11465         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11466         * passed to handlers has the following properties:<br />
11467         * <ul style="padding:5px;padding-left:16px;">
11468         * <li>tree - The TreePanel</li>
11469         * <li>target - The node being targeted for the drop</li>
11470         * <li>data - The drag data from the drag source</li>
11471         * <li>point - The point of the drop - append, above or below</li>
11472         * <li>source - The drag source</li>
11473         * <li>rawEvent - Raw mouse event</li>
11474         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11475         * to be inserted by setting them on this object.</li>
11476         * <li>cancel - Set this to true to cancel the drop.</li>
11477         * </ul>
11478         * @param {Object} dropEvent
11479         */
11480        "beforenodedrop" : true,
11481        /**
11482         * @event nodedrop
11483         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11484         * passed to handlers has the following properties:<br />
11485         * <ul style="padding:5px;padding-left:16px;">
11486         * <li>tree - The TreePanel</li>
11487         * <li>target - The node being targeted for the drop</li>
11488         * <li>data - The drag data from the drag source</li>
11489         * <li>point - The point of the drop - append, above or below</li>
11490         * <li>source - The drag source</li>
11491         * <li>rawEvent - Raw mouse event</li>
11492         * <li>dropNode - Dropped node(s).</li>
11493         * </ul>
11494         * @param {Object} dropEvent
11495         */
11496        "nodedrop" : true,
11497         /**
11498         * @event nodedragover
11499         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11500         * passed to handlers has the following properties:<br />
11501         * <ul style="padding:5px;padding-left:16px;">
11502         * <li>tree - The TreePanel</li>
11503         * <li>target - The node being targeted for the drop</li>
11504         * <li>data - The drag data from the drag source</li>
11505         * <li>point - The point of the drop - append, above or below</li>
11506         * <li>source - The drag source</li>
11507         * <li>rawEvent - Raw mouse event</li>
11508         * <li>dropNode - Drop node(s) provided by the source.</li>
11509         * <li>cancel - Set this to true to signal drop not allowed.</li>
11510         * </ul>
11511         * @param {Object} dragOverEvent
11512         */
11513        "nodedragover" : true,
11514        /**
11515         * @event appendnode
11516         * Fires when append node to the tree
11517         * @param {Roo.tree.TreePanel} this
11518         * @param {Roo.tree.TreeNode} node
11519         * @param {Number} index The index of the newly appended node
11520         */
11521        "appendnode" : true
11522         
11523     });
11524     if(this.singleExpand){
11525        this.on("beforeexpand", this.restrictExpand, this);
11526     }
11527     if (this.editor) {
11528         this.editor.tree = this;
11529         this.editor = Roo.factory(this.editor, Roo.tree);
11530     }
11531     
11532     if (this.selModel) {
11533         this.selModel = Roo.factory(this.selModel, Roo.tree);
11534     }
11535    
11536 };
11537 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11538     rootVisible : true,
11539     animate: Roo.enableFx,
11540     lines : true,
11541     enableDD : false,
11542     hlDrop : Roo.enableFx,
11543   
11544     renderer: false,
11545     
11546     rendererTip: false,
11547     // private
11548     restrictExpand : function(node){
11549         var p = node.parentNode;
11550         if(p){
11551             if(p.expandedChild && p.expandedChild.parentNode == p){
11552                 p.expandedChild.collapse();
11553             }
11554             p.expandedChild = node;
11555         }
11556     },
11557
11558     // private override
11559     setRootNode : function(node){
11560         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11561         if(!this.rootVisible){
11562             node.ui = new Roo.tree.RootTreeNodeUI(node);
11563         }
11564         return node;
11565     },
11566
11567     /**
11568      * Returns the container element for this TreePanel
11569      */
11570     getEl : function(){
11571         return this.el;
11572     },
11573
11574     /**
11575      * Returns the default TreeLoader for this TreePanel
11576      */
11577     getLoader : function(){
11578         return this.loader;
11579     },
11580
11581     /**
11582      * Expand all nodes
11583      */
11584     expandAll : function(){
11585         this.root.expand(true);
11586     },
11587
11588     /**
11589      * Collapse all nodes
11590      */
11591     collapseAll : function(){
11592         this.root.collapse(true);
11593     },
11594
11595     /**
11596      * Returns the selection model used by this TreePanel
11597      */
11598     getSelectionModel : function(){
11599         if(!this.selModel){
11600             this.selModel = new Roo.tree.DefaultSelectionModel();
11601         }
11602         return this.selModel;
11603     },
11604
11605     /**
11606      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11607      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11608      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11609      * @return {Array}
11610      */
11611     getChecked : function(a, startNode){
11612         startNode = startNode || this.root;
11613         var r = [];
11614         var f = function(){
11615             if(this.attributes.checked){
11616                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11617             }
11618         }
11619         startNode.cascade(f);
11620         return r;
11621     },
11622
11623     /**
11624      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11625      * @param {String} path
11626      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11627      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11628      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11629      */
11630     expandPath : function(path, attr, callback){
11631         attr = attr || "id";
11632         var keys = path.split(this.pathSeparator);
11633         var curNode = this.root;
11634         if(curNode.attributes[attr] != keys[1]){ // invalid root
11635             if(callback){
11636                 callback(false, null);
11637             }
11638             return;
11639         }
11640         var index = 1;
11641         var f = function(){
11642             if(++index == keys.length){
11643                 if(callback){
11644                     callback(true, curNode);
11645                 }
11646                 return;
11647             }
11648             var c = curNode.findChild(attr, keys[index]);
11649             if(!c){
11650                 if(callback){
11651                     callback(false, curNode);
11652                 }
11653                 return;
11654             }
11655             curNode = c;
11656             c.expand(false, false, f);
11657         };
11658         curNode.expand(false, false, f);
11659     },
11660
11661     /**
11662      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11663      * @param {String} path
11664      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11665      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11666      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11667      */
11668     selectPath : function(path, attr, callback){
11669         attr = attr || "id";
11670         var keys = path.split(this.pathSeparator);
11671         var v = keys.pop();
11672         if(keys.length > 0){
11673             var f = function(success, node){
11674                 if(success && node){
11675                     var n = node.findChild(attr, v);
11676                     if(n){
11677                         n.select();
11678                         if(callback){
11679                             callback(true, n);
11680                         }
11681                     }else if(callback){
11682                         callback(false, n);
11683                     }
11684                 }else{
11685                     if(callback){
11686                         callback(false, n);
11687                     }
11688                 }
11689             };
11690             this.expandPath(keys.join(this.pathSeparator), attr, f);
11691         }else{
11692             this.root.select();
11693             if(callback){
11694                 callback(true, this.root);
11695             }
11696         }
11697     },
11698
11699     getTreeEl : function(){
11700         return this.el;
11701     },
11702
11703     /**
11704      * Trigger rendering of this TreePanel
11705      */
11706     render : function(){
11707         if (this.innerCt) {
11708             return this; // stop it rendering more than once!!
11709         }
11710         
11711         this.innerCt = this.el.createChild({tag:"ul",
11712                cls:"x-tree-root-ct " +
11713                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11714
11715         if(this.containerScroll){
11716             Roo.dd.ScrollManager.register(this.el);
11717         }
11718         if((this.enableDD || this.enableDrop) && !this.dropZone){
11719            /**
11720             * The dropZone used by this tree if drop is enabled
11721             * @type Roo.tree.TreeDropZone
11722             */
11723              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11724                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11725            });
11726         }
11727         if((this.enableDD || this.enableDrag) && !this.dragZone){
11728            /**
11729             * The dragZone used by this tree if drag is enabled
11730             * @type Roo.tree.TreeDragZone
11731             */
11732             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11733                ddGroup: this.ddGroup || "TreeDD",
11734                scroll: this.ddScroll
11735            });
11736         }
11737         this.getSelectionModel().init(this);
11738         if (!this.root) {
11739             Roo.log("ROOT not set in tree");
11740             return this;
11741         }
11742         this.root.render();
11743         if(!this.rootVisible){
11744             this.root.renderChildren();
11745         }
11746         return this;
11747     }
11748 });/*
11749  * Based on:
11750  * Ext JS Library 1.1.1
11751  * Copyright(c) 2006-2007, Ext JS, LLC.
11752  *
11753  * Originally Released Under LGPL - original licence link has changed is not relivant.
11754  *
11755  * Fork - LGPL
11756  * <script type="text/javascript">
11757  */
11758  
11759
11760 /**
11761  * @class Roo.tree.DefaultSelectionModel
11762  * @extends Roo.util.Observable
11763  * The default single selection for a TreePanel.
11764  * @param {Object} cfg Configuration
11765  */
11766 Roo.tree.DefaultSelectionModel = function(cfg){
11767    this.selNode = null;
11768    
11769    
11770    
11771    this.addEvents({
11772        /**
11773         * @event selectionchange
11774         * Fires when the selected node changes
11775         * @param {DefaultSelectionModel} this
11776         * @param {TreeNode} node the new selection
11777         */
11778        "selectionchange" : true,
11779
11780        /**
11781         * @event beforeselect
11782         * Fires before the selected node changes, return false to cancel the change
11783         * @param {DefaultSelectionModel} this
11784         * @param {TreeNode} node the new selection
11785         * @param {TreeNode} node the old selection
11786         */
11787        "beforeselect" : true
11788    });
11789    
11790     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11791 };
11792
11793 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11794     init : function(tree){
11795         this.tree = tree;
11796         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11797         tree.on("click", this.onNodeClick, this);
11798     },
11799     
11800     onNodeClick : function(node, e){
11801         if (e.ctrlKey && this.selNode == node)  {
11802             this.unselect(node);
11803             return;
11804         }
11805         this.select(node);
11806     },
11807     
11808     /**
11809      * Select a node.
11810      * @param {TreeNode} node The node to select
11811      * @return {TreeNode} The selected node
11812      */
11813     select : function(node){
11814         var last = this.selNode;
11815         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11816             if(last){
11817                 last.ui.onSelectedChange(false);
11818             }
11819             this.selNode = node;
11820             node.ui.onSelectedChange(true);
11821             this.fireEvent("selectionchange", this, node, last);
11822         }
11823         return node;
11824     },
11825     
11826     /**
11827      * Deselect a node.
11828      * @param {TreeNode} node The node to unselect
11829      */
11830     unselect : function(node){
11831         if(this.selNode == node){
11832             this.clearSelections();
11833         }    
11834     },
11835     
11836     /**
11837      * Clear all selections
11838      */
11839     clearSelections : function(){
11840         var n = this.selNode;
11841         if(n){
11842             n.ui.onSelectedChange(false);
11843             this.selNode = null;
11844             this.fireEvent("selectionchange", this, null);
11845         }
11846         return n;
11847     },
11848     
11849     /**
11850      * Get the selected node
11851      * @return {TreeNode} The selected node
11852      */
11853     getSelectedNode : function(){
11854         return this.selNode;    
11855     },
11856     
11857     /**
11858      * Returns true if the node is selected
11859      * @param {TreeNode} node The node to check
11860      * @return {Boolean}
11861      */
11862     isSelected : function(node){
11863         return this.selNode == node;  
11864     },
11865
11866     /**
11867      * Selects the node above the selected node in the tree, intelligently walking the nodes
11868      * @return TreeNode The new selection
11869      */
11870     selectPrevious : function(){
11871         var s = this.selNode || this.lastSelNode;
11872         if(!s){
11873             return null;
11874         }
11875         var ps = s.previousSibling;
11876         if(ps){
11877             if(!ps.isExpanded() || ps.childNodes.length < 1){
11878                 return this.select(ps);
11879             } else{
11880                 var lc = ps.lastChild;
11881                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11882                     lc = lc.lastChild;
11883                 }
11884                 return this.select(lc);
11885             }
11886         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11887             return this.select(s.parentNode);
11888         }
11889         return null;
11890     },
11891
11892     /**
11893      * Selects the node above the selected node in the tree, intelligently walking the nodes
11894      * @return TreeNode The new selection
11895      */
11896     selectNext : function(){
11897         var s = this.selNode || this.lastSelNode;
11898         if(!s){
11899             return null;
11900         }
11901         if(s.firstChild && s.isExpanded()){
11902              return this.select(s.firstChild);
11903          }else if(s.nextSibling){
11904              return this.select(s.nextSibling);
11905          }else if(s.parentNode){
11906             var newS = null;
11907             s.parentNode.bubble(function(){
11908                 if(this.nextSibling){
11909                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11910                     return false;
11911                 }
11912             });
11913             return newS;
11914          }
11915         return null;
11916     },
11917
11918     onKeyDown : function(e){
11919         var s = this.selNode || this.lastSelNode;
11920         // undesirable, but required
11921         var sm = this;
11922         if(!s){
11923             return;
11924         }
11925         var k = e.getKey();
11926         switch(k){
11927              case e.DOWN:
11928                  e.stopEvent();
11929                  this.selectNext();
11930              break;
11931              case e.UP:
11932                  e.stopEvent();
11933                  this.selectPrevious();
11934              break;
11935              case e.RIGHT:
11936                  e.preventDefault();
11937                  if(s.hasChildNodes()){
11938                      if(!s.isExpanded()){
11939                          s.expand();
11940                      }else if(s.firstChild){
11941                          this.select(s.firstChild, e);
11942                      }
11943                  }
11944              break;
11945              case e.LEFT:
11946                  e.preventDefault();
11947                  if(s.hasChildNodes() && s.isExpanded()){
11948                      s.collapse();
11949                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11950                      this.select(s.parentNode, e);
11951                  }
11952              break;
11953         };
11954     }
11955 });
11956
11957 /**
11958  * @class Roo.tree.MultiSelectionModel
11959  * @extends Roo.util.Observable
11960  * Multi selection for a TreePanel.
11961  * @param {Object} cfg Configuration
11962  */
11963 Roo.tree.MultiSelectionModel = function(){
11964    this.selNodes = [];
11965    this.selMap = {};
11966    this.addEvents({
11967        /**
11968         * @event selectionchange
11969         * Fires when the selected nodes change
11970         * @param {MultiSelectionModel} this
11971         * @param {Array} nodes Array of the selected nodes
11972         */
11973        "selectionchange" : true
11974    });
11975    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11976    
11977 };
11978
11979 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11980     init : function(tree){
11981         this.tree = tree;
11982         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11983         tree.on("click", this.onNodeClick, this);
11984     },
11985     
11986     onNodeClick : function(node, e){
11987         this.select(node, e, e.ctrlKey);
11988     },
11989     
11990     /**
11991      * Select a node.
11992      * @param {TreeNode} node The node to select
11993      * @param {EventObject} e (optional) An event associated with the selection
11994      * @param {Boolean} keepExisting True to retain existing selections
11995      * @return {TreeNode} The selected node
11996      */
11997     select : function(node, e, keepExisting){
11998         if(keepExisting !== true){
11999             this.clearSelections(true);
12000         }
12001         if(this.isSelected(node)){
12002             this.lastSelNode = node;
12003             return node;
12004         }
12005         this.selNodes.push(node);
12006         this.selMap[node.id] = node;
12007         this.lastSelNode = node;
12008         node.ui.onSelectedChange(true);
12009         this.fireEvent("selectionchange", this, this.selNodes);
12010         return node;
12011     },
12012     
12013     /**
12014      * Deselect a node.
12015      * @param {TreeNode} node The node to unselect
12016      */
12017     unselect : function(node){
12018         if(this.selMap[node.id]){
12019             node.ui.onSelectedChange(false);
12020             var sn = this.selNodes;
12021             var index = -1;
12022             if(sn.indexOf){
12023                 index = sn.indexOf(node);
12024             }else{
12025                 for(var i = 0, len = sn.length; i < len; i++){
12026                     if(sn[i] == node){
12027                         index = i;
12028                         break;
12029                     }
12030                 }
12031             }
12032             if(index != -1){
12033                 this.selNodes.splice(index, 1);
12034             }
12035             delete this.selMap[node.id];
12036             this.fireEvent("selectionchange", this, this.selNodes);
12037         }
12038     },
12039     
12040     /**
12041      * Clear all selections
12042      */
12043     clearSelections : function(suppressEvent){
12044         var sn = this.selNodes;
12045         if(sn.length > 0){
12046             for(var i = 0, len = sn.length; i < len; i++){
12047                 sn[i].ui.onSelectedChange(false);
12048             }
12049             this.selNodes = [];
12050             this.selMap = {};
12051             if(suppressEvent !== true){
12052                 this.fireEvent("selectionchange", this, this.selNodes);
12053             }
12054         }
12055     },
12056     
12057     /**
12058      * Returns true if the node is selected
12059      * @param {TreeNode} node The node to check
12060      * @return {Boolean}
12061      */
12062     isSelected : function(node){
12063         return this.selMap[node.id] ? true : false;  
12064     },
12065     
12066     /**
12067      * Returns an array of the selected nodes
12068      * @return {Array}
12069      */
12070     getSelectedNodes : function(){
12071         return this.selNodes;    
12072     },
12073
12074     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12075
12076     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12077
12078     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12079 });/*
12080  * Based on:
12081  * Ext JS Library 1.1.1
12082  * Copyright(c) 2006-2007, Ext JS, LLC.
12083  *
12084  * Originally Released Under LGPL - original licence link has changed is not relivant.
12085  *
12086  * Fork - LGPL
12087  * <script type="text/javascript">
12088  */
12089  
12090 /**
12091  * @class Roo.tree.TreeNode
12092  * @extends Roo.data.Node
12093  * @cfg {String} text The text for this node
12094  * @cfg {Boolean} expanded true to start the node expanded
12095  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12096  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12097  * @cfg {Boolean} disabled true to start the node disabled
12098  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12099  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12100  * @cfg {String} cls A css class to be added to the node
12101  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12102  * @cfg {String} href URL of the link used for the node (defaults to #)
12103  * @cfg {String} hrefTarget target frame for the link
12104  * @cfg {String} qtip An Ext QuickTip for the node
12105  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12106  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12107  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12108  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12109  * (defaults to undefined with no checkbox rendered)
12110  * @constructor
12111  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12112  */
12113 Roo.tree.TreeNode = function(attributes){
12114     attributes = attributes || {};
12115     if(typeof attributes == "string"){
12116         attributes = {text: attributes};
12117     }
12118     this.childrenRendered = false;
12119     this.rendered = false;
12120     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12121     this.expanded = attributes.expanded === true;
12122     this.isTarget = attributes.isTarget !== false;
12123     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12124     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12125
12126     /**
12127      * Read-only. The text for this node. To change it use setText().
12128      * @type String
12129      */
12130     this.text = attributes.text;
12131     /**
12132      * True if this node is disabled.
12133      * @type Boolean
12134      */
12135     this.disabled = attributes.disabled === true;
12136
12137     this.addEvents({
12138         /**
12139         * @event textchange
12140         * Fires when the text for this node is changed
12141         * @param {Node} this This node
12142         * @param {String} text The new text
12143         * @param {String} oldText The old text
12144         */
12145         "textchange" : true,
12146         /**
12147         * @event beforeexpand
12148         * Fires before this node is expanded, return false to cancel.
12149         * @param {Node} this This node
12150         * @param {Boolean} deep
12151         * @param {Boolean} anim
12152         */
12153         "beforeexpand" : true,
12154         /**
12155         * @event beforecollapse
12156         * Fires before this node is collapsed, return false to cancel.
12157         * @param {Node} this This node
12158         * @param {Boolean} deep
12159         * @param {Boolean} anim
12160         */
12161         "beforecollapse" : true,
12162         /**
12163         * @event expand
12164         * Fires when this node is expanded
12165         * @param {Node} this This node
12166         */
12167         "expand" : true,
12168         /**
12169         * @event disabledchange
12170         * Fires when the disabled status of this node changes
12171         * @param {Node} this This node
12172         * @param {Boolean} disabled
12173         */
12174         "disabledchange" : true,
12175         /**
12176         * @event collapse
12177         * Fires when this node is collapsed
12178         * @param {Node} this This node
12179         */
12180         "collapse" : true,
12181         /**
12182         * @event beforeclick
12183         * Fires before click processing. Return false to cancel the default action.
12184         * @param {Node} this This node
12185         * @param {Roo.EventObject} e The event object
12186         */
12187         "beforeclick":true,
12188         /**
12189         * @event checkchange
12190         * Fires when a node with a checkbox's checked property changes
12191         * @param {Node} this This node
12192         * @param {Boolean} checked
12193         */
12194         "checkchange":true,
12195         /**
12196         * @event click
12197         * Fires when this node is clicked
12198         * @param {Node} this This node
12199         * @param {Roo.EventObject} e The event object
12200         */
12201         "click":true,
12202         /**
12203         * @event dblclick
12204         * Fires when this node is double clicked
12205         * @param {Node} this This node
12206         * @param {Roo.EventObject} e The event object
12207         */
12208         "dblclick":true,
12209         /**
12210         * @event contextmenu
12211         * Fires when this node is right clicked
12212         * @param {Node} this This node
12213         * @param {Roo.EventObject} e The event object
12214         */
12215         "contextmenu":true,
12216         /**
12217         * @event beforechildrenrendered
12218         * Fires right before the child nodes for this node are rendered
12219         * @param {Node} this This node
12220         */
12221         "beforechildrenrendered":true
12222     });
12223
12224     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12225
12226     /**
12227      * Read-only. The UI for this node
12228      * @type TreeNodeUI
12229      */
12230     this.ui = new uiClass(this);
12231     
12232     // finally support items[]
12233     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12234         return;
12235     }
12236     
12237     
12238     Roo.each(this.attributes.items, function(c) {
12239         this.appendChild(Roo.factory(c,Roo.Tree));
12240     }, this);
12241     delete this.attributes.items;
12242     
12243     
12244     
12245 };
12246 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12247     preventHScroll: true,
12248     /**
12249      * Returns true if this node is expanded
12250      * @return {Boolean}
12251      */
12252     isExpanded : function(){
12253         return this.expanded;
12254     },
12255
12256     /**
12257      * Returns the UI object for this node
12258      * @return {TreeNodeUI}
12259      */
12260     getUI : function(){
12261         return this.ui;
12262     },
12263
12264     // private override
12265     setFirstChild : function(node){
12266         var of = this.firstChild;
12267         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12268         if(this.childrenRendered && of && node != of){
12269             of.renderIndent(true, true);
12270         }
12271         if(this.rendered){
12272             this.renderIndent(true, true);
12273         }
12274     },
12275
12276     // private override
12277     setLastChild : function(node){
12278         var ol = this.lastChild;
12279         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12280         if(this.childrenRendered && ol && node != ol){
12281             ol.renderIndent(true, true);
12282         }
12283         if(this.rendered){
12284             this.renderIndent(true, true);
12285         }
12286     },
12287
12288     // these methods are overridden to provide lazy rendering support
12289     // private override
12290     appendChild : function()
12291     {
12292         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12293         if(node && this.childrenRendered){
12294             node.render();
12295         }
12296         this.ui.updateExpandIcon();
12297         return node;
12298     },
12299
12300     // private override
12301     removeChild : function(node){
12302         this.ownerTree.getSelectionModel().unselect(node);
12303         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12304         // if it's been rendered remove dom node
12305         if(this.childrenRendered){
12306             node.ui.remove();
12307         }
12308         if(this.childNodes.length < 1){
12309             this.collapse(false, false);
12310         }else{
12311             this.ui.updateExpandIcon();
12312         }
12313         if(!this.firstChild) {
12314             this.childrenRendered = false;
12315         }
12316         return node;
12317     },
12318
12319     // private override
12320     insertBefore : function(node, refNode){
12321         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12322         if(newNode && refNode && this.childrenRendered){
12323             node.render();
12324         }
12325         this.ui.updateExpandIcon();
12326         return newNode;
12327     },
12328
12329     /**
12330      * Sets the text for this node
12331      * @param {String} text
12332      */
12333     setText : function(text){
12334         var oldText = this.text;
12335         this.text = text;
12336         this.attributes.text = text;
12337         if(this.rendered){ // event without subscribing
12338             this.ui.onTextChange(this, text, oldText);
12339         }
12340         this.fireEvent("textchange", this, text, oldText);
12341     },
12342
12343     /**
12344      * Triggers selection of this node
12345      */
12346     select : function(){
12347         this.getOwnerTree().getSelectionModel().select(this);
12348     },
12349
12350     /**
12351      * Triggers deselection of this node
12352      */
12353     unselect : function(){
12354         this.getOwnerTree().getSelectionModel().unselect(this);
12355     },
12356
12357     /**
12358      * Returns true if this node is selected
12359      * @return {Boolean}
12360      */
12361     isSelected : function(){
12362         return this.getOwnerTree().getSelectionModel().isSelected(this);
12363     },
12364
12365     /**
12366      * Expand this node.
12367      * @param {Boolean} deep (optional) True to expand all children as well
12368      * @param {Boolean} anim (optional) false to cancel the default animation
12369      * @param {Function} callback (optional) A callback to be called when
12370      * expanding this node completes (does not wait for deep expand to complete).
12371      * Called with 1 parameter, this node.
12372      */
12373     expand : function(deep, anim, callback){
12374         if(!this.expanded){
12375             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12376                 return;
12377             }
12378             if(!this.childrenRendered){
12379                 this.renderChildren();
12380             }
12381             this.expanded = true;
12382             
12383             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12384                 this.ui.animExpand(function(){
12385                     this.fireEvent("expand", this);
12386                     if(typeof callback == "function"){
12387                         callback(this);
12388                     }
12389                     if(deep === true){
12390                         this.expandChildNodes(true);
12391                     }
12392                 }.createDelegate(this));
12393                 return;
12394             }else{
12395                 this.ui.expand();
12396                 this.fireEvent("expand", this);
12397                 if(typeof callback == "function"){
12398                     callback(this);
12399                 }
12400             }
12401         }else{
12402            if(typeof callback == "function"){
12403                callback(this);
12404            }
12405         }
12406         if(deep === true){
12407             this.expandChildNodes(true);
12408         }
12409     },
12410
12411     isHiddenRoot : function(){
12412         return this.isRoot && !this.getOwnerTree().rootVisible;
12413     },
12414
12415     /**
12416      * Collapse this node.
12417      * @param {Boolean} deep (optional) True to collapse all children as well
12418      * @param {Boolean} anim (optional) false to cancel the default animation
12419      */
12420     collapse : function(deep, anim){
12421         if(this.expanded && !this.isHiddenRoot()){
12422             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12423                 return;
12424             }
12425             this.expanded = false;
12426             if((this.getOwnerTree().animate && anim !== false) || anim){
12427                 this.ui.animCollapse(function(){
12428                     this.fireEvent("collapse", this);
12429                     if(deep === true){
12430                         this.collapseChildNodes(true);
12431                     }
12432                 }.createDelegate(this));
12433                 return;
12434             }else{
12435                 this.ui.collapse();
12436                 this.fireEvent("collapse", this);
12437             }
12438         }
12439         if(deep === true){
12440             var cs = this.childNodes;
12441             for(var i = 0, len = cs.length; i < len; i++) {
12442                 cs[i].collapse(true, false);
12443             }
12444         }
12445     },
12446
12447     // private
12448     delayedExpand : function(delay){
12449         if(!this.expandProcId){
12450             this.expandProcId = this.expand.defer(delay, this);
12451         }
12452     },
12453
12454     // private
12455     cancelExpand : function(){
12456         if(this.expandProcId){
12457             clearTimeout(this.expandProcId);
12458         }
12459         this.expandProcId = false;
12460     },
12461
12462     /**
12463      * Toggles expanded/collapsed state of the node
12464      */
12465     toggle : function(){
12466         if(this.expanded){
12467             this.collapse();
12468         }else{
12469             this.expand();
12470         }
12471     },
12472
12473     /**
12474      * Ensures all parent nodes are expanded
12475      */
12476     ensureVisible : function(callback){
12477         var tree = this.getOwnerTree();
12478         tree.expandPath(this.parentNode.getPath(), false, function(){
12479             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12480             Roo.callback(callback);
12481         }.createDelegate(this));
12482     },
12483
12484     /**
12485      * Expand all child nodes
12486      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12487      */
12488     expandChildNodes : function(deep){
12489         var cs = this.childNodes;
12490         for(var i = 0, len = cs.length; i < len; i++) {
12491                 cs[i].expand(deep);
12492         }
12493     },
12494
12495     /**
12496      * Collapse all child nodes
12497      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12498      */
12499     collapseChildNodes : function(deep){
12500         var cs = this.childNodes;
12501         for(var i = 0, len = cs.length; i < len; i++) {
12502                 cs[i].collapse(deep);
12503         }
12504     },
12505
12506     /**
12507      * Disables this node
12508      */
12509     disable : function(){
12510         this.disabled = true;
12511         this.unselect();
12512         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12513             this.ui.onDisableChange(this, true);
12514         }
12515         this.fireEvent("disabledchange", this, true);
12516     },
12517
12518     /**
12519      * Enables this node
12520      */
12521     enable : function(){
12522         this.disabled = false;
12523         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12524             this.ui.onDisableChange(this, false);
12525         }
12526         this.fireEvent("disabledchange", this, false);
12527     },
12528
12529     // private
12530     renderChildren : function(suppressEvent){
12531         if(suppressEvent !== false){
12532             this.fireEvent("beforechildrenrendered", this);
12533         }
12534         var cs = this.childNodes;
12535         for(var i = 0, len = cs.length; i < len; i++){
12536             cs[i].render(true);
12537         }
12538         this.childrenRendered = true;
12539     },
12540
12541     // private
12542     sort : function(fn, scope){
12543         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12544         if(this.childrenRendered){
12545             var cs = this.childNodes;
12546             for(var i = 0, len = cs.length; i < len; i++){
12547                 cs[i].render(true);
12548             }
12549         }
12550     },
12551
12552     // private
12553     render : function(bulkRender){
12554         this.ui.render(bulkRender);
12555         if(!this.rendered){
12556             this.rendered = true;
12557             if(this.expanded){
12558                 this.expanded = false;
12559                 this.expand(false, false);
12560             }
12561         }
12562     },
12563
12564     // private
12565     renderIndent : function(deep, refresh){
12566         if(refresh){
12567             this.ui.childIndent = null;
12568         }
12569         this.ui.renderIndent();
12570         if(deep === true && this.childrenRendered){
12571             var cs = this.childNodes;
12572             for(var i = 0, len = cs.length; i < len; i++){
12573                 cs[i].renderIndent(true, refresh);
12574             }
12575         }
12576     }
12577 });/*
12578  * Based on:
12579  * Ext JS Library 1.1.1
12580  * Copyright(c) 2006-2007, Ext JS, LLC.
12581  *
12582  * Originally Released Under LGPL - original licence link has changed is not relivant.
12583  *
12584  * Fork - LGPL
12585  * <script type="text/javascript">
12586  */
12587  
12588 /**
12589  * @class Roo.tree.AsyncTreeNode
12590  * @extends Roo.tree.TreeNode
12591  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12592  * @constructor
12593  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12594  */
12595  Roo.tree.AsyncTreeNode = function(config){
12596     this.loaded = false;
12597     this.loading = false;
12598     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12599     /**
12600     * @event beforeload
12601     * Fires before this node is loaded, return false to cancel
12602     * @param {Node} this This node
12603     */
12604     this.addEvents({'beforeload':true, 'load': true});
12605     /**
12606     * @event load
12607     * Fires when this node is loaded
12608     * @param {Node} this This node
12609     */
12610     /**
12611      * The loader used by this node (defaults to using the tree's defined loader)
12612      * @type TreeLoader
12613      * @property loader
12614      */
12615 };
12616 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12617     expand : function(deep, anim, callback){
12618         if(this.loading){ // if an async load is already running, waiting til it's done
12619             var timer;
12620             var f = function(){
12621                 if(!this.loading){ // done loading
12622                     clearInterval(timer);
12623                     this.expand(deep, anim, callback);
12624                 }
12625             }.createDelegate(this);
12626             timer = setInterval(f, 200);
12627             return;
12628         }
12629         if(!this.loaded){
12630             if(this.fireEvent("beforeload", this) === false){
12631                 return;
12632             }
12633             this.loading = true;
12634             this.ui.beforeLoad(this);
12635             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12636             if(loader){
12637                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12638                 return;
12639             }
12640         }
12641         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12642     },
12643     
12644     /**
12645      * Returns true if this node is currently loading
12646      * @return {Boolean}
12647      */
12648     isLoading : function(){
12649         return this.loading;  
12650     },
12651     
12652     loadComplete : function(deep, anim, callback){
12653         this.loading = false;
12654         this.loaded = true;
12655         this.ui.afterLoad(this);
12656         this.fireEvent("load", this);
12657         this.expand(deep, anim, callback);
12658     },
12659     
12660     /**
12661      * Returns true if this node has been loaded
12662      * @return {Boolean}
12663      */
12664     isLoaded : function(){
12665         return this.loaded;
12666     },
12667     
12668     hasChildNodes : function(){
12669         if(!this.isLeaf() && !this.loaded){
12670             return true;
12671         }else{
12672             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12673         }
12674     },
12675
12676     /**
12677      * Trigger a reload for this node
12678      * @param {Function} callback
12679      */
12680     reload : function(callback){
12681         this.collapse(false, false);
12682         while(this.firstChild){
12683             this.removeChild(this.firstChild);
12684         }
12685         this.childrenRendered = false;
12686         this.loaded = false;
12687         if(this.isHiddenRoot()){
12688             this.expanded = false;
12689         }
12690         this.expand(false, false, callback);
12691     }
12692 });/*
12693  * Based on:
12694  * Ext JS Library 1.1.1
12695  * Copyright(c) 2006-2007, Ext JS, LLC.
12696  *
12697  * Originally Released Under LGPL - original licence link has changed is not relivant.
12698  *
12699  * Fork - LGPL
12700  * <script type="text/javascript">
12701  */
12702  
12703 /**
12704  * @class Roo.tree.TreeNodeUI
12705  * @constructor
12706  * @param {Object} node The node to render
12707  * The TreeNode UI implementation is separate from the
12708  * tree implementation. Unless you are customizing the tree UI,
12709  * you should never have to use this directly.
12710  */
12711 Roo.tree.TreeNodeUI = function(node){
12712     this.node = node;
12713     this.rendered = false;
12714     this.animating = false;
12715     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12716 };
12717
12718 Roo.tree.TreeNodeUI.prototype = {
12719     removeChild : function(node){
12720         if(this.rendered){
12721             this.ctNode.removeChild(node.ui.getEl());
12722         }
12723     },
12724
12725     beforeLoad : function(){
12726          this.addClass("x-tree-node-loading");
12727     },
12728
12729     afterLoad : function(){
12730          this.removeClass("x-tree-node-loading");
12731     },
12732
12733     onTextChange : function(node, text, oldText){
12734         if(this.rendered){
12735             this.textNode.innerHTML = text;
12736         }
12737     },
12738
12739     onDisableChange : function(node, state){
12740         this.disabled = state;
12741         if(state){
12742             this.addClass("x-tree-node-disabled");
12743         }else{
12744             this.removeClass("x-tree-node-disabled");
12745         }
12746     },
12747
12748     onSelectedChange : function(state){
12749         if(state){
12750             this.focus();
12751             this.addClass("x-tree-selected");
12752         }else{
12753             //this.blur();
12754             this.removeClass("x-tree-selected");
12755         }
12756     },
12757
12758     onMove : function(tree, node, oldParent, newParent, index, refNode){
12759         this.childIndent = null;
12760         if(this.rendered){
12761             var targetNode = newParent.ui.getContainer();
12762             if(!targetNode){//target not rendered
12763                 this.holder = document.createElement("div");
12764                 this.holder.appendChild(this.wrap);
12765                 return;
12766             }
12767             var insertBefore = refNode ? refNode.ui.getEl() : null;
12768             if(insertBefore){
12769                 targetNode.insertBefore(this.wrap, insertBefore);
12770             }else{
12771                 targetNode.appendChild(this.wrap);
12772             }
12773             this.node.renderIndent(true);
12774         }
12775     },
12776
12777     addClass : function(cls){
12778         if(this.elNode){
12779             Roo.fly(this.elNode).addClass(cls);
12780         }
12781     },
12782
12783     removeClass : function(cls){
12784         if(this.elNode){
12785             Roo.fly(this.elNode).removeClass(cls);
12786         }
12787     },
12788
12789     remove : function(){
12790         if(this.rendered){
12791             this.holder = document.createElement("div");
12792             this.holder.appendChild(this.wrap);
12793         }
12794     },
12795
12796     fireEvent : function(){
12797         return this.node.fireEvent.apply(this.node, arguments);
12798     },
12799
12800     initEvents : function(){
12801         this.node.on("move", this.onMove, this);
12802         var E = Roo.EventManager;
12803         var a = this.anchor;
12804
12805         var el = Roo.fly(a, '_treeui');
12806
12807         if(Roo.isOpera){ // opera render bug ignores the CSS
12808             el.setStyle("text-decoration", "none");
12809         }
12810
12811         el.on("click", this.onClick, this);
12812         el.on("dblclick", this.onDblClick, this);
12813
12814         if(this.checkbox){
12815             Roo.EventManager.on(this.checkbox,
12816                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12817         }
12818
12819         el.on("contextmenu", this.onContextMenu, this);
12820
12821         var icon = Roo.fly(this.iconNode);
12822         icon.on("click", this.onClick, this);
12823         icon.on("dblclick", this.onDblClick, this);
12824         icon.on("contextmenu", this.onContextMenu, this);
12825         E.on(this.ecNode, "click", this.ecClick, this, true);
12826
12827         if(this.node.disabled){
12828             this.addClass("x-tree-node-disabled");
12829         }
12830         if(this.node.hidden){
12831             this.addClass("x-tree-node-disabled");
12832         }
12833         var ot = this.node.getOwnerTree();
12834         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12835         if(dd && (!this.node.isRoot || ot.rootVisible)){
12836             Roo.dd.Registry.register(this.elNode, {
12837                 node: this.node,
12838                 handles: this.getDDHandles(),
12839                 isHandle: false
12840             });
12841         }
12842     },
12843
12844     getDDHandles : function(){
12845         return [this.iconNode, this.textNode];
12846     },
12847
12848     hide : function(){
12849         if(this.rendered){
12850             this.wrap.style.display = "none";
12851         }
12852     },
12853
12854     show : function(){
12855         if(this.rendered){
12856             this.wrap.style.display = "";
12857         }
12858     },
12859
12860     onContextMenu : function(e){
12861         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12862             e.preventDefault();
12863             this.focus();
12864             this.fireEvent("contextmenu", this.node, e);
12865         }
12866     },
12867
12868     onClick : function(e){
12869         if(this.dropping){
12870             e.stopEvent();
12871             return;
12872         }
12873         if(this.fireEvent("beforeclick", this.node, e) !== false){
12874             if(!this.disabled && this.node.attributes.href){
12875                 this.fireEvent("click", this.node, e);
12876                 return;
12877             }
12878             e.preventDefault();
12879             if(this.disabled){
12880                 return;
12881             }
12882
12883             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12884                 this.node.toggle();
12885             }
12886
12887             this.fireEvent("click", this.node, e);
12888         }else{
12889             e.stopEvent();
12890         }
12891     },
12892
12893     onDblClick : function(e){
12894         e.preventDefault();
12895         if(this.disabled){
12896             return;
12897         }
12898         if(this.checkbox){
12899             this.toggleCheck();
12900         }
12901         if(!this.animating && this.node.hasChildNodes()){
12902             this.node.toggle();
12903         }
12904         this.fireEvent("dblclick", this.node, e);
12905     },
12906
12907     onCheckChange : function(){
12908         var checked = this.checkbox.checked;
12909         this.node.attributes.checked = checked;
12910         this.fireEvent('checkchange', this.node, checked);
12911     },
12912
12913     ecClick : function(e){
12914         if(!this.animating && this.node.hasChildNodes()){
12915             this.node.toggle();
12916         }
12917     },
12918
12919     startDrop : function(){
12920         this.dropping = true;
12921     },
12922
12923     // delayed drop so the click event doesn't get fired on a drop
12924     endDrop : function(){
12925        setTimeout(function(){
12926            this.dropping = false;
12927        }.createDelegate(this), 50);
12928     },
12929
12930     expand : function(){
12931         this.updateExpandIcon();
12932         this.ctNode.style.display = "";
12933     },
12934
12935     focus : function(){
12936         if(!this.node.preventHScroll){
12937             try{this.anchor.focus();
12938             }catch(e){}
12939         }else if(!Roo.isIE){
12940             try{
12941                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12942                 var l = noscroll.scrollLeft;
12943                 this.anchor.focus();
12944                 noscroll.scrollLeft = l;
12945             }catch(e){}
12946         }
12947     },
12948
12949     toggleCheck : function(value){
12950         var cb = this.checkbox;
12951         if(cb){
12952             cb.checked = (value === undefined ? !cb.checked : value);
12953         }
12954     },
12955
12956     blur : function(){
12957         try{
12958             this.anchor.blur();
12959         }catch(e){}
12960     },
12961
12962     animExpand : function(callback){
12963         var ct = Roo.get(this.ctNode);
12964         ct.stopFx();
12965         if(!this.node.hasChildNodes()){
12966             this.updateExpandIcon();
12967             this.ctNode.style.display = "";
12968             Roo.callback(callback);
12969             return;
12970         }
12971         this.animating = true;
12972         this.updateExpandIcon();
12973
12974         ct.slideIn('t', {
12975            callback : function(){
12976                this.animating = false;
12977                Roo.callback(callback);
12978             },
12979             scope: this,
12980             duration: this.node.ownerTree.duration || .25
12981         });
12982     },
12983
12984     highlight : function(){
12985         var tree = this.node.getOwnerTree();
12986         Roo.fly(this.wrap).highlight(
12987             tree.hlColor || "C3DAF9",
12988             {endColor: tree.hlBaseColor}
12989         );
12990     },
12991
12992     collapse : function(){
12993         this.updateExpandIcon();
12994         this.ctNode.style.display = "none";
12995     },
12996
12997     animCollapse : function(callback){
12998         var ct = Roo.get(this.ctNode);
12999         ct.enableDisplayMode('block');
13000         ct.stopFx();
13001
13002         this.animating = true;
13003         this.updateExpandIcon();
13004
13005         ct.slideOut('t', {
13006             callback : function(){
13007                this.animating = false;
13008                Roo.callback(callback);
13009             },
13010             scope: this,
13011             duration: this.node.ownerTree.duration || .25
13012         });
13013     },
13014
13015     getContainer : function(){
13016         return this.ctNode;
13017     },
13018
13019     getEl : function(){
13020         return this.wrap;
13021     },
13022
13023     appendDDGhost : function(ghostNode){
13024         ghostNode.appendChild(this.elNode.cloneNode(true));
13025     },
13026
13027     getDDRepairXY : function(){
13028         return Roo.lib.Dom.getXY(this.iconNode);
13029     },
13030
13031     onRender : function(){
13032         this.render();
13033     },
13034
13035     render : function(bulkRender){
13036         var n = this.node, a = n.attributes;
13037         var targetNode = n.parentNode ?
13038               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13039
13040         if(!this.rendered){
13041             this.rendered = true;
13042
13043             this.renderElements(n, a, targetNode, bulkRender);
13044
13045             if(a.qtip){
13046                if(this.textNode.setAttributeNS){
13047                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13048                    if(a.qtipTitle){
13049                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13050                    }
13051                }else{
13052                    this.textNode.setAttribute("ext:qtip", a.qtip);
13053                    if(a.qtipTitle){
13054                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13055                    }
13056                }
13057             }else if(a.qtipCfg){
13058                 a.qtipCfg.target = Roo.id(this.textNode);
13059                 Roo.QuickTips.register(a.qtipCfg);
13060             }
13061             this.initEvents();
13062             if(!this.node.expanded){
13063                 this.updateExpandIcon();
13064             }
13065         }else{
13066             if(bulkRender === true) {
13067                 targetNode.appendChild(this.wrap);
13068             }
13069         }
13070     },
13071
13072     renderElements : function(n, a, targetNode, bulkRender)
13073     {
13074         // add some indent caching, this helps performance when rendering a large tree
13075         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13076         var t = n.getOwnerTree();
13077         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13078         if (typeof(n.attributes.html) != 'undefined') {
13079             txt = n.attributes.html;
13080         }
13081         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13082         var cb = typeof a.checked == 'boolean';
13083         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13084         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13085             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13086             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13087             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13088             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13089             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13090              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13091                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13092             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13093             "</li>"];
13094
13095         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13096             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13097                                 n.nextSibling.ui.getEl(), buf.join(""));
13098         }else{
13099             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13100         }
13101
13102         this.elNode = this.wrap.childNodes[0];
13103         this.ctNode = this.wrap.childNodes[1];
13104         var cs = this.elNode.childNodes;
13105         this.indentNode = cs[0];
13106         this.ecNode = cs[1];
13107         this.iconNode = cs[2];
13108         var index = 3;
13109         if(cb){
13110             this.checkbox = cs[3];
13111             index++;
13112         }
13113         this.anchor = cs[index];
13114         this.textNode = cs[index].firstChild;
13115     },
13116
13117     getAnchor : function(){
13118         return this.anchor;
13119     },
13120
13121     getTextEl : function(){
13122         return this.textNode;
13123     },
13124
13125     getIconEl : function(){
13126         return this.iconNode;
13127     },
13128
13129     isChecked : function(){
13130         return this.checkbox ? this.checkbox.checked : false;
13131     },
13132
13133     updateExpandIcon : function(){
13134         if(this.rendered){
13135             var n = this.node, c1, c2;
13136             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13137             var hasChild = n.hasChildNodes();
13138             if(hasChild){
13139                 if(n.expanded){
13140                     cls += "-minus";
13141                     c1 = "x-tree-node-collapsed";
13142                     c2 = "x-tree-node-expanded";
13143                 }else{
13144                     cls += "-plus";
13145                     c1 = "x-tree-node-expanded";
13146                     c2 = "x-tree-node-collapsed";
13147                 }
13148                 if(this.wasLeaf){
13149                     this.removeClass("x-tree-node-leaf");
13150                     this.wasLeaf = false;
13151                 }
13152                 if(this.c1 != c1 || this.c2 != c2){
13153                     Roo.fly(this.elNode).replaceClass(c1, c2);
13154                     this.c1 = c1; this.c2 = c2;
13155                 }
13156             }else{
13157                 // this changes non-leafs into leafs if they have no children.
13158                 // it's not very rational behaviour..
13159                 
13160                 if(!this.wasLeaf && this.node.leaf){
13161                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13162                     delete this.c1;
13163                     delete this.c2;
13164                     this.wasLeaf = true;
13165                 }
13166             }
13167             var ecc = "x-tree-ec-icon "+cls;
13168             if(this.ecc != ecc){
13169                 this.ecNode.className = ecc;
13170                 this.ecc = ecc;
13171             }
13172         }
13173     },
13174
13175     getChildIndent : function(){
13176         if(!this.childIndent){
13177             var buf = [];
13178             var p = this.node;
13179             while(p){
13180                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13181                     if(!p.isLast()) {
13182                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13183                     } else {
13184                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13185                     }
13186                 }
13187                 p = p.parentNode;
13188             }
13189             this.childIndent = buf.join("");
13190         }
13191         return this.childIndent;
13192     },
13193
13194     renderIndent : function(){
13195         if(this.rendered){
13196             var indent = "";
13197             var p = this.node.parentNode;
13198             if(p){
13199                 indent = p.ui.getChildIndent();
13200             }
13201             if(this.indentMarkup != indent){ // don't rerender if not required
13202                 this.indentNode.innerHTML = indent;
13203                 this.indentMarkup = indent;
13204             }
13205             this.updateExpandIcon();
13206         }
13207     }
13208 };
13209
13210 Roo.tree.RootTreeNodeUI = function(){
13211     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13212 };
13213 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13214     render : function(){
13215         if(!this.rendered){
13216             var targetNode = this.node.ownerTree.innerCt.dom;
13217             this.node.expanded = true;
13218             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13219             this.wrap = this.ctNode = targetNode.firstChild;
13220         }
13221     },
13222     collapse : function(){
13223     },
13224     expand : function(){
13225     }
13226 });/*
13227  * Based on:
13228  * Ext JS Library 1.1.1
13229  * Copyright(c) 2006-2007, Ext JS, LLC.
13230  *
13231  * Originally Released Under LGPL - original licence link has changed is not relivant.
13232  *
13233  * Fork - LGPL
13234  * <script type="text/javascript">
13235  */
13236 /**
13237  * @class Roo.tree.TreeLoader
13238  * @extends Roo.util.Observable
13239  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13240  * nodes from a specified URL. The response must be a javascript Array definition
13241  * who's elements are node definition objects. eg:
13242  * <pre><code>
13243 {  success : true,
13244    data :      [
13245    
13246     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13247     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13248     ]
13249 }
13250
13251
13252 </code></pre>
13253  * <br><br>
13254  * The old style respose with just an array is still supported, but not recommended.
13255  * <br><br>
13256  *
13257  * A server request is sent, and child nodes are loaded only when a node is expanded.
13258  * The loading node's id is passed to the server under the parameter name "node" to
13259  * enable the server to produce the correct child nodes.
13260  * <br><br>
13261  * To pass extra parameters, an event handler may be attached to the "beforeload"
13262  * event, and the parameters specified in the TreeLoader's baseParams property:
13263  * <pre><code>
13264     myTreeLoader.on("beforeload", function(treeLoader, node) {
13265         this.baseParams.category = node.attributes.category;
13266     }, this);
13267     
13268 </code></pre>
13269  *
13270  * This would pass an HTTP parameter called "category" to the server containing
13271  * the value of the Node's "category" attribute.
13272  * @constructor
13273  * Creates a new Treeloader.
13274  * @param {Object} config A config object containing config properties.
13275  */
13276 Roo.tree.TreeLoader = function(config){
13277     this.baseParams = {};
13278     this.requestMethod = "POST";
13279     Roo.apply(this, config);
13280
13281     this.addEvents({
13282     
13283         /**
13284          * @event beforeload
13285          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13286          * @param {Object} This TreeLoader object.
13287          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13288          * @param {Object} callback The callback function specified in the {@link #load} call.
13289          */
13290         beforeload : true,
13291         /**
13292          * @event load
13293          * Fires when the node has been successfuly loaded.
13294          * @param {Object} This TreeLoader object.
13295          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13296          * @param {Object} response The response object containing the data from the server.
13297          */
13298         load : true,
13299         /**
13300          * @event loadexception
13301          * Fires if the network request failed.
13302          * @param {Object} This TreeLoader object.
13303          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13304          * @param {Object} response The response object containing the data from the server.
13305          */
13306         loadexception : true,
13307         /**
13308          * @event create
13309          * Fires before a node is created, enabling you to return custom Node types 
13310          * @param {Object} This TreeLoader object.
13311          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13312          */
13313         create : true
13314     });
13315
13316     Roo.tree.TreeLoader.superclass.constructor.call(this);
13317 };
13318
13319 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13320     /**
13321     * @cfg {String} dataUrl The URL from which to request a Json string which
13322     * specifies an array of node definition object representing the child nodes
13323     * to be loaded.
13324     */
13325     /**
13326     * @cfg {String} requestMethod either GET or POST
13327     * defaults to POST (due to BC)
13328     * to be loaded.
13329     */
13330     /**
13331     * @cfg {Object} baseParams (optional) An object containing properties which
13332     * specify HTTP parameters to be passed to each request for child nodes.
13333     */
13334     /**
13335     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13336     * created by this loader. If the attributes sent by the server have an attribute in this object,
13337     * they take priority.
13338     */
13339     /**
13340     * @cfg {Object} uiProviders (optional) An object containing properties which
13341     * 
13342     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13343     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13344     * <i>uiProvider</i> attribute of a returned child node is a string rather
13345     * than a reference to a TreeNodeUI implementation, this that string value
13346     * is used as a property name in the uiProviders object. You can define the provider named
13347     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13348     */
13349     uiProviders : {},
13350
13351     /**
13352     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13353     * child nodes before loading.
13354     */
13355     clearOnLoad : true,
13356
13357     /**
13358     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13359     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13360     * Grid query { data : [ .....] }
13361     */
13362     
13363     root : false,
13364      /**
13365     * @cfg {String} queryParam (optional) 
13366     * Name of the query as it will be passed on the querystring (defaults to 'node')
13367     * eg. the request will be ?node=[id]
13368     */
13369     
13370     
13371     queryParam: false,
13372     
13373     /**
13374      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13375      * This is called automatically when a node is expanded, but may be used to reload
13376      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13377      * @param {Roo.tree.TreeNode} node
13378      * @param {Function} callback
13379      */
13380     load : function(node, callback){
13381         if(this.clearOnLoad){
13382             while(node.firstChild){
13383                 node.removeChild(node.firstChild);
13384             }
13385         }
13386         if(node.attributes.children){ // preloaded json children
13387             var cs = node.attributes.children;
13388             for(var i = 0, len = cs.length; i < len; i++){
13389                 node.appendChild(this.createNode(cs[i]));
13390             }
13391             if(typeof callback == "function"){
13392                 callback();
13393             }
13394         }else if(this.dataUrl){
13395             this.requestData(node, callback);
13396         }
13397     },
13398
13399     getParams: function(node){
13400         var buf = [], bp = this.baseParams;
13401         for(var key in bp){
13402             if(typeof bp[key] != "function"){
13403                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13404             }
13405         }
13406         var n = this.queryParam === false ? 'node' : this.queryParam;
13407         buf.push(n + "=", encodeURIComponent(node.id));
13408         return buf.join("");
13409     },
13410
13411     requestData : function(node, callback){
13412         if(this.fireEvent("beforeload", this, node, callback) !== false){
13413             this.transId = Roo.Ajax.request({
13414                 method:this.requestMethod,
13415                 url: this.dataUrl||this.url,
13416                 success: this.handleResponse,
13417                 failure: this.handleFailure,
13418                 scope: this,
13419                 argument: {callback: callback, node: node},
13420                 params: this.getParams(node)
13421             });
13422         }else{
13423             // if the load is cancelled, make sure we notify
13424             // the node that we are done
13425             if(typeof callback == "function"){
13426                 callback();
13427             }
13428         }
13429     },
13430
13431     isLoading : function(){
13432         return this.transId ? true : false;
13433     },
13434
13435     abort : function(){
13436         if(this.isLoading()){
13437             Roo.Ajax.abort(this.transId);
13438         }
13439     },
13440
13441     // private
13442     createNode : function(attr)
13443     {
13444         // apply baseAttrs, nice idea Corey!
13445         if(this.baseAttrs){
13446             Roo.applyIf(attr, this.baseAttrs);
13447         }
13448         if(this.applyLoader !== false){
13449             attr.loader = this;
13450         }
13451         // uiProvider = depreciated..
13452         
13453         if(typeof(attr.uiProvider) == 'string'){
13454            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13455                 /**  eval:var:attr */ eval(attr.uiProvider);
13456         }
13457         if(typeof(this.uiProviders['default']) != 'undefined') {
13458             attr.uiProvider = this.uiProviders['default'];
13459         }
13460         
13461         this.fireEvent('create', this, attr);
13462         
13463         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13464         return(attr.leaf ?
13465                         new Roo.tree.TreeNode(attr) :
13466                         new Roo.tree.AsyncTreeNode(attr));
13467     },
13468
13469     processResponse : function(response, node, callback)
13470     {
13471         var json = response.responseText;
13472         try {
13473             
13474             var o = Roo.decode(json);
13475             
13476             if (this.root === false && typeof(o.success) != undefined) {
13477                 this.root = 'data'; // the default behaviour for list like data..
13478                 }
13479                 
13480             if (this.root !== false &&  !o.success) {
13481                 // it's a failure condition.
13482                 var a = response.argument;
13483                 this.fireEvent("loadexception", this, a.node, response);
13484                 Roo.log("Load failed - should have a handler really");
13485                 return;
13486             }
13487             
13488             
13489             
13490             if (this.root !== false) {
13491                  o = o[this.root];
13492             }
13493             
13494             for(var i = 0, len = o.length; i < len; i++){
13495                 var n = this.createNode(o[i]);
13496                 if(n){
13497                     node.appendChild(n);
13498                 }
13499             }
13500             if(typeof callback == "function"){
13501                 callback(this, node);
13502             }
13503         }catch(e){
13504             this.handleFailure(response);
13505         }
13506     },
13507
13508     handleResponse : function(response){
13509         this.transId = false;
13510         var a = response.argument;
13511         this.processResponse(response, a.node, a.callback);
13512         this.fireEvent("load", this, a.node, response);
13513     },
13514
13515     handleFailure : function(response)
13516     {
13517         // should handle failure better..
13518         this.transId = false;
13519         var a = response.argument;
13520         this.fireEvent("loadexception", this, a.node, response);
13521         if(typeof a.callback == "function"){
13522             a.callback(this, a.node);
13523         }
13524     }
13525 });/*
13526  * Based on:
13527  * Ext JS Library 1.1.1
13528  * Copyright(c) 2006-2007, Ext JS, LLC.
13529  *
13530  * Originally Released Under LGPL - original licence link has changed is not relivant.
13531  *
13532  * Fork - LGPL
13533  * <script type="text/javascript">
13534  */
13535
13536 /**
13537 * @class Roo.tree.TreeFilter
13538 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13539 * @param {TreePanel} tree
13540 * @param {Object} config (optional)
13541  */
13542 Roo.tree.TreeFilter = function(tree, config){
13543     this.tree = tree;
13544     this.filtered = {};
13545     Roo.apply(this, config);
13546 };
13547
13548 Roo.tree.TreeFilter.prototype = {
13549     clearBlank:false,
13550     reverse:false,
13551     autoClear:false,
13552     remove:false,
13553
13554      /**
13555      * Filter the data by a specific attribute.
13556      * @param {String/RegExp} value Either string that the attribute value
13557      * should start with or a RegExp to test against the attribute
13558      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13559      * @param {TreeNode} startNode (optional) The node to start the filter at.
13560      */
13561     filter : function(value, attr, startNode){
13562         attr = attr || "text";
13563         var f;
13564         if(typeof value == "string"){
13565             var vlen = value.length;
13566             // auto clear empty filter
13567             if(vlen == 0 && this.clearBlank){
13568                 this.clear();
13569                 return;
13570             }
13571             value = value.toLowerCase();
13572             f = function(n){
13573                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13574             };
13575         }else if(value.exec){ // regex?
13576             f = function(n){
13577                 return value.test(n.attributes[attr]);
13578             };
13579         }else{
13580             throw 'Illegal filter type, must be string or regex';
13581         }
13582         this.filterBy(f, null, startNode);
13583         },
13584
13585     /**
13586      * Filter by a function. The passed function will be called with each
13587      * node in the tree (or from the startNode). If the function returns true, the node is kept
13588      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13589      * @param {Function} fn The filter function
13590      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13591      */
13592     filterBy : function(fn, scope, startNode){
13593         startNode = startNode || this.tree.root;
13594         if(this.autoClear){
13595             this.clear();
13596         }
13597         var af = this.filtered, rv = this.reverse;
13598         var f = function(n){
13599             if(n == startNode){
13600                 return true;
13601             }
13602             if(af[n.id]){
13603                 return false;
13604             }
13605             var m = fn.call(scope || n, n);
13606             if(!m || rv){
13607                 af[n.id] = n;
13608                 n.ui.hide();
13609                 return false;
13610             }
13611             return true;
13612         };
13613         startNode.cascade(f);
13614         if(this.remove){
13615            for(var id in af){
13616                if(typeof id != "function"){
13617                    var n = af[id];
13618                    if(n && n.parentNode){
13619                        n.parentNode.removeChild(n);
13620                    }
13621                }
13622            }
13623         }
13624     },
13625
13626     /**
13627      * Clears the current filter. Note: with the "remove" option
13628      * set a filter cannot be cleared.
13629      */
13630     clear : function(){
13631         var t = this.tree;
13632         var af = this.filtered;
13633         for(var id in af){
13634             if(typeof id != "function"){
13635                 var n = af[id];
13636                 if(n){
13637                     n.ui.show();
13638                 }
13639             }
13640         }
13641         this.filtered = {};
13642     }
13643 };
13644 /*
13645  * Based on:
13646  * Ext JS Library 1.1.1
13647  * Copyright(c) 2006-2007, Ext JS, LLC.
13648  *
13649  * Originally Released Under LGPL - original licence link has changed is not relivant.
13650  *
13651  * Fork - LGPL
13652  * <script type="text/javascript">
13653  */
13654  
13655
13656 /**
13657  * @class Roo.tree.TreeSorter
13658  * Provides sorting of nodes in a TreePanel
13659  * 
13660  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13661  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13662  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13663  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13664  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13665  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13666  * @constructor
13667  * @param {TreePanel} tree
13668  * @param {Object} config
13669  */
13670 Roo.tree.TreeSorter = function(tree, config){
13671     Roo.apply(this, config);
13672     tree.on("beforechildrenrendered", this.doSort, this);
13673     tree.on("append", this.updateSort, this);
13674     tree.on("insert", this.updateSort, this);
13675     
13676     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13677     var p = this.property || "text";
13678     var sortType = this.sortType;
13679     var fs = this.folderSort;
13680     var cs = this.caseSensitive === true;
13681     var leafAttr = this.leafAttr || 'leaf';
13682
13683     this.sortFn = function(n1, n2){
13684         if(fs){
13685             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13686                 return 1;
13687             }
13688             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13689                 return -1;
13690             }
13691         }
13692         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13693         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13694         if(v1 < v2){
13695                         return dsc ? +1 : -1;
13696                 }else if(v1 > v2){
13697                         return dsc ? -1 : +1;
13698         }else{
13699                 return 0;
13700         }
13701     };
13702 };
13703
13704 Roo.tree.TreeSorter.prototype = {
13705     doSort : function(node){
13706         node.sort(this.sortFn);
13707     },
13708     
13709     compareNodes : function(n1, n2){
13710         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13711     },
13712     
13713     updateSort : function(tree, node){
13714         if(node.childrenRendered){
13715             this.doSort.defer(1, this, [node]);
13716         }
13717     }
13718 };/*
13719  * Based on:
13720  * Ext JS Library 1.1.1
13721  * Copyright(c) 2006-2007, Ext JS, LLC.
13722  *
13723  * Originally Released Under LGPL - original licence link has changed is not relivant.
13724  *
13725  * Fork - LGPL
13726  * <script type="text/javascript">
13727  */
13728
13729 if(Roo.dd.DropZone){
13730     
13731 Roo.tree.TreeDropZone = function(tree, config){
13732     this.allowParentInsert = false;
13733     this.allowContainerDrop = false;
13734     this.appendOnly = false;
13735     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13736     this.tree = tree;
13737     this.lastInsertClass = "x-tree-no-status";
13738     this.dragOverData = {};
13739 };
13740
13741 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13742     ddGroup : "TreeDD",
13743     scroll:  true,
13744     
13745     expandDelay : 1000,
13746     
13747     expandNode : function(node){
13748         if(node.hasChildNodes() && !node.isExpanded()){
13749             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13750         }
13751     },
13752     
13753     queueExpand : function(node){
13754         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13755     },
13756     
13757     cancelExpand : function(){
13758         if(this.expandProcId){
13759             clearTimeout(this.expandProcId);
13760             this.expandProcId = false;
13761         }
13762     },
13763     
13764     isValidDropPoint : function(n, pt, dd, e, data){
13765         if(!n || !data){ return false; }
13766         var targetNode = n.node;
13767         var dropNode = data.node;
13768         // default drop rules
13769         if(!(targetNode && targetNode.isTarget && pt)){
13770             return false;
13771         }
13772         if(pt == "append" && targetNode.allowChildren === false){
13773             return false;
13774         }
13775         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13776             return false;
13777         }
13778         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13779             return false;
13780         }
13781         // reuse the object
13782         var overEvent = this.dragOverData;
13783         overEvent.tree = this.tree;
13784         overEvent.target = targetNode;
13785         overEvent.data = data;
13786         overEvent.point = pt;
13787         overEvent.source = dd;
13788         overEvent.rawEvent = e;
13789         overEvent.dropNode = dropNode;
13790         overEvent.cancel = false;  
13791         var result = this.tree.fireEvent("nodedragover", overEvent);
13792         return overEvent.cancel === false && result !== false;
13793     },
13794     
13795     getDropPoint : function(e, n, dd)
13796     {
13797         var tn = n.node;
13798         if(tn.isRoot){
13799             return tn.allowChildren !== false ? "append" : false; // always append for root
13800         }
13801         var dragEl = n.ddel;
13802         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13803         var y = Roo.lib.Event.getPageY(e);
13804         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13805         
13806         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13807         var noAppend = tn.allowChildren === false;
13808         if(this.appendOnly || tn.parentNode.allowChildren === false){
13809             return noAppend ? false : "append";
13810         }
13811         var noBelow = false;
13812         if(!this.allowParentInsert){
13813             noBelow = tn.hasChildNodes() && tn.isExpanded();
13814         }
13815         var q = (b - t) / (noAppend ? 2 : 3);
13816         if(y >= t && y < (t + q)){
13817             return "above";
13818         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13819             return "below";
13820         }else{
13821             return "append";
13822         }
13823     },
13824     
13825     onNodeEnter : function(n, dd, e, data)
13826     {
13827         this.cancelExpand();
13828     },
13829     
13830     onNodeOver : function(n, dd, e, data)
13831     {
13832        
13833         var pt = this.getDropPoint(e, n, dd);
13834         var node = n.node;
13835         
13836         // auto node expand check
13837         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13838             this.queueExpand(node);
13839         }else if(pt != "append"){
13840             this.cancelExpand();
13841         }
13842         
13843         // set the insert point style on the target node
13844         var returnCls = this.dropNotAllowed;
13845         if(this.isValidDropPoint(n, pt, dd, e, data)){
13846            if(pt){
13847                var el = n.ddel;
13848                var cls;
13849                if(pt == "above"){
13850                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13851                    cls = "x-tree-drag-insert-above";
13852                }else if(pt == "below"){
13853                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13854                    cls = "x-tree-drag-insert-below";
13855                }else{
13856                    returnCls = "x-tree-drop-ok-append";
13857                    cls = "x-tree-drag-append";
13858                }
13859                if(this.lastInsertClass != cls){
13860                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13861                    this.lastInsertClass = cls;
13862                }
13863            }
13864        }
13865        return returnCls;
13866     },
13867     
13868     onNodeOut : function(n, dd, e, data){
13869         
13870         this.cancelExpand();
13871         this.removeDropIndicators(n);
13872     },
13873     
13874     onNodeDrop : function(n, dd, e, data){
13875         var point = this.getDropPoint(e, n, dd);
13876         var targetNode = n.node;
13877         targetNode.ui.startDrop();
13878         if(!this.isValidDropPoint(n, point, dd, e, data)){
13879             targetNode.ui.endDrop();
13880             return false;
13881         }
13882         // first try to find the drop node
13883         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13884         var dropEvent = {
13885             tree : this.tree,
13886             target: targetNode,
13887             data: data,
13888             point: point,
13889             source: dd,
13890             rawEvent: e,
13891             dropNode: dropNode,
13892             cancel: !dropNode   
13893         };
13894         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13895         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13896             targetNode.ui.endDrop();
13897             return false;
13898         }
13899         // allow target changing
13900         targetNode = dropEvent.target;
13901         if(point == "append" && !targetNode.isExpanded()){
13902             targetNode.expand(false, null, function(){
13903                 this.completeDrop(dropEvent);
13904             }.createDelegate(this));
13905         }else{
13906             this.completeDrop(dropEvent);
13907         }
13908         return true;
13909     },
13910     
13911     completeDrop : function(de){
13912         var ns = de.dropNode, p = de.point, t = de.target;
13913         if(!(ns instanceof Array)){
13914             ns = [ns];
13915         }
13916         var n;
13917         for(var i = 0, len = ns.length; i < len; i++){
13918             n = ns[i];
13919             if(p == "above"){
13920                 t.parentNode.insertBefore(n, t);
13921             }else if(p == "below"){
13922                 t.parentNode.insertBefore(n, t.nextSibling);
13923             }else{
13924                 t.appendChild(n);
13925             }
13926         }
13927         n.ui.focus();
13928         if(this.tree.hlDrop){
13929             n.ui.highlight();
13930         }
13931         t.ui.endDrop();
13932         this.tree.fireEvent("nodedrop", de);
13933     },
13934     
13935     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13936         if(this.tree.hlDrop){
13937             dropNode.ui.focus();
13938             dropNode.ui.highlight();
13939         }
13940         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13941     },
13942     
13943     getTree : function(){
13944         return this.tree;
13945     },
13946     
13947     removeDropIndicators : function(n){
13948         if(n && n.ddel){
13949             var el = n.ddel;
13950             Roo.fly(el).removeClass([
13951                     "x-tree-drag-insert-above",
13952                     "x-tree-drag-insert-below",
13953                     "x-tree-drag-append"]);
13954             this.lastInsertClass = "_noclass";
13955         }
13956     },
13957     
13958     beforeDragDrop : function(target, e, id){
13959         this.cancelExpand();
13960         return true;
13961     },
13962     
13963     afterRepair : function(data){
13964         if(data && Roo.enableFx){
13965             data.node.ui.highlight();
13966         }
13967         this.hideProxy();
13968     } 
13969     
13970 });
13971
13972 }
13973 /*
13974  * Based on:
13975  * Ext JS Library 1.1.1
13976  * Copyright(c) 2006-2007, Ext JS, LLC.
13977  *
13978  * Originally Released Under LGPL - original licence link has changed is not relivant.
13979  *
13980  * Fork - LGPL
13981  * <script type="text/javascript">
13982  */
13983  
13984
13985 if(Roo.dd.DragZone){
13986 Roo.tree.TreeDragZone = function(tree, config){
13987     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13988     this.tree = tree;
13989 };
13990
13991 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13992     ddGroup : "TreeDD",
13993    
13994     onBeforeDrag : function(data, e){
13995         var n = data.node;
13996         return n && n.draggable && !n.disabled;
13997     },
13998      
13999     
14000     onInitDrag : function(e){
14001         var data = this.dragData;
14002         this.tree.getSelectionModel().select(data.node);
14003         this.proxy.update("");
14004         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14005         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14006     },
14007     
14008     getRepairXY : function(e, data){
14009         return data.node.ui.getDDRepairXY();
14010     },
14011     
14012     onEndDrag : function(data, e){
14013         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14014         
14015         
14016     },
14017     
14018     onValidDrop : function(dd, e, id){
14019         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14020         this.hideProxy();
14021     },
14022     
14023     beforeInvalidDrop : function(e, id){
14024         // this scrolls the original position back into view
14025         var sm = this.tree.getSelectionModel();
14026         sm.clearSelections();
14027         sm.select(this.dragData.node);
14028     }
14029 });
14030 }/*
14031  * Based on:
14032  * Ext JS Library 1.1.1
14033  * Copyright(c) 2006-2007, Ext JS, LLC.
14034  *
14035  * Originally Released Under LGPL - original licence link has changed is not relivant.
14036  *
14037  * Fork - LGPL
14038  * <script type="text/javascript">
14039  */
14040 /**
14041  * @class Roo.tree.TreeEditor
14042  * @extends Roo.Editor
14043  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14044  * as the editor field.
14045  * @constructor
14046  * @param {Object} config (used to be the tree panel.)
14047  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14048  * 
14049  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14050  * @cfg {Roo.form.TextField} field [required] The field configuration
14051  *
14052  * 
14053  */
14054 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14055     var tree = config;
14056     var field;
14057     if (oldconfig) { // old style..
14058         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14059     } else {
14060         // new style..
14061         tree = config.tree;
14062         config.field = config.field  || {};
14063         config.field.xtype = 'TextField';
14064         field = Roo.factory(config.field, Roo.form);
14065     }
14066     config = config || {};
14067     
14068     
14069     this.addEvents({
14070         /**
14071          * @event beforenodeedit
14072          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14073          * false from the handler of this event.
14074          * @param {Editor} this
14075          * @param {Roo.tree.Node} node 
14076          */
14077         "beforenodeedit" : true
14078     });
14079     
14080     //Roo.log(config);
14081     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14082
14083     this.tree = tree;
14084
14085     tree.on('beforeclick', this.beforeNodeClick, this);
14086     tree.getTreeEl().on('mousedown', this.hide, this);
14087     this.on('complete', this.updateNode, this);
14088     this.on('beforestartedit', this.fitToTree, this);
14089     this.on('startedit', this.bindScroll, this, {delay:10});
14090     this.on('specialkey', this.onSpecialKey, this);
14091 };
14092
14093 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14094     /**
14095      * @cfg {String} alignment
14096      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14097      */
14098     alignment: "l-l",
14099     // inherit
14100     autoSize: false,
14101     /**
14102      * @cfg {Boolean} hideEl
14103      * True to hide the bound element while the editor is displayed (defaults to false)
14104      */
14105     hideEl : false,
14106     /**
14107      * @cfg {String} cls
14108      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14109      */
14110     cls: "x-small-editor x-tree-editor",
14111     /**
14112      * @cfg {Boolean} shim
14113      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14114      */
14115     shim:false,
14116     // inherit
14117     shadow:"frame",
14118     /**
14119      * @cfg {Number} maxWidth
14120      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14121      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14122      * scroll and client offsets into account prior to each edit.
14123      */
14124     maxWidth: 250,
14125
14126     editDelay : 350,
14127
14128     // private
14129     fitToTree : function(ed, el){
14130         var td = this.tree.getTreeEl().dom, nd = el.dom;
14131         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14132             td.scrollLeft = nd.offsetLeft;
14133         }
14134         var w = Math.min(
14135                 this.maxWidth,
14136                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14137         this.setSize(w, '');
14138         
14139         return this.fireEvent('beforenodeedit', this, this.editNode);
14140         
14141     },
14142
14143     // private
14144     triggerEdit : function(node){
14145         this.completeEdit();
14146         this.editNode = node;
14147         this.startEdit(node.ui.textNode, node.text);
14148     },
14149
14150     // private
14151     bindScroll : function(){
14152         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14153     },
14154
14155     // private
14156     beforeNodeClick : function(node, e){
14157         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14158         this.lastClick = new Date();
14159         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14160             e.stopEvent();
14161             this.triggerEdit(node);
14162             return false;
14163         }
14164         return true;
14165     },
14166
14167     // private
14168     updateNode : function(ed, value){
14169         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14170         this.editNode.setText(value);
14171     },
14172
14173     // private
14174     onHide : function(){
14175         Roo.tree.TreeEditor.superclass.onHide.call(this);
14176         if(this.editNode){
14177             this.editNode.ui.focus();
14178         }
14179     },
14180
14181     // private
14182     onSpecialKey : function(field, e){
14183         var k = e.getKey();
14184         if(k == e.ESC){
14185             e.stopEvent();
14186             this.cancelEdit();
14187         }else if(k == e.ENTER && !e.hasModifier()){
14188             e.stopEvent();
14189             this.completeEdit();
14190         }
14191     }
14192 });//<Script type="text/javascript">
14193 /*
14194  * Based on:
14195  * Ext JS Library 1.1.1
14196  * Copyright(c) 2006-2007, Ext JS, LLC.
14197  *
14198  * Originally Released Under LGPL - original licence link has changed is not relivant.
14199  *
14200  * Fork - LGPL
14201  * <script type="text/javascript">
14202  */
14203  
14204 /**
14205  * Not documented??? - probably should be...
14206  */
14207
14208 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14209     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14210     
14211     renderElements : function(n, a, targetNode, bulkRender){
14212         //consel.log("renderElements?");
14213         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14214
14215         var t = n.getOwnerTree();
14216         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14217         
14218         var cols = t.columns;
14219         var bw = t.borderWidth;
14220         var c = cols[0];
14221         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14222          var cb = typeof a.checked == "boolean";
14223         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14224         var colcls = 'x-t-' + tid + '-c0';
14225         var buf = [
14226             '<li class="x-tree-node">',
14227             
14228                 
14229                 '<div class="x-tree-node-el ', a.cls,'">',
14230                     // extran...
14231                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14232                 
14233                 
14234                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14235                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14236                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14237                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14238                            (a.iconCls ? ' '+a.iconCls : ''),
14239                            '" unselectable="on" />',
14240                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14241                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14242                              
14243                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14244                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14245                             '<span unselectable="on" qtip="' + tx + '">',
14246                              tx,
14247                              '</span></a>' ,
14248                     '</div>',
14249                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14250                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14251                  ];
14252         for(var i = 1, len = cols.length; i < len; i++){
14253             c = cols[i];
14254             colcls = 'x-t-' + tid + '-c' +i;
14255             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14256             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14257                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14258                       "</div>");
14259          }
14260          
14261          buf.push(
14262             '</a>',
14263             '<div class="x-clear"></div></div>',
14264             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14265             "</li>");
14266         
14267         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14268             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14269                                 n.nextSibling.ui.getEl(), buf.join(""));
14270         }else{
14271             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14272         }
14273         var el = this.wrap.firstChild;
14274         this.elRow = el;
14275         this.elNode = el.firstChild;
14276         this.ranchor = el.childNodes[1];
14277         this.ctNode = this.wrap.childNodes[1];
14278         var cs = el.firstChild.childNodes;
14279         this.indentNode = cs[0];
14280         this.ecNode = cs[1];
14281         this.iconNode = cs[2];
14282         var index = 3;
14283         if(cb){
14284             this.checkbox = cs[3];
14285             index++;
14286         }
14287         this.anchor = cs[index];
14288         
14289         this.textNode = cs[index].firstChild;
14290         
14291         //el.on("click", this.onClick, this);
14292         //el.on("dblclick", this.onDblClick, this);
14293         
14294         
14295        // console.log(this);
14296     },
14297     initEvents : function(){
14298         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14299         
14300             
14301         var a = this.ranchor;
14302
14303         var el = Roo.get(a);
14304
14305         if(Roo.isOpera){ // opera render bug ignores the CSS
14306             el.setStyle("text-decoration", "none");
14307         }
14308
14309         el.on("click", this.onClick, this);
14310         el.on("dblclick", this.onDblClick, this);
14311         el.on("contextmenu", this.onContextMenu, this);
14312         
14313     },
14314     
14315     /*onSelectedChange : function(state){
14316         if(state){
14317             this.focus();
14318             this.addClass("x-tree-selected");
14319         }else{
14320             //this.blur();
14321             this.removeClass("x-tree-selected");
14322         }
14323     },*/
14324     addClass : function(cls){
14325         if(this.elRow){
14326             Roo.fly(this.elRow).addClass(cls);
14327         }
14328         
14329     },
14330     
14331     
14332     removeClass : function(cls){
14333         if(this.elRow){
14334             Roo.fly(this.elRow).removeClass(cls);
14335         }
14336     }
14337
14338     
14339     
14340 });//<Script type="text/javascript">
14341
14342 /*
14343  * Based on:
14344  * Ext JS Library 1.1.1
14345  * Copyright(c) 2006-2007, Ext JS, LLC.
14346  *
14347  * Originally Released Under LGPL - original licence link has changed is not relivant.
14348  *
14349  * Fork - LGPL
14350  * <script type="text/javascript">
14351  */
14352  
14353
14354 /**
14355  * @class Roo.tree.ColumnTree
14356  * @extends Roo.tree.TreePanel
14357  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14358  * @cfg {int} borderWidth  compined right/left border allowance
14359  * @constructor
14360  * @param {String/HTMLElement/Element} el The container element
14361  * @param {Object} config
14362  */
14363 Roo.tree.ColumnTree =  function(el, config)
14364 {
14365    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14366    this.addEvents({
14367         /**
14368         * @event resize
14369         * Fire this event on a container when it resizes
14370         * @param {int} w Width
14371         * @param {int} h Height
14372         */
14373        "resize" : true
14374     });
14375     this.on('resize', this.onResize, this);
14376 };
14377
14378 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14379     //lines:false,
14380     
14381     
14382     borderWidth: Roo.isBorderBox ? 0 : 2, 
14383     headEls : false,
14384     
14385     render : function(){
14386         // add the header.....
14387        
14388         Roo.tree.ColumnTree.superclass.render.apply(this);
14389         
14390         this.el.addClass('x-column-tree');
14391         
14392         this.headers = this.el.createChild(
14393             {cls:'x-tree-headers'},this.innerCt.dom);
14394    
14395         var cols = this.columns, c;
14396         var totalWidth = 0;
14397         this.headEls = [];
14398         var  len = cols.length;
14399         for(var i = 0; i < len; i++){
14400              c = cols[i];
14401              totalWidth += c.width;
14402             this.headEls.push(this.headers.createChild({
14403                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14404                  cn: {
14405                      cls:'x-tree-hd-text',
14406                      html: c.header
14407                  },
14408                  style:'width:'+(c.width-this.borderWidth)+'px;'
14409              }));
14410         }
14411         this.headers.createChild({cls:'x-clear'});
14412         // prevent floats from wrapping when clipped
14413         this.headers.setWidth(totalWidth);
14414         //this.innerCt.setWidth(totalWidth);
14415         this.innerCt.setStyle({ overflow: 'auto' });
14416         this.onResize(this.width, this.height);
14417              
14418         
14419     },
14420     onResize : function(w,h)
14421     {
14422         this.height = h;
14423         this.width = w;
14424         // resize cols..
14425         this.innerCt.setWidth(this.width);
14426         this.innerCt.setHeight(this.height-20);
14427         
14428         // headers...
14429         var cols = this.columns, c;
14430         var totalWidth = 0;
14431         var expEl = false;
14432         var len = cols.length;
14433         for(var i = 0; i < len; i++){
14434             c = cols[i];
14435             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14436                 // it's the expander..
14437                 expEl  = this.headEls[i];
14438                 continue;
14439             }
14440             totalWidth += c.width;
14441             
14442         }
14443         if (expEl) {
14444             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14445         }
14446         this.headers.setWidth(w-20);
14447
14448         
14449         
14450         
14451     }
14452 });
14453 /*
14454  * Based on:
14455  * Ext JS Library 1.1.1
14456  * Copyright(c) 2006-2007, Ext JS, LLC.
14457  *
14458  * Originally Released Under LGPL - original licence link has changed is not relivant.
14459  *
14460  * Fork - LGPL
14461  * <script type="text/javascript">
14462  */
14463  
14464 /**
14465  * @class Roo.menu.Menu
14466  * @extends Roo.util.Observable
14467  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14468  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14469  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14470  * @constructor
14471  * Creates a new Menu
14472  * @param {Object} config Configuration options
14473  */
14474 Roo.menu.Menu = function(config){
14475     
14476     Roo.menu.Menu.superclass.constructor.call(this, config);
14477     
14478     this.id = this.id || Roo.id();
14479     this.addEvents({
14480         /**
14481          * @event beforeshow
14482          * Fires before this menu is displayed
14483          * @param {Roo.menu.Menu} this
14484          */
14485         beforeshow : true,
14486         /**
14487          * @event beforehide
14488          * Fires before this menu is hidden
14489          * @param {Roo.menu.Menu} this
14490          */
14491         beforehide : true,
14492         /**
14493          * @event show
14494          * Fires after this menu is displayed
14495          * @param {Roo.menu.Menu} this
14496          */
14497         show : true,
14498         /**
14499          * @event hide
14500          * Fires after this menu is hidden
14501          * @param {Roo.menu.Menu} this
14502          */
14503         hide : true,
14504         /**
14505          * @event click
14506          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14507          * @param {Roo.menu.Menu} this
14508          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14509          * @param {Roo.EventObject} e
14510          */
14511         click : true,
14512         /**
14513          * @event mouseover
14514          * Fires when the mouse is hovering over this menu
14515          * @param {Roo.menu.Menu} this
14516          * @param {Roo.EventObject} e
14517          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14518          */
14519         mouseover : true,
14520         /**
14521          * @event mouseout
14522          * Fires when the mouse exits this menu
14523          * @param {Roo.menu.Menu} this
14524          * @param {Roo.EventObject} e
14525          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14526          */
14527         mouseout : true,
14528         /**
14529          * @event itemclick
14530          * Fires when a menu item contained in this menu is clicked
14531          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14532          * @param {Roo.EventObject} e
14533          */
14534         itemclick: true
14535     });
14536     if (this.registerMenu) {
14537         Roo.menu.MenuMgr.register(this);
14538     }
14539     
14540     var mis = this.items;
14541     this.items = new Roo.util.MixedCollection();
14542     if(mis){
14543         this.add.apply(this, mis);
14544     }
14545 };
14546
14547 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14548     /**
14549      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14550      */
14551     minWidth : 120,
14552     /**
14553      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14554      * for bottom-right shadow (defaults to "sides")
14555      */
14556     shadow : "sides",
14557     /**
14558      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14559      * this menu (defaults to "tl-tr?")
14560      */
14561     subMenuAlign : "tl-tr?",
14562     /**
14563      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14564      * relative to its element of origin (defaults to "tl-bl?")
14565      */
14566     defaultAlign : "tl-bl?",
14567     /**
14568      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14569      */
14570     allowOtherMenus : false,
14571     /**
14572      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14573      */
14574     registerMenu : true,
14575
14576     hidden:true,
14577
14578     // private
14579     render : function(){
14580         if(this.el){
14581             return;
14582         }
14583         var el = this.el = new Roo.Layer({
14584             cls: "x-menu",
14585             shadow:this.shadow,
14586             constrain: false,
14587             parentEl: this.parentEl || document.body,
14588             zindex:15000
14589         });
14590
14591         this.keyNav = new Roo.menu.MenuNav(this);
14592
14593         if(this.plain){
14594             el.addClass("x-menu-plain");
14595         }
14596         if(this.cls){
14597             el.addClass(this.cls);
14598         }
14599         // generic focus element
14600         this.focusEl = el.createChild({
14601             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14602         });
14603         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14604         //disabling touch- as it's causing issues ..
14605         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14606         ul.on('click'   , this.onClick, this);
14607         
14608         
14609         ul.on("mouseover", this.onMouseOver, this);
14610         ul.on("mouseout", this.onMouseOut, this);
14611         this.items.each(function(item){
14612             if (item.hidden) {
14613                 return;
14614             }
14615             
14616             var li = document.createElement("li");
14617             li.className = "x-menu-list-item";
14618             ul.dom.appendChild(li);
14619             item.render(li, this);
14620         }, this);
14621         this.ul = ul;
14622         this.autoWidth();
14623     },
14624
14625     // private
14626     autoWidth : function(){
14627         var el = this.el, ul = this.ul;
14628         if(!el){
14629             return;
14630         }
14631         var w = this.width;
14632         if(w){
14633             el.setWidth(w);
14634         }else if(Roo.isIE){
14635             el.setWidth(this.minWidth);
14636             var t = el.dom.offsetWidth; // force recalc
14637             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14638         }
14639     },
14640
14641     // private
14642     delayAutoWidth : function(){
14643         if(this.rendered){
14644             if(!this.awTask){
14645                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14646             }
14647             this.awTask.delay(20);
14648         }
14649     },
14650
14651     // private
14652     findTargetItem : function(e){
14653         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14654         if(t && t.menuItemId){
14655             return this.items.get(t.menuItemId);
14656         }
14657     },
14658
14659     // private
14660     onClick : function(e){
14661         Roo.log("menu.onClick");
14662         var t = this.findTargetItem(e);
14663         if(!t){
14664             return;
14665         }
14666         Roo.log(e);
14667         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14668             if(t == this.activeItem && t.shouldDeactivate(e)){
14669                 this.activeItem.deactivate();
14670                 delete this.activeItem;
14671                 return;
14672             }
14673             if(t.canActivate){
14674                 this.setActiveItem(t, true);
14675             }
14676             return;
14677             
14678             
14679         }
14680         
14681         t.onClick(e);
14682         this.fireEvent("click", this, t, e);
14683     },
14684
14685     // private
14686     setActiveItem : function(item, autoExpand){
14687         if(item != this.activeItem){
14688             if(this.activeItem){
14689                 this.activeItem.deactivate();
14690             }
14691             this.activeItem = item;
14692             item.activate(autoExpand);
14693         }else if(autoExpand){
14694             item.expandMenu();
14695         }
14696     },
14697
14698     // private
14699     tryActivate : function(start, step){
14700         var items = this.items;
14701         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14702             var item = items.get(i);
14703             if(!item.disabled && item.canActivate){
14704                 this.setActiveItem(item, false);
14705                 return item;
14706             }
14707         }
14708         return false;
14709     },
14710
14711     // private
14712     onMouseOver : function(e){
14713         var t;
14714         if(t = this.findTargetItem(e)){
14715             if(t.canActivate && !t.disabled){
14716                 this.setActiveItem(t, true);
14717             }
14718         }
14719         this.fireEvent("mouseover", this, e, t);
14720     },
14721
14722     // private
14723     onMouseOut : function(e){
14724         var t;
14725         if(t = this.findTargetItem(e)){
14726             if(t == this.activeItem && t.shouldDeactivate(e)){
14727                 this.activeItem.deactivate();
14728                 delete this.activeItem;
14729             }
14730         }
14731         this.fireEvent("mouseout", this, e, t);
14732     },
14733
14734     /**
14735      * Read-only.  Returns true if the menu is currently displayed, else false.
14736      * @type Boolean
14737      */
14738     isVisible : function(){
14739         return this.el && !this.hidden;
14740     },
14741
14742     /**
14743      * Displays this menu relative to another element
14744      * @param {String/HTMLElement/Roo.Element} element The element to align to
14745      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14746      * the element (defaults to this.defaultAlign)
14747      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14748      */
14749     show : function(el, pos, parentMenu){
14750         this.parentMenu = parentMenu;
14751         if(!this.el){
14752             this.render();
14753         }
14754         this.fireEvent("beforeshow", this);
14755         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14756     },
14757
14758     /**
14759      * Displays this menu at a specific xy position
14760      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14761      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14762      */
14763     showAt : function(xy, parentMenu, /* private: */_e){
14764         this.parentMenu = parentMenu;
14765         if(!this.el){
14766             this.render();
14767         }
14768         if(_e !== false){
14769             this.fireEvent("beforeshow", this);
14770             xy = this.el.adjustForConstraints(xy);
14771         }
14772         this.el.setXY(xy);
14773         this.el.show();
14774         this.hidden = false;
14775         this.focus();
14776         this.fireEvent("show", this);
14777     },
14778
14779     focus : function(){
14780         if(!this.hidden){
14781             this.doFocus.defer(50, this);
14782         }
14783     },
14784
14785     doFocus : function(){
14786         if(!this.hidden){
14787             this.focusEl.focus();
14788         }
14789     },
14790
14791     /**
14792      * Hides this menu and optionally all parent menus
14793      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14794      */
14795     hide : function(deep){
14796         if(this.el && this.isVisible()){
14797             this.fireEvent("beforehide", this);
14798             if(this.activeItem){
14799                 this.activeItem.deactivate();
14800                 this.activeItem = null;
14801             }
14802             this.el.hide();
14803             this.hidden = true;
14804             this.fireEvent("hide", this);
14805         }
14806         if(deep === true && this.parentMenu){
14807             this.parentMenu.hide(true);
14808         }
14809     },
14810
14811     /**
14812      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14813      * Any of the following are valid:
14814      * <ul>
14815      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14816      * <li>An HTMLElement object which will be converted to a menu item</li>
14817      * <li>A menu item config object that will be created as a new menu item</li>
14818      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14819      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14820      * </ul>
14821      * Usage:
14822      * <pre><code>
14823 // Create the menu
14824 var menu = new Roo.menu.Menu();
14825
14826 // Create a menu item to add by reference
14827 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14828
14829 // Add a bunch of items at once using different methods.
14830 // Only the last item added will be returned.
14831 var item = menu.add(
14832     menuItem,                // add existing item by ref
14833     'Dynamic Item',          // new TextItem
14834     '-',                     // new separator
14835     { text: 'Config Item' }  // new item by config
14836 );
14837 </code></pre>
14838      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14839      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14840      */
14841     add : function(){
14842         var a = arguments, l = a.length, item;
14843         for(var i = 0; i < l; i++){
14844             var el = a[i];
14845             if ((typeof(el) == "object") && el.xtype && el.xns) {
14846                 el = Roo.factory(el, Roo.menu);
14847             }
14848             
14849             if(el.render){ // some kind of Item
14850                 item = this.addItem(el);
14851             }else if(typeof el == "string"){ // string
14852                 if(el == "separator" || el == "-"){
14853                     item = this.addSeparator();
14854                 }else{
14855                     item = this.addText(el);
14856                 }
14857             }else if(el.tagName || el.el){ // element
14858                 item = this.addElement(el);
14859             }else if(typeof el == "object"){ // must be menu item config?
14860                 item = this.addMenuItem(el);
14861             }
14862         }
14863         return item;
14864     },
14865
14866     /**
14867      * Returns this menu's underlying {@link Roo.Element} object
14868      * @return {Roo.Element} The element
14869      */
14870     getEl : function(){
14871         if(!this.el){
14872             this.render();
14873         }
14874         return this.el;
14875     },
14876
14877     /**
14878      * Adds a separator bar to the menu
14879      * @return {Roo.menu.Item} The menu item that was added
14880      */
14881     addSeparator : function(){
14882         return this.addItem(new Roo.menu.Separator());
14883     },
14884
14885     /**
14886      * Adds an {@link Roo.Element} object to the menu
14887      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14888      * @return {Roo.menu.Item} The menu item that was added
14889      */
14890     addElement : function(el){
14891         return this.addItem(new Roo.menu.BaseItem(el));
14892     },
14893
14894     /**
14895      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14896      * @param {Roo.menu.Item} item The menu item to add
14897      * @return {Roo.menu.Item} The menu item that was added
14898      */
14899     addItem : function(item){
14900         this.items.add(item);
14901         if(this.ul){
14902             var li = document.createElement("li");
14903             li.className = "x-menu-list-item";
14904             this.ul.dom.appendChild(li);
14905             item.render(li, this);
14906             this.delayAutoWidth();
14907         }
14908         return item;
14909     },
14910
14911     /**
14912      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14913      * @param {Object} config A MenuItem config object
14914      * @return {Roo.menu.Item} The menu item that was added
14915      */
14916     addMenuItem : function(config){
14917         if(!(config instanceof Roo.menu.Item)){
14918             if(typeof config.checked == "boolean"){ // must be check menu item config?
14919                 config = new Roo.menu.CheckItem(config);
14920             }else{
14921                 config = new Roo.menu.Item(config);
14922             }
14923         }
14924         return this.addItem(config);
14925     },
14926
14927     /**
14928      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14929      * @param {String} text The text to display in the menu item
14930      * @return {Roo.menu.Item} The menu item that was added
14931      */
14932     addText : function(text){
14933         return this.addItem(new Roo.menu.TextItem({ text : text }));
14934     },
14935
14936     /**
14937      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14938      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14939      * @param {Roo.menu.Item} item The menu item to add
14940      * @return {Roo.menu.Item} The menu item that was added
14941      */
14942     insert : function(index, item){
14943         this.items.insert(index, item);
14944         if(this.ul){
14945             var li = document.createElement("li");
14946             li.className = "x-menu-list-item";
14947             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14948             item.render(li, this);
14949             this.delayAutoWidth();
14950         }
14951         return item;
14952     },
14953
14954     /**
14955      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14956      * @param {Roo.menu.Item} item The menu item to remove
14957      */
14958     remove : function(item){
14959         this.items.removeKey(item.id);
14960         item.destroy();
14961     },
14962
14963     /**
14964      * Removes and destroys all items in the menu
14965      */
14966     removeAll : function(){
14967         var f;
14968         while(f = this.items.first()){
14969             this.remove(f);
14970         }
14971     }
14972 });
14973
14974 // MenuNav is a private utility class used internally by the Menu
14975 Roo.menu.MenuNav = function(menu){
14976     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14977     this.scope = this.menu = menu;
14978 };
14979
14980 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14981     doRelay : function(e, h){
14982         var k = e.getKey();
14983         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14984             this.menu.tryActivate(0, 1);
14985             return false;
14986         }
14987         return h.call(this.scope || this, e, this.menu);
14988     },
14989
14990     up : function(e, m){
14991         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14992             m.tryActivate(m.items.length-1, -1);
14993         }
14994     },
14995
14996     down : function(e, m){
14997         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14998             m.tryActivate(0, 1);
14999         }
15000     },
15001
15002     right : function(e, m){
15003         if(m.activeItem){
15004             m.activeItem.expandMenu(true);
15005         }
15006     },
15007
15008     left : function(e, m){
15009         m.hide();
15010         if(m.parentMenu && m.parentMenu.activeItem){
15011             m.parentMenu.activeItem.activate();
15012         }
15013     },
15014
15015     enter : function(e, m){
15016         if(m.activeItem){
15017             e.stopPropagation();
15018             m.activeItem.onClick(e);
15019             m.fireEvent("click", this, m.activeItem);
15020             return true;
15021         }
15022     }
15023 });/*
15024  * Based on:
15025  * Ext JS Library 1.1.1
15026  * Copyright(c) 2006-2007, Ext JS, LLC.
15027  *
15028  * Originally Released Under LGPL - original licence link has changed is not relivant.
15029  *
15030  * Fork - LGPL
15031  * <script type="text/javascript">
15032  */
15033  
15034 /**
15035  * @class Roo.menu.MenuMgr
15036  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15037  * @static
15038  */
15039 Roo.menu.MenuMgr = function(){
15040    var menus, active, groups = {}, attached = false, lastShow = new Date();
15041
15042    // private - called when first menu is created
15043    function init(){
15044        menus = {};
15045        active = new Roo.util.MixedCollection();
15046        Roo.get(document).addKeyListener(27, function(){
15047            if(active.length > 0){
15048                hideAll();
15049            }
15050        });
15051    }
15052
15053    // private
15054    function hideAll(){
15055        if(active && active.length > 0){
15056            var c = active.clone();
15057            c.each(function(m){
15058                m.hide();
15059            });
15060        }
15061    }
15062
15063    // private
15064    function onHide(m){
15065        active.remove(m);
15066        if(active.length < 1){
15067            Roo.get(document).un("mousedown", onMouseDown);
15068            attached = false;
15069        }
15070    }
15071
15072    // private
15073    function onShow(m){
15074        var last = active.last();
15075        lastShow = new Date();
15076        active.add(m);
15077        if(!attached){
15078            Roo.get(document).on("mousedown", onMouseDown);
15079            attached = true;
15080        }
15081        if(m.parentMenu){
15082           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15083           m.parentMenu.activeChild = m;
15084        }else if(last && last.isVisible()){
15085           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15086        }
15087    }
15088
15089    // private
15090    function onBeforeHide(m){
15091        if(m.activeChild){
15092            m.activeChild.hide();
15093        }
15094        if(m.autoHideTimer){
15095            clearTimeout(m.autoHideTimer);
15096            delete m.autoHideTimer;
15097        }
15098    }
15099
15100    // private
15101    function onBeforeShow(m){
15102        var pm = m.parentMenu;
15103        if(!pm && !m.allowOtherMenus){
15104            hideAll();
15105        }else if(pm && pm.activeChild && active != m){
15106            pm.activeChild.hide();
15107        }
15108    }
15109
15110    // private
15111    function onMouseDown(e){
15112        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15113            hideAll();
15114        }
15115    }
15116
15117    // private
15118    function onBeforeCheck(mi, state){
15119        if(state){
15120            var g = groups[mi.group];
15121            for(var i = 0, l = g.length; i < l; i++){
15122                if(g[i] != mi){
15123                    g[i].setChecked(false);
15124                }
15125            }
15126        }
15127    }
15128
15129    return {
15130
15131        /**
15132         * Hides all menus that are currently visible
15133         */
15134        hideAll : function(){
15135             hideAll();  
15136        },
15137
15138        // private
15139        register : function(menu){
15140            if(!menus){
15141                init();
15142            }
15143            menus[menu.id] = menu;
15144            menu.on("beforehide", onBeforeHide);
15145            menu.on("hide", onHide);
15146            menu.on("beforeshow", onBeforeShow);
15147            menu.on("show", onShow);
15148            var g = menu.group;
15149            if(g && menu.events["checkchange"]){
15150                if(!groups[g]){
15151                    groups[g] = [];
15152                }
15153                groups[g].push(menu);
15154                menu.on("checkchange", onCheck);
15155            }
15156        },
15157
15158         /**
15159          * Returns a {@link Roo.menu.Menu} object
15160          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15161          * be used to generate and return a new Menu instance.
15162          */
15163        get : function(menu){
15164            if(typeof menu == "string"){ // menu id
15165                return menus[menu];
15166            }else if(menu.events){  // menu instance
15167                return menu;
15168            }else if(typeof menu.length == 'number'){ // array of menu items?
15169                return new Roo.menu.Menu({items:menu});
15170            }else{ // otherwise, must be a config
15171                return new Roo.menu.Menu(menu);
15172            }
15173        },
15174
15175        // private
15176        unregister : function(menu){
15177            delete menus[menu.id];
15178            menu.un("beforehide", onBeforeHide);
15179            menu.un("hide", onHide);
15180            menu.un("beforeshow", onBeforeShow);
15181            menu.un("show", onShow);
15182            var g = menu.group;
15183            if(g && menu.events["checkchange"]){
15184                groups[g].remove(menu);
15185                menu.un("checkchange", onCheck);
15186            }
15187        },
15188
15189        // private
15190        registerCheckable : function(menuItem){
15191            var g = menuItem.group;
15192            if(g){
15193                if(!groups[g]){
15194                    groups[g] = [];
15195                }
15196                groups[g].push(menuItem);
15197                menuItem.on("beforecheckchange", onBeforeCheck);
15198            }
15199        },
15200
15201        // private
15202        unregisterCheckable : function(menuItem){
15203            var g = menuItem.group;
15204            if(g){
15205                groups[g].remove(menuItem);
15206                menuItem.un("beforecheckchange", onBeforeCheck);
15207            }
15208        }
15209    };
15210 }();/*
15211  * Based on:
15212  * Ext JS Library 1.1.1
15213  * Copyright(c) 2006-2007, Ext JS, LLC.
15214  *
15215  * Originally Released Under LGPL - original licence link has changed is not relivant.
15216  *
15217  * Fork - LGPL
15218  * <script type="text/javascript">
15219  */
15220  
15221
15222 /**
15223  * @class Roo.menu.BaseItem
15224  * @extends Roo.Component
15225  * @abstract
15226  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15227  * management and base configuration options shared by all menu components.
15228  * @constructor
15229  * Creates a new BaseItem
15230  * @param {Object} config Configuration options
15231  */
15232 Roo.menu.BaseItem = function(config){
15233     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15234
15235     this.addEvents({
15236         /**
15237          * @event click
15238          * Fires when this item is clicked
15239          * @param {Roo.menu.BaseItem} this
15240          * @param {Roo.EventObject} e
15241          */
15242         click: true,
15243         /**
15244          * @event activate
15245          * Fires when this item is activated
15246          * @param {Roo.menu.BaseItem} this
15247          */
15248         activate : true,
15249         /**
15250          * @event deactivate
15251          * Fires when this item is deactivated
15252          * @param {Roo.menu.BaseItem} this
15253          */
15254         deactivate : true
15255     });
15256
15257     if(this.handler){
15258         this.on("click", this.handler, this.scope, true);
15259     }
15260 };
15261
15262 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15263     /**
15264      * @cfg {Function} handler
15265      * A function that will handle the click event of this menu item (defaults to undefined)
15266      */
15267     /**
15268      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15269      */
15270     canActivate : false,
15271     
15272      /**
15273      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15274      */
15275     hidden: false,
15276     
15277     /**
15278      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15279      */
15280     activeClass : "x-menu-item-active",
15281     /**
15282      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15283      */
15284     hideOnClick : true,
15285     /**
15286      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15287      */
15288     hideDelay : 100,
15289
15290     // private
15291     ctype: "Roo.menu.BaseItem",
15292
15293     // private
15294     actionMode : "container",
15295
15296     // private
15297     render : function(container, parentMenu){
15298         this.parentMenu = parentMenu;
15299         Roo.menu.BaseItem.superclass.render.call(this, container);
15300         this.container.menuItemId = this.id;
15301     },
15302
15303     // private
15304     onRender : function(container, position){
15305         this.el = Roo.get(this.el);
15306         container.dom.appendChild(this.el.dom);
15307     },
15308
15309     // private
15310     onClick : function(e){
15311         if(!this.disabled && this.fireEvent("click", this, e) !== false
15312                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15313             this.handleClick(e);
15314         }else{
15315             e.stopEvent();
15316         }
15317     },
15318
15319     // private
15320     activate : function(){
15321         if(this.disabled){
15322             return false;
15323         }
15324         var li = this.container;
15325         li.addClass(this.activeClass);
15326         this.region = li.getRegion().adjust(2, 2, -2, -2);
15327         this.fireEvent("activate", this);
15328         return true;
15329     },
15330
15331     // private
15332     deactivate : function(){
15333         this.container.removeClass(this.activeClass);
15334         this.fireEvent("deactivate", this);
15335     },
15336
15337     // private
15338     shouldDeactivate : function(e){
15339         return !this.region || !this.region.contains(e.getPoint());
15340     },
15341
15342     // private
15343     handleClick : function(e){
15344         if(this.hideOnClick){
15345             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15346         }
15347     },
15348
15349     // private
15350     expandMenu : function(autoActivate){
15351         // do nothing
15352     },
15353
15354     // private
15355     hideMenu : function(){
15356         // do nothing
15357     }
15358 });/*
15359  * Based on:
15360  * Ext JS Library 1.1.1
15361  * Copyright(c) 2006-2007, Ext JS, LLC.
15362  *
15363  * Originally Released Under LGPL - original licence link has changed is not relivant.
15364  *
15365  * Fork - LGPL
15366  * <script type="text/javascript">
15367  */
15368  
15369 /**
15370  * @class Roo.menu.Adapter
15371  * @extends Roo.menu.BaseItem
15372  * @abstract
15373  * 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.
15374  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15375  * @constructor
15376  * Creates a new Adapter
15377  * @param {Object} config Configuration options
15378  */
15379 Roo.menu.Adapter = function(component, config){
15380     Roo.menu.Adapter.superclass.constructor.call(this, config);
15381     this.component = component;
15382 };
15383 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15384     // private
15385     canActivate : true,
15386
15387     // private
15388     onRender : function(container, position){
15389         this.component.render(container);
15390         this.el = this.component.getEl();
15391     },
15392
15393     // private
15394     activate : function(){
15395         if(this.disabled){
15396             return false;
15397         }
15398         this.component.focus();
15399         this.fireEvent("activate", this);
15400         return true;
15401     },
15402
15403     // private
15404     deactivate : function(){
15405         this.fireEvent("deactivate", this);
15406     },
15407
15408     // private
15409     disable : function(){
15410         this.component.disable();
15411         Roo.menu.Adapter.superclass.disable.call(this);
15412     },
15413
15414     // private
15415     enable : function(){
15416         this.component.enable();
15417         Roo.menu.Adapter.superclass.enable.call(this);
15418     }
15419 });/*
15420  * Based on:
15421  * Ext JS Library 1.1.1
15422  * Copyright(c) 2006-2007, Ext JS, LLC.
15423  *
15424  * Originally Released Under LGPL - original licence link has changed is not relivant.
15425  *
15426  * Fork - LGPL
15427  * <script type="text/javascript">
15428  */
15429
15430 /**
15431  * @class Roo.menu.TextItem
15432  * @extends Roo.menu.BaseItem
15433  * Adds a static text string to a menu, usually used as either a heading or group separator.
15434  * Note: old style constructor with text is still supported.
15435  * 
15436  * @constructor
15437  * Creates a new TextItem
15438  * @param {Object} cfg Configuration
15439  */
15440 Roo.menu.TextItem = function(cfg){
15441     if (typeof(cfg) == 'string') {
15442         this.text = cfg;
15443     } else {
15444         Roo.apply(this,cfg);
15445     }
15446     
15447     Roo.menu.TextItem.superclass.constructor.call(this);
15448 };
15449
15450 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15451     /**
15452      * @cfg {String} text Text to show on item.
15453      */
15454     text : '',
15455     
15456     /**
15457      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15458      */
15459     hideOnClick : false,
15460     /**
15461      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15462      */
15463     itemCls : "x-menu-text",
15464
15465     // private
15466     onRender : function(){
15467         var s = document.createElement("span");
15468         s.className = this.itemCls;
15469         s.innerHTML = this.text;
15470         this.el = s;
15471         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15472     }
15473 });/*
15474  * Based on:
15475  * Ext JS Library 1.1.1
15476  * Copyright(c) 2006-2007, Ext JS, LLC.
15477  *
15478  * Originally Released Under LGPL - original licence link has changed is not relivant.
15479  *
15480  * Fork - LGPL
15481  * <script type="text/javascript">
15482  */
15483
15484 /**
15485  * @class Roo.menu.Separator
15486  * @extends Roo.menu.BaseItem
15487  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15488  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15489  * @constructor
15490  * @param {Object} config Configuration options
15491  */
15492 Roo.menu.Separator = function(config){
15493     Roo.menu.Separator.superclass.constructor.call(this, config);
15494 };
15495
15496 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15497     /**
15498      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15499      */
15500     itemCls : "x-menu-sep",
15501     /**
15502      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15503      */
15504     hideOnClick : false,
15505
15506     // private
15507     onRender : function(li){
15508         var s = document.createElement("span");
15509         s.className = this.itemCls;
15510         s.innerHTML = "&#160;";
15511         this.el = s;
15512         li.addClass("x-menu-sep-li");
15513         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15514     }
15515 });/*
15516  * Based on:
15517  * Ext JS Library 1.1.1
15518  * Copyright(c) 2006-2007, Ext JS, LLC.
15519  *
15520  * Originally Released Under LGPL - original licence link has changed is not relivant.
15521  *
15522  * Fork - LGPL
15523  * <script type="text/javascript">
15524  */
15525 /**
15526  * @class Roo.menu.Item
15527  * @extends Roo.menu.BaseItem
15528  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15529  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15530  * activation and click handling.
15531  * @constructor
15532  * Creates a new Item
15533  * @param {Object} config Configuration options
15534  */
15535 Roo.menu.Item = function(config){
15536     Roo.menu.Item.superclass.constructor.call(this, config);
15537     if(this.menu){
15538         this.menu = Roo.menu.MenuMgr.get(this.menu);
15539     }
15540 };
15541 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15542     /**
15543      * @cfg {Roo.menu.Menu} menu
15544      * A Sub menu
15545      */
15546     /**
15547      * @cfg {String} text
15548      * The text to show on the menu item.
15549      */
15550     text: '',
15551      /**
15552      * @cfg {String} html to render in menu
15553      * The text to show on the menu item (HTML version).
15554      */
15555     html: '',
15556     /**
15557      * @cfg {String} icon
15558      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15559      */
15560     icon: undefined,
15561     /**
15562      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15563      */
15564     itemCls : "x-menu-item",
15565     /**
15566      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15567      */
15568     canActivate : true,
15569     /**
15570      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15571      */
15572     showDelay: 200,
15573     // doc'd in BaseItem
15574     hideDelay: 200,
15575
15576     // private
15577     ctype: "Roo.menu.Item",
15578     
15579     // private
15580     onRender : function(container, position){
15581         var el = document.createElement("a");
15582         el.hideFocus = true;
15583         el.unselectable = "on";
15584         el.href = this.href || "#";
15585         if(this.hrefTarget){
15586             el.target = this.hrefTarget;
15587         }
15588         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15589         
15590         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15591         
15592         el.innerHTML = String.format(
15593                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15594                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15595         this.el = el;
15596         Roo.menu.Item.superclass.onRender.call(this, container, position);
15597     },
15598
15599     /**
15600      * Sets the text to display in this menu item
15601      * @param {String} text The text to display
15602      * @param {Boolean} isHTML true to indicate text is pure html.
15603      */
15604     setText : function(text, isHTML){
15605         if (isHTML) {
15606             this.html = text;
15607         } else {
15608             this.text = text;
15609             this.html = '';
15610         }
15611         if(this.rendered){
15612             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15613      
15614             this.el.update(String.format(
15615                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15616                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15617             this.parentMenu.autoWidth();
15618         }
15619     },
15620
15621     // private
15622     handleClick : function(e){
15623         if(!this.href){ // if no link defined, stop the event automatically
15624             e.stopEvent();
15625         }
15626         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15627     },
15628
15629     // private
15630     activate : function(autoExpand){
15631         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15632             this.focus();
15633             if(autoExpand){
15634                 this.expandMenu();
15635             }
15636         }
15637         return true;
15638     },
15639
15640     // private
15641     shouldDeactivate : function(e){
15642         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15643             if(this.menu && this.menu.isVisible()){
15644                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15645             }
15646             return true;
15647         }
15648         return false;
15649     },
15650
15651     // private
15652     deactivate : function(){
15653         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15654         this.hideMenu();
15655     },
15656
15657     // private
15658     expandMenu : function(autoActivate){
15659         if(!this.disabled && this.menu){
15660             clearTimeout(this.hideTimer);
15661             delete this.hideTimer;
15662             if(!this.menu.isVisible() && !this.showTimer){
15663                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15664             }else if (this.menu.isVisible() && autoActivate){
15665                 this.menu.tryActivate(0, 1);
15666             }
15667         }
15668     },
15669
15670     // private
15671     deferExpand : function(autoActivate){
15672         delete this.showTimer;
15673         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15674         if(autoActivate){
15675             this.menu.tryActivate(0, 1);
15676         }
15677     },
15678
15679     // private
15680     hideMenu : function(){
15681         clearTimeout(this.showTimer);
15682         delete this.showTimer;
15683         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15684             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15685         }
15686     },
15687
15688     // private
15689     deferHide : function(){
15690         delete this.hideTimer;
15691         this.menu.hide();
15692     }
15693 });/*
15694  * Based on:
15695  * Ext JS Library 1.1.1
15696  * Copyright(c) 2006-2007, Ext JS, LLC.
15697  *
15698  * Originally Released Under LGPL - original licence link has changed is not relivant.
15699  *
15700  * Fork - LGPL
15701  * <script type="text/javascript">
15702  */
15703  
15704 /**
15705  * @class Roo.menu.CheckItem
15706  * @extends Roo.menu.Item
15707  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15708  * @constructor
15709  * Creates a new CheckItem
15710  * @param {Object} config Configuration options
15711  */
15712 Roo.menu.CheckItem = function(config){
15713     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15714     this.addEvents({
15715         /**
15716          * @event beforecheckchange
15717          * Fires before the checked value is set, providing an opportunity to cancel if needed
15718          * @param {Roo.menu.CheckItem} this
15719          * @param {Boolean} checked The new checked value that will be set
15720          */
15721         "beforecheckchange" : true,
15722         /**
15723          * @event checkchange
15724          * Fires after the checked value has been set
15725          * @param {Roo.menu.CheckItem} this
15726          * @param {Boolean} checked The checked value that was set
15727          */
15728         "checkchange" : true
15729     });
15730     if(this.checkHandler){
15731         this.on('checkchange', this.checkHandler, this.scope);
15732     }
15733 };
15734 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15735     /**
15736      * @cfg {String} group
15737      * All check items with the same group name will automatically be grouped into a single-select
15738      * radio button group (defaults to '')
15739      */
15740     /**
15741      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15742      */
15743     itemCls : "x-menu-item x-menu-check-item",
15744     /**
15745      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15746      */
15747     groupClass : "x-menu-group-item",
15748
15749     /**
15750      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15751      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15752      * initialized with checked = true will be rendered as checked.
15753      */
15754     checked: false,
15755
15756     // private
15757     ctype: "Roo.menu.CheckItem",
15758
15759     // private
15760     onRender : function(c){
15761         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15762         if(this.group){
15763             this.el.addClass(this.groupClass);
15764         }
15765         Roo.menu.MenuMgr.registerCheckable(this);
15766         if(this.checked){
15767             this.checked = false;
15768             this.setChecked(true, true);
15769         }
15770     },
15771
15772     // private
15773     destroy : function(){
15774         if(this.rendered){
15775             Roo.menu.MenuMgr.unregisterCheckable(this);
15776         }
15777         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15778     },
15779
15780     /**
15781      * Set the checked state of this item
15782      * @param {Boolean} checked The new checked value
15783      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15784      */
15785     setChecked : function(state, suppressEvent){
15786         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15787             if(this.container){
15788                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15789             }
15790             this.checked = state;
15791             if(suppressEvent !== true){
15792                 this.fireEvent("checkchange", this, state);
15793             }
15794         }
15795     },
15796
15797     // private
15798     handleClick : function(e){
15799        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15800            this.setChecked(!this.checked);
15801        }
15802        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15803     }
15804 });/*
15805  * Based on:
15806  * Ext JS Library 1.1.1
15807  * Copyright(c) 2006-2007, Ext JS, LLC.
15808  *
15809  * Originally Released Under LGPL - original licence link has changed is not relivant.
15810  *
15811  * Fork - LGPL
15812  * <script type="text/javascript">
15813  */
15814  
15815 /**
15816  * @class Roo.menu.DateItem
15817  * @extends Roo.menu.Adapter
15818  * A menu item that wraps the {@link Roo.DatPicker} component.
15819  * @constructor
15820  * Creates a new DateItem
15821  * @param {Object} config Configuration options
15822  */
15823 Roo.menu.DateItem = function(config){
15824     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15825     /** The Roo.DatePicker object @type Roo.DatePicker */
15826     this.picker = this.component;
15827     this.addEvents({select: true});
15828     
15829     this.picker.on("render", function(picker){
15830         picker.getEl().swallowEvent("click");
15831         picker.container.addClass("x-menu-date-item");
15832     });
15833
15834     this.picker.on("select", this.onSelect, this);
15835 };
15836
15837 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15838     // private
15839     onSelect : function(picker, date){
15840         this.fireEvent("select", this, date, picker);
15841         Roo.menu.DateItem.superclass.handleClick.call(this);
15842     }
15843 });/*
15844  * Based on:
15845  * Ext JS Library 1.1.1
15846  * Copyright(c) 2006-2007, Ext JS, LLC.
15847  *
15848  * Originally Released Under LGPL - original licence link has changed is not relivant.
15849  *
15850  * Fork - LGPL
15851  * <script type="text/javascript">
15852  */
15853  
15854 /**
15855  * @class Roo.menu.ColorItem
15856  * @extends Roo.menu.Adapter
15857  * A menu item that wraps the {@link Roo.ColorPalette} component.
15858  * @constructor
15859  * Creates a new ColorItem
15860  * @param {Object} config Configuration options
15861  */
15862 Roo.menu.ColorItem = function(config){
15863     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15864     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15865     this.palette = this.component;
15866     this.relayEvents(this.palette, ["select"]);
15867     if(this.selectHandler){
15868         this.on('select', this.selectHandler, this.scope);
15869     }
15870 };
15871 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15872  * Based on:
15873  * Ext JS Library 1.1.1
15874  * Copyright(c) 2006-2007, Ext JS, LLC.
15875  *
15876  * Originally Released Under LGPL - original licence link has changed is not relivant.
15877  *
15878  * Fork - LGPL
15879  * <script type="text/javascript">
15880  */
15881  
15882
15883 /**
15884  * @class Roo.menu.DateMenu
15885  * @extends Roo.menu.Menu
15886  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15887  * @constructor
15888  * Creates a new DateMenu
15889  * @param {Object} config Configuration options
15890  */
15891 Roo.menu.DateMenu = function(config){
15892     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15893     this.plain = true;
15894     var di = new Roo.menu.DateItem(config);
15895     this.add(di);
15896     /**
15897      * The {@link Roo.DatePicker} instance for this DateMenu
15898      * @type DatePicker
15899      */
15900     this.picker = di.picker;
15901     /**
15902      * @event select
15903      * @param {DatePicker} picker
15904      * @param {Date} date
15905      */
15906     this.relayEvents(di, ["select"]);
15907     this.on('beforeshow', function(){
15908         if(this.picker){
15909             this.picker.hideMonthPicker(false);
15910         }
15911     }, this);
15912 };
15913 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15914     cls:'x-date-menu'
15915 });/*
15916  * Based on:
15917  * Ext JS Library 1.1.1
15918  * Copyright(c) 2006-2007, Ext JS, LLC.
15919  *
15920  * Originally Released Under LGPL - original licence link has changed is not relivant.
15921  *
15922  * Fork - LGPL
15923  * <script type="text/javascript">
15924  */
15925  
15926
15927 /**
15928  * @class Roo.menu.ColorMenu
15929  * @extends Roo.menu.Menu
15930  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15931  * @constructor
15932  * Creates a new ColorMenu
15933  * @param {Object} config Configuration options
15934  */
15935 Roo.menu.ColorMenu = function(config){
15936     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15937     this.plain = true;
15938     var ci = new Roo.menu.ColorItem(config);
15939     this.add(ci);
15940     /**
15941      * The {@link Roo.ColorPalette} instance for this ColorMenu
15942      * @type ColorPalette
15943      */
15944     this.palette = ci.palette;
15945     /**
15946      * @event select
15947      * @param {ColorPalette} palette
15948      * @param {String} color
15949      */
15950     this.relayEvents(ci, ["select"]);
15951 };
15952 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15953  * Based on:
15954  * Ext JS Library 1.1.1
15955  * Copyright(c) 2006-2007, Ext JS, LLC.
15956  *
15957  * Originally Released Under LGPL - original licence link has changed is not relivant.
15958  *
15959  * Fork - LGPL
15960  * <script type="text/javascript">
15961  */
15962  
15963 /**
15964  * @class Roo.form.TextItem
15965  * @extends Roo.BoxComponent
15966  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15967  * @constructor
15968  * Creates a new TextItem
15969  * @param {Object} config Configuration options
15970  */
15971 Roo.form.TextItem = function(config){
15972     Roo.form.TextItem.superclass.constructor.call(this, config);
15973 };
15974
15975 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15976     
15977     /**
15978      * @cfg {String} tag the tag for this item (default div)
15979      */
15980     tag : 'div',
15981     /**
15982      * @cfg {String} html the content for this item
15983      */
15984     html : '',
15985     
15986     getAutoCreate : function()
15987     {
15988         var cfg = {
15989             id: this.id,
15990             tag: this.tag,
15991             html: this.html,
15992             cls: 'x-form-item'
15993         };
15994         
15995         return cfg;
15996         
15997     },
15998     
15999     onRender : function(ct, position)
16000     {
16001         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16002         
16003         if(!this.el){
16004             var cfg = this.getAutoCreate();
16005             if(!cfg.name){
16006                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16007             }
16008             if (!cfg.name.length) {
16009                 delete cfg.name;
16010             }
16011             this.el = ct.createChild(cfg, position);
16012         }
16013     },
16014     /*
16015      * setHTML
16016      * @param {String} html update the Contents of the element.
16017      */
16018     setHTML : function(html)
16019     {
16020         this.fieldEl.dom.innerHTML = html;
16021     }
16022     
16023 });/*
16024  * Based on:
16025  * Ext JS Library 1.1.1
16026  * Copyright(c) 2006-2007, Ext JS, LLC.
16027  *
16028  * Originally Released Under LGPL - original licence link has changed is not relivant.
16029  *
16030  * Fork - LGPL
16031  * <script type="text/javascript">
16032  */
16033  
16034 /**
16035  * @class Roo.form.Field
16036  * @extends Roo.BoxComponent
16037  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16038  * @constructor
16039  * Creates a new Field
16040  * @param {Object} config Configuration options
16041  */
16042 Roo.form.Field = function(config){
16043     Roo.form.Field.superclass.constructor.call(this, config);
16044 };
16045
16046 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16047     /**
16048      * @cfg {String} fieldLabel Label to use when rendering a form.
16049      */
16050        /**
16051      * @cfg {String} qtip Mouse over tip
16052      */
16053      
16054     /**
16055      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16056      */
16057     invalidClass : "x-form-invalid",
16058     /**
16059      * @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")
16060      */
16061     invalidText : "The value in this field is invalid",
16062     /**
16063      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16064      */
16065     focusClass : "x-form-focus",
16066     /**
16067      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16068       automatic validation (defaults to "keyup").
16069      */
16070     validationEvent : "keyup",
16071     /**
16072      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16073      */
16074     validateOnBlur : true,
16075     /**
16076      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16077      */
16078     validationDelay : 250,
16079     /**
16080      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16081      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16082      */
16083     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16084     /**
16085      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16086      */
16087     fieldClass : "x-form-field",
16088     /**
16089      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16090      *<pre>
16091 Value         Description
16092 -----------   ----------------------------------------------------------------------
16093 qtip          Display a quick tip when the user hovers over the field
16094 title         Display a default browser title attribute popup
16095 under         Add a block div beneath the field containing the error text
16096 side          Add an error icon to the right of the field with a popup on hover
16097 [element id]  Add the error text directly to the innerHTML of the specified element
16098 </pre>
16099      */
16100     msgTarget : 'qtip',
16101     /**
16102      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16103      */
16104     msgFx : 'normal',
16105
16106     /**
16107      * @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.
16108      */
16109     readOnly : false,
16110
16111     /**
16112      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16113      */
16114     disabled : false,
16115
16116     /**
16117      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16118      */
16119     inputType : undefined,
16120     
16121     /**
16122      * @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).
16123          */
16124         tabIndex : undefined,
16125         
16126     // private
16127     isFormField : true,
16128
16129     // private
16130     hasFocus : false,
16131     /**
16132      * @property {Roo.Element} fieldEl
16133      * Element Containing the rendered Field (with label etc.)
16134      */
16135     /**
16136      * @cfg {Mixed} value A value to initialize this field with.
16137      */
16138     value : undefined,
16139
16140     /**
16141      * @cfg {String} name The field's HTML name attribute.
16142      */
16143     /**
16144      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16145      */
16146     // private
16147     loadedValue : false,
16148      
16149      
16150         // private ??
16151         initComponent : function(){
16152         Roo.form.Field.superclass.initComponent.call(this);
16153         this.addEvents({
16154             /**
16155              * @event focus
16156              * Fires when this field receives input focus.
16157              * @param {Roo.form.Field} this
16158              */
16159             focus : true,
16160             /**
16161              * @event blur
16162              * Fires when this field loses input focus.
16163              * @param {Roo.form.Field} this
16164              */
16165             blur : true,
16166             /**
16167              * @event specialkey
16168              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16169              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16170              * @param {Roo.form.Field} this
16171              * @param {Roo.EventObject} e The event object
16172              */
16173             specialkey : true,
16174             /**
16175              * @event change
16176              * Fires just before the field blurs if the field value has changed.
16177              * @param {Roo.form.Field} this
16178              * @param {Mixed} newValue The new value
16179              * @param {Mixed} oldValue The original value
16180              */
16181             change : true,
16182             /**
16183              * @event invalid
16184              * Fires after the field has been marked as invalid.
16185              * @param {Roo.form.Field} this
16186              * @param {String} msg The validation message
16187              */
16188             invalid : true,
16189             /**
16190              * @event valid
16191              * Fires after the field has been validated with no errors.
16192              * @param {Roo.form.Field} this
16193              */
16194             valid : true,
16195              /**
16196              * @event keyup
16197              * Fires after the key up
16198              * @param {Roo.form.Field} this
16199              * @param {Roo.EventObject}  e The event Object
16200              */
16201             keyup : true
16202         });
16203     },
16204
16205     /**
16206      * Returns the name attribute of the field if available
16207      * @return {String} name The field name
16208      */
16209     getName: function(){
16210          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16211     },
16212
16213     // private
16214     onRender : function(ct, position){
16215         Roo.form.Field.superclass.onRender.call(this, ct, position);
16216         if(!this.el){
16217             var cfg = this.getAutoCreate();
16218             if(!cfg.name){
16219                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16220             }
16221             if (!cfg.name.length) {
16222                 delete cfg.name;
16223             }
16224             if(this.inputType){
16225                 cfg.type = this.inputType;
16226             }
16227             this.el = ct.createChild(cfg, position);
16228         }
16229         var type = this.el.dom.type;
16230         if(type){
16231             if(type == 'password'){
16232                 type = 'text';
16233             }
16234             this.el.addClass('x-form-'+type);
16235         }
16236         if(this.readOnly){
16237             this.el.dom.readOnly = true;
16238         }
16239         if(this.tabIndex !== undefined){
16240             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16241         }
16242
16243         this.el.addClass([this.fieldClass, this.cls]);
16244         this.initValue();
16245     },
16246
16247     /**
16248      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16249      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16250      * @return {Roo.form.Field} this
16251      */
16252     applyTo : function(target){
16253         this.allowDomMove = false;
16254         this.el = Roo.get(target);
16255         this.render(this.el.dom.parentNode);
16256         return this;
16257     },
16258
16259     // private
16260     initValue : function(){
16261         if(this.value !== undefined){
16262             this.setValue(this.value);
16263         }else if(this.el.dom.value.length > 0){
16264             this.setValue(this.el.dom.value);
16265         }
16266     },
16267
16268     /**
16269      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16270      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16271      */
16272     isDirty : function() {
16273         if(this.disabled) {
16274             return false;
16275         }
16276         return String(this.getValue()) !== String(this.originalValue);
16277     },
16278
16279     /**
16280      * stores the current value in loadedValue
16281      */
16282     resetHasChanged : function()
16283     {
16284         this.loadedValue = String(this.getValue());
16285     },
16286     /**
16287      * checks the current value against the 'loaded' value.
16288      * Note - will return false if 'resetHasChanged' has not been called first.
16289      */
16290     hasChanged : function()
16291     {
16292         if(this.disabled || this.readOnly) {
16293             return false;
16294         }
16295         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16296     },
16297     
16298     
16299     
16300     // private
16301     afterRender : function(){
16302         Roo.form.Field.superclass.afterRender.call(this);
16303         this.initEvents();
16304     },
16305
16306     // private
16307     fireKey : function(e){
16308         //Roo.log('field ' + e.getKey());
16309         if(e.isNavKeyPress()){
16310             this.fireEvent("specialkey", this, e);
16311         }
16312     },
16313
16314     /**
16315      * Resets the current field value to the originally loaded value and clears any validation messages
16316      */
16317     reset : function(){
16318         this.setValue(this.resetValue);
16319         this.originalValue = this.getValue();
16320         this.clearInvalid();
16321     },
16322
16323     // private
16324     initEvents : function(){
16325         // safari killled keypress - so keydown is now used..
16326         this.el.on("keydown" , this.fireKey,  this);
16327         this.el.on("focus", this.onFocus,  this);
16328         this.el.on("blur", this.onBlur,  this);
16329         this.el.relayEvent('keyup', this);
16330
16331         // reference to original value for reset
16332         this.originalValue = this.getValue();
16333         this.resetValue =  this.getValue();
16334     },
16335
16336     // private
16337     onFocus : function(){
16338         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16339             this.el.addClass(this.focusClass);
16340         }
16341         if(!this.hasFocus){
16342             this.hasFocus = true;
16343             this.startValue = this.getValue();
16344             this.fireEvent("focus", this);
16345         }
16346     },
16347
16348     beforeBlur : Roo.emptyFn,
16349
16350     // private
16351     onBlur : function(){
16352         this.beforeBlur();
16353         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16354             this.el.removeClass(this.focusClass);
16355         }
16356         this.hasFocus = false;
16357         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16358             this.validate();
16359         }
16360         var v = this.getValue();
16361         if(String(v) !== String(this.startValue)){
16362             this.fireEvent('change', this, v, this.startValue);
16363         }
16364         this.fireEvent("blur", this);
16365     },
16366
16367     /**
16368      * Returns whether or not the field value is currently valid
16369      * @param {Boolean} preventMark True to disable marking the field invalid
16370      * @return {Boolean} True if the value is valid, else false
16371      */
16372     isValid : function(preventMark){
16373         if(this.disabled){
16374             return true;
16375         }
16376         var restore = this.preventMark;
16377         this.preventMark = preventMark === true;
16378         var v = this.validateValue(this.processValue(this.getRawValue()));
16379         this.preventMark = restore;
16380         return v;
16381     },
16382
16383     /**
16384      * Validates the field value
16385      * @return {Boolean} True if the value is valid, else false
16386      */
16387     validate : function(){
16388         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16389             this.clearInvalid();
16390             return true;
16391         }
16392         return false;
16393     },
16394
16395     processValue : function(value){
16396         return value;
16397     },
16398
16399     // private
16400     // Subclasses should provide the validation implementation by overriding this
16401     validateValue : function(value){
16402         return true;
16403     },
16404
16405     /**
16406      * Mark this field as invalid
16407      * @param {String} msg The validation message
16408      */
16409     markInvalid : function(msg){
16410         if(!this.rendered || this.preventMark){ // not rendered
16411             return;
16412         }
16413         
16414         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16415         
16416         obj.el.addClass(this.invalidClass);
16417         msg = msg || this.invalidText;
16418         switch(this.msgTarget){
16419             case 'qtip':
16420                 obj.el.dom.qtip = msg;
16421                 obj.el.dom.qclass = 'x-form-invalid-tip';
16422                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16423                     Roo.QuickTips.enable();
16424                 }
16425                 break;
16426             case 'title':
16427                 this.el.dom.title = msg;
16428                 break;
16429             case 'under':
16430                 if(!this.errorEl){
16431                     var elp = this.el.findParent('.x-form-element', 5, true);
16432                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16433                     this.errorEl.setWidth(elp.getWidth(true)-20);
16434                 }
16435                 this.errorEl.update(msg);
16436                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16437                 break;
16438             case 'side':
16439                 if(!this.errorIcon){
16440                     var elp = this.el.findParent('.x-form-element', 5, true);
16441                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16442                 }
16443                 this.alignErrorIcon();
16444                 this.errorIcon.dom.qtip = msg;
16445                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16446                 this.errorIcon.show();
16447                 this.on('resize', this.alignErrorIcon, this);
16448                 break;
16449             default:
16450                 var t = Roo.getDom(this.msgTarget);
16451                 t.innerHTML = msg;
16452                 t.style.display = this.msgDisplay;
16453                 break;
16454         }
16455         this.fireEvent('invalid', this, msg);
16456     },
16457
16458     // private
16459     alignErrorIcon : function(){
16460         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16461     },
16462
16463     /**
16464      * Clear any invalid styles/messages for this field
16465      */
16466     clearInvalid : function(){
16467         if(!this.rendered || this.preventMark){ // not rendered
16468             return;
16469         }
16470         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16471         
16472         obj.el.removeClass(this.invalidClass);
16473         switch(this.msgTarget){
16474             case 'qtip':
16475                 obj.el.dom.qtip = '';
16476                 break;
16477             case 'title':
16478                 this.el.dom.title = '';
16479                 break;
16480             case 'under':
16481                 if(this.errorEl){
16482                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16483                 }
16484                 break;
16485             case 'side':
16486                 if(this.errorIcon){
16487                     this.errorIcon.dom.qtip = '';
16488                     this.errorIcon.hide();
16489                     this.un('resize', this.alignErrorIcon, this);
16490                 }
16491                 break;
16492             default:
16493                 var t = Roo.getDom(this.msgTarget);
16494                 t.innerHTML = '';
16495                 t.style.display = 'none';
16496                 break;
16497         }
16498         this.fireEvent('valid', this);
16499     },
16500
16501     /**
16502      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16503      * @return {Mixed} value The field value
16504      */
16505     getRawValue : function(){
16506         var v = this.el.getValue();
16507         
16508         return v;
16509     },
16510
16511     /**
16512      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16513      * @return {Mixed} value The field value
16514      */
16515     getValue : function(){
16516         var v = this.el.getValue();
16517          
16518         return v;
16519     },
16520
16521     /**
16522      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16523      * @param {Mixed} value The value to set
16524      */
16525     setRawValue : function(v){
16526         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16527     },
16528
16529     /**
16530      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16531      * @param {Mixed} value The value to set
16532      */
16533     setValue : function(v){
16534         this.value = v;
16535         if(this.rendered){
16536             this.el.dom.value = (v === null || v === undefined ? '' : v);
16537              this.validate();
16538         }
16539     },
16540
16541     adjustSize : function(w, h){
16542         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16543         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16544         return s;
16545     },
16546
16547     adjustWidth : function(tag, w){
16548         tag = tag.toLowerCase();
16549         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16550             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16551                 if(tag == 'input'){
16552                     return w + 2;
16553                 }
16554                 if(tag == 'textarea'){
16555                     return w-2;
16556                 }
16557             }else if(Roo.isOpera){
16558                 if(tag == 'input'){
16559                     return w + 2;
16560                 }
16561                 if(tag == 'textarea'){
16562                     return w-2;
16563                 }
16564             }
16565         }
16566         return w;
16567     }
16568 });
16569
16570
16571 // anything other than normal should be considered experimental
16572 Roo.form.Field.msgFx = {
16573     normal : {
16574         show: function(msgEl, f){
16575             msgEl.setDisplayed('block');
16576         },
16577
16578         hide : function(msgEl, f){
16579             msgEl.setDisplayed(false).update('');
16580         }
16581     },
16582
16583     slide : {
16584         show: function(msgEl, f){
16585             msgEl.slideIn('t', {stopFx:true});
16586         },
16587
16588         hide : function(msgEl, f){
16589             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16590         }
16591     },
16592
16593     slideRight : {
16594         show: function(msgEl, f){
16595             msgEl.fixDisplay();
16596             msgEl.alignTo(f.el, 'tl-tr');
16597             msgEl.slideIn('l', {stopFx:true});
16598         },
16599
16600         hide : function(msgEl, f){
16601             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16602         }
16603     }
16604 };/*
16605  * Based on:
16606  * Ext JS Library 1.1.1
16607  * Copyright(c) 2006-2007, Ext JS, LLC.
16608  *
16609  * Originally Released Under LGPL - original licence link has changed is not relivant.
16610  *
16611  * Fork - LGPL
16612  * <script type="text/javascript">
16613  */
16614  
16615
16616 /**
16617  * @class Roo.form.TextField
16618  * @extends Roo.form.Field
16619  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16620  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16621  * @constructor
16622  * Creates a new TextField
16623  * @param {Object} config Configuration options
16624  */
16625 Roo.form.TextField = function(config){
16626     Roo.form.TextField.superclass.constructor.call(this, config);
16627     this.addEvents({
16628         /**
16629          * @event autosize
16630          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16631          * according to the default logic, but this event provides a hook for the developer to apply additional
16632          * logic at runtime to resize the field if needed.
16633              * @param {Roo.form.Field} this This text field
16634              * @param {Number} width The new field width
16635              */
16636         autosize : true
16637     });
16638 };
16639
16640 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16641     /**
16642      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16643      */
16644     grow : false,
16645     /**
16646      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16647      */
16648     growMin : 30,
16649     /**
16650      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16651      */
16652     growMax : 800,
16653     /**
16654      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16655      */
16656     vtype : null,
16657     /**
16658      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16659      */
16660     maskRe : null,
16661     /**
16662      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16663      */
16664     disableKeyFilter : false,
16665     /**
16666      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16667      */
16668     allowBlank : true,
16669     /**
16670      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16671      */
16672     minLength : 0,
16673     /**
16674      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16675      */
16676     maxLength : Number.MAX_VALUE,
16677     /**
16678      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16679      */
16680     minLengthText : "The minimum length for this field is {0}",
16681     /**
16682      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16683      */
16684     maxLengthText : "The maximum length for this field is {0}",
16685     /**
16686      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16687      */
16688     selectOnFocus : false,
16689     /**
16690      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16691      */    
16692     allowLeadingSpace : false,
16693     /**
16694      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16695      */
16696     blankText : "This field is required",
16697     /**
16698      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16699      * If available, this function will be called only after the basic validators all return true, and will be passed the
16700      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16701      */
16702     validator : null,
16703     /**
16704      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16705      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16706      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16707      */
16708     regex : null,
16709     /**
16710      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16711      */
16712     regexText : "",
16713     /**
16714      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16715      */
16716     emptyText : null,
16717    
16718
16719     // private
16720     initEvents : function()
16721     {
16722         if (this.emptyText) {
16723             this.el.attr('placeholder', this.emptyText);
16724         }
16725         
16726         Roo.form.TextField.superclass.initEvents.call(this);
16727         if(this.validationEvent == 'keyup'){
16728             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16729             this.el.on('keyup', this.filterValidation, this);
16730         }
16731         else if(this.validationEvent !== false){
16732             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16733         }
16734         
16735         if(this.selectOnFocus){
16736             this.on("focus", this.preFocus, this);
16737         }
16738         if (!this.allowLeadingSpace) {
16739             this.on('blur', this.cleanLeadingSpace, this);
16740         }
16741         
16742         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16743             this.el.on("keypress", this.filterKeys, this);
16744         }
16745         if(this.grow){
16746             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16747             this.el.on("click", this.autoSize,  this);
16748         }
16749         if(this.el.is('input[type=password]') && Roo.isSafari){
16750             this.el.on('keydown', this.SafariOnKeyDown, this);
16751         }
16752     },
16753
16754     processValue : function(value){
16755         if(this.stripCharsRe){
16756             var newValue = value.replace(this.stripCharsRe, '');
16757             if(newValue !== value){
16758                 this.setRawValue(newValue);
16759                 return newValue;
16760             }
16761         }
16762         return value;
16763     },
16764
16765     filterValidation : function(e){
16766         if(!e.isNavKeyPress()){
16767             this.validationTask.delay(this.validationDelay);
16768         }
16769     },
16770
16771     // private
16772     onKeyUp : function(e){
16773         if(!e.isNavKeyPress()){
16774             this.autoSize();
16775         }
16776     },
16777     // private - clean the leading white space
16778     cleanLeadingSpace : function(e)
16779     {
16780         if ( this.inputType == 'file') {
16781             return;
16782         }
16783         
16784         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16785     },
16786     /**
16787      * Resets the current field value to the originally-loaded value and clears any validation messages.
16788      *  
16789      */
16790     reset : function(){
16791         Roo.form.TextField.superclass.reset.call(this);
16792        
16793     }, 
16794     // private
16795     preFocus : function(){
16796         
16797         if(this.selectOnFocus){
16798             this.el.dom.select();
16799         }
16800     },
16801
16802     
16803     // private
16804     filterKeys : function(e){
16805         var k = e.getKey();
16806         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16807             return;
16808         }
16809         var c = e.getCharCode(), cc = String.fromCharCode(c);
16810         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16811             return;
16812         }
16813         if(!this.maskRe.test(cc)){
16814             e.stopEvent();
16815         }
16816     },
16817
16818     setValue : function(v){
16819         
16820         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16821         
16822         this.autoSize();
16823     },
16824
16825     /**
16826      * Validates a value according to the field's validation rules and marks the field as invalid
16827      * if the validation fails
16828      * @param {Mixed} value The value to validate
16829      * @return {Boolean} True if the value is valid, else false
16830      */
16831     validateValue : function(value){
16832         if(value.length < 1)  { // if it's blank
16833              if(this.allowBlank){
16834                 this.clearInvalid();
16835                 return true;
16836              }else{
16837                 this.markInvalid(this.blankText);
16838                 return false;
16839              }
16840         }
16841         if(value.length < this.minLength){
16842             this.markInvalid(String.format(this.minLengthText, this.minLength));
16843             return false;
16844         }
16845         if(value.length > this.maxLength){
16846             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16847             return false;
16848         }
16849         if(this.vtype){
16850             var vt = Roo.form.VTypes;
16851             if(!vt[this.vtype](value, this)){
16852                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16853                 return false;
16854             }
16855         }
16856         if(typeof this.validator == "function"){
16857             var msg = this.validator(value);
16858             if(msg !== true){
16859                 this.markInvalid(msg);
16860                 return false;
16861             }
16862         }
16863         if(this.regex && !this.regex.test(value)){
16864             this.markInvalid(this.regexText);
16865             return false;
16866         }
16867         return true;
16868     },
16869
16870     /**
16871      * Selects text in this field
16872      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16873      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16874      */
16875     selectText : function(start, end){
16876         var v = this.getRawValue();
16877         if(v.length > 0){
16878             start = start === undefined ? 0 : start;
16879             end = end === undefined ? v.length : end;
16880             var d = this.el.dom;
16881             if(d.setSelectionRange){
16882                 d.setSelectionRange(start, end);
16883             }else if(d.createTextRange){
16884                 var range = d.createTextRange();
16885                 range.moveStart("character", start);
16886                 range.moveEnd("character", v.length-end);
16887                 range.select();
16888             }
16889         }
16890     },
16891
16892     /**
16893      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16894      * This only takes effect if grow = true, and fires the autosize event.
16895      */
16896     autoSize : function(){
16897         if(!this.grow || !this.rendered){
16898             return;
16899         }
16900         if(!this.metrics){
16901             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16902         }
16903         var el = this.el;
16904         var v = el.dom.value;
16905         var d = document.createElement('div');
16906         d.appendChild(document.createTextNode(v));
16907         v = d.innerHTML;
16908         d = null;
16909         v += "&#160;";
16910         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16911         this.el.setWidth(w);
16912         this.fireEvent("autosize", this, w);
16913     },
16914     
16915     // private
16916     SafariOnKeyDown : function(event)
16917     {
16918         // this is a workaround for a password hang bug on chrome/ webkit.
16919         
16920         var isSelectAll = false;
16921         
16922         if(this.el.dom.selectionEnd > 0){
16923             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16924         }
16925         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16926             event.preventDefault();
16927             this.setValue('');
16928             return;
16929         }
16930         
16931         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16932             
16933             event.preventDefault();
16934             // this is very hacky as keydown always get's upper case.
16935             
16936             var cc = String.fromCharCode(event.getCharCode());
16937             
16938             
16939             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16940             
16941         }
16942         
16943         
16944     }
16945 });/*
16946  * Based on:
16947  * Ext JS Library 1.1.1
16948  * Copyright(c) 2006-2007, Ext JS, LLC.
16949  *
16950  * Originally Released Under LGPL - original licence link has changed is not relivant.
16951  *
16952  * Fork - LGPL
16953  * <script type="text/javascript">
16954  */
16955  
16956 /**
16957  * @class Roo.form.Hidden
16958  * @extends Roo.form.TextField
16959  * Simple Hidden element used on forms 
16960  * 
16961  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16962  * 
16963  * @constructor
16964  * Creates a new Hidden form element.
16965  * @param {Object} config Configuration options
16966  */
16967
16968
16969
16970 // easy hidden field...
16971 Roo.form.Hidden = function(config){
16972     Roo.form.Hidden.superclass.constructor.call(this, config);
16973 };
16974   
16975 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16976     fieldLabel:      '',
16977     inputType:      'hidden',
16978     width:          50,
16979     allowBlank:     true,
16980     labelSeparator: '',
16981     hidden:         true,
16982     itemCls :       'x-form-item-display-none'
16983
16984
16985 });
16986
16987
16988 /*
16989  * Based on:
16990  * Ext JS Library 1.1.1
16991  * Copyright(c) 2006-2007, Ext JS, LLC.
16992  *
16993  * Originally Released Under LGPL - original licence link has changed is not relivant.
16994  *
16995  * Fork - LGPL
16996  * <script type="text/javascript">
16997  */
16998  
16999 /**
17000  * @class Roo.form.TriggerField
17001  * @extends Roo.form.TextField
17002  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17003  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17004  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17005  * for which you can provide a custom implementation.  For example:
17006  * <pre><code>
17007 var trigger = new Roo.form.TriggerField();
17008 trigger.onTriggerClick = myTriggerFn;
17009 trigger.applyTo('my-field');
17010 </code></pre>
17011  *
17012  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17013  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17014  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17015  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17016  * @constructor
17017  * Create a new TriggerField.
17018  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17019  * to the base TextField)
17020  */
17021 Roo.form.TriggerField = function(config){
17022     this.mimicing = false;
17023     Roo.form.TriggerField.superclass.constructor.call(this, config);
17024 };
17025
17026 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17027     /**
17028      * @cfg {String} triggerClass A CSS class to apply to the trigger
17029      */
17030     /**
17031      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17032      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17033      */
17034     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17035     /**
17036      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17037      */
17038     hideTrigger:false,
17039
17040     /** @cfg {Boolean} grow @hide */
17041     /** @cfg {Number} growMin @hide */
17042     /** @cfg {Number} growMax @hide */
17043
17044     /**
17045      * @hide 
17046      * @method
17047      */
17048     autoSize: Roo.emptyFn,
17049     // private
17050     monitorTab : true,
17051     // private
17052     deferHeight : true,
17053
17054     
17055     actionMode : 'wrap',
17056     // private
17057     onResize : function(w, h){
17058         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17059         if(typeof w == 'number'){
17060             var x = w - this.trigger.getWidth();
17061             this.el.setWidth(this.adjustWidth('input', x));
17062             this.trigger.setStyle('left', x+'px');
17063         }
17064     },
17065
17066     // private
17067     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17068
17069     // private
17070     getResizeEl : function(){
17071         return this.wrap;
17072     },
17073
17074     // private
17075     getPositionEl : function(){
17076         return this.wrap;
17077     },
17078
17079     // private
17080     alignErrorIcon : function(){
17081         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17082     },
17083
17084     // private
17085     onRender : function(ct, position){
17086         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17087         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17088         this.trigger = this.wrap.createChild(this.triggerConfig ||
17089                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17090         if(this.hideTrigger){
17091             this.trigger.setDisplayed(false);
17092         }
17093         this.initTrigger();
17094         if(!this.width){
17095             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17096         }
17097     },
17098
17099     // private
17100     initTrigger : function(){
17101         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17102         this.trigger.addClassOnOver('x-form-trigger-over');
17103         this.trigger.addClassOnClick('x-form-trigger-click');
17104     },
17105
17106     // private
17107     onDestroy : function(){
17108         if(this.trigger){
17109             this.trigger.removeAllListeners();
17110             this.trigger.remove();
17111         }
17112         if(this.wrap){
17113             this.wrap.remove();
17114         }
17115         Roo.form.TriggerField.superclass.onDestroy.call(this);
17116     },
17117
17118     // private
17119     onFocus : function(){
17120         Roo.form.TriggerField.superclass.onFocus.call(this);
17121         if(!this.mimicing){
17122             this.wrap.addClass('x-trigger-wrap-focus');
17123             this.mimicing = true;
17124             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17125             if(this.monitorTab){
17126                 this.el.on("keydown", this.checkTab, this);
17127             }
17128         }
17129     },
17130
17131     // private
17132     checkTab : function(e){
17133         if(e.getKey() == e.TAB){
17134             this.triggerBlur();
17135         }
17136     },
17137
17138     // private
17139     onBlur : function(){
17140         // do nothing
17141     },
17142
17143     // private
17144     mimicBlur : function(e, t){
17145         if(!this.wrap.contains(t) && this.validateBlur()){
17146             this.triggerBlur();
17147         }
17148     },
17149
17150     // private
17151     triggerBlur : function(){
17152         this.mimicing = false;
17153         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17154         if(this.monitorTab){
17155             this.el.un("keydown", this.checkTab, this);
17156         }
17157         this.wrap.removeClass('x-trigger-wrap-focus');
17158         Roo.form.TriggerField.superclass.onBlur.call(this);
17159     },
17160
17161     // private
17162     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17163     validateBlur : function(e, t){
17164         return true;
17165     },
17166
17167     // private
17168     onDisable : function(){
17169         Roo.form.TriggerField.superclass.onDisable.call(this);
17170         if(this.wrap){
17171             this.wrap.addClass('x-item-disabled');
17172         }
17173     },
17174
17175     // private
17176     onEnable : function(){
17177         Roo.form.TriggerField.superclass.onEnable.call(this);
17178         if(this.wrap){
17179             this.wrap.removeClass('x-item-disabled');
17180         }
17181     },
17182
17183     // private
17184     onShow : function(){
17185         var ae = this.getActionEl();
17186         
17187         if(ae){
17188             ae.dom.style.display = '';
17189             ae.dom.style.visibility = 'visible';
17190         }
17191     },
17192
17193     // private
17194     
17195     onHide : function(){
17196         var ae = this.getActionEl();
17197         ae.dom.style.display = 'none';
17198     },
17199
17200     /**
17201      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17202      * by an implementing function.
17203      * @method
17204      * @param {EventObject} e
17205      */
17206     onTriggerClick : Roo.emptyFn
17207 });
17208
17209 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17210 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17211 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17212 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17213     initComponent : function(){
17214         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17215
17216         this.triggerConfig = {
17217             tag:'span', cls:'x-form-twin-triggers', cn:[
17218             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17219             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17220         ]};
17221     },
17222
17223     getTrigger : function(index){
17224         return this.triggers[index];
17225     },
17226
17227     initTrigger : function(){
17228         var ts = this.trigger.select('.x-form-trigger', true);
17229         this.wrap.setStyle('overflow', 'hidden');
17230         var triggerField = this;
17231         ts.each(function(t, all, index){
17232             t.hide = function(){
17233                 var w = triggerField.wrap.getWidth();
17234                 this.dom.style.display = 'none';
17235                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17236             };
17237             t.show = function(){
17238                 var w = triggerField.wrap.getWidth();
17239                 this.dom.style.display = '';
17240                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17241             };
17242             var triggerIndex = 'Trigger'+(index+1);
17243
17244             if(this['hide'+triggerIndex]){
17245                 t.dom.style.display = 'none';
17246             }
17247             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17248             t.addClassOnOver('x-form-trigger-over');
17249             t.addClassOnClick('x-form-trigger-click');
17250         }, this);
17251         this.triggers = ts.elements;
17252     },
17253
17254     onTrigger1Click : Roo.emptyFn,
17255     onTrigger2Click : Roo.emptyFn
17256 });/*
17257  * Based on:
17258  * Ext JS Library 1.1.1
17259  * Copyright(c) 2006-2007, Ext JS, LLC.
17260  *
17261  * Originally Released Under LGPL - original licence link has changed is not relivant.
17262  *
17263  * Fork - LGPL
17264  * <script type="text/javascript">
17265  */
17266  
17267 /**
17268  * @class Roo.form.TextArea
17269  * @extends Roo.form.TextField
17270  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17271  * support for auto-sizing.
17272  * @constructor
17273  * Creates a new TextArea
17274  * @param {Object} config Configuration options
17275  */
17276 Roo.form.TextArea = function(config){
17277     Roo.form.TextArea.superclass.constructor.call(this, config);
17278     // these are provided exchanges for backwards compat
17279     // minHeight/maxHeight were replaced by growMin/growMax to be
17280     // compatible with TextField growing config values
17281     if(this.minHeight !== undefined){
17282         this.growMin = this.minHeight;
17283     }
17284     if(this.maxHeight !== undefined){
17285         this.growMax = this.maxHeight;
17286     }
17287 };
17288
17289 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17290     /**
17291      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17292      */
17293     growMin : 60,
17294     /**
17295      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17296      */
17297     growMax: 1000,
17298     /**
17299      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17300      * in the field (equivalent to setting overflow: hidden, defaults to false)
17301      */
17302     preventScrollbars: false,
17303     /**
17304      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17305      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17306      */
17307
17308     // private
17309     onRender : function(ct, position){
17310         if(!this.el){
17311             this.defaultAutoCreate = {
17312                 tag: "textarea",
17313                 style:"width:300px;height:60px;",
17314                 autocomplete: "new-password"
17315             };
17316         }
17317         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17318         if(this.grow){
17319             this.textSizeEl = Roo.DomHelper.append(document.body, {
17320                 tag: "pre", cls: "x-form-grow-sizer"
17321             });
17322             if(this.preventScrollbars){
17323                 this.el.setStyle("overflow", "hidden");
17324             }
17325             this.el.setHeight(this.growMin);
17326         }
17327     },
17328
17329     onDestroy : function(){
17330         if(this.textSizeEl){
17331             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17332         }
17333         Roo.form.TextArea.superclass.onDestroy.call(this);
17334     },
17335
17336     // private
17337     onKeyUp : function(e){
17338         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17339             this.autoSize();
17340         }
17341     },
17342
17343     /**
17344      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17345      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17346      */
17347     autoSize : function(){
17348         if(!this.grow || !this.textSizeEl){
17349             return;
17350         }
17351         var el = this.el;
17352         var v = el.dom.value;
17353         var ts = this.textSizeEl;
17354
17355         ts.innerHTML = '';
17356         ts.appendChild(document.createTextNode(v));
17357         v = ts.innerHTML;
17358
17359         Roo.fly(ts).setWidth(this.el.getWidth());
17360         if(v.length < 1){
17361             v = "&#160;&#160;";
17362         }else{
17363             if(Roo.isIE){
17364                 v = v.replace(/\n/g, '<p>&#160;</p>');
17365             }
17366             v += "&#160;\n&#160;";
17367         }
17368         ts.innerHTML = v;
17369         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17370         if(h != this.lastHeight){
17371             this.lastHeight = h;
17372             this.el.setHeight(h);
17373             this.fireEvent("autosize", this, h);
17374         }
17375     }
17376 });/*
17377  * Based on:
17378  * Ext JS Library 1.1.1
17379  * Copyright(c) 2006-2007, Ext JS, LLC.
17380  *
17381  * Originally Released Under LGPL - original licence link has changed is not relivant.
17382  *
17383  * Fork - LGPL
17384  * <script type="text/javascript">
17385  */
17386  
17387
17388 /**
17389  * @class Roo.form.NumberField
17390  * @extends Roo.form.TextField
17391  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17392  * @constructor
17393  * Creates a new NumberField
17394  * @param {Object} config Configuration options
17395  */
17396 Roo.form.NumberField = function(config){
17397     Roo.form.NumberField.superclass.constructor.call(this, config);
17398 };
17399
17400 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17401     /**
17402      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17403      */
17404     fieldClass: "x-form-field x-form-num-field",
17405     /**
17406      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17407      */
17408     allowDecimals : true,
17409     /**
17410      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17411      */
17412     decimalSeparator : ".",
17413     /**
17414      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17415      */
17416     decimalPrecision : 2,
17417     /**
17418      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17419      */
17420     allowNegative : true,
17421     /**
17422      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17423      */
17424     minValue : Number.NEGATIVE_INFINITY,
17425     /**
17426      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17427      */
17428     maxValue : Number.MAX_VALUE,
17429     /**
17430      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17431      */
17432     minText : "The minimum value for this field is {0}",
17433     /**
17434      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17435      */
17436     maxText : "The maximum value for this field is {0}",
17437     /**
17438      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17439      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17440      */
17441     nanText : "{0} is not a valid number",
17442
17443     // private
17444     initEvents : function(){
17445         Roo.form.NumberField.superclass.initEvents.call(this);
17446         var allowed = "0123456789";
17447         if(this.allowDecimals){
17448             allowed += this.decimalSeparator;
17449         }
17450         if(this.allowNegative){
17451             allowed += "-";
17452         }
17453         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17454         var keyPress = function(e){
17455             var k = e.getKey();
17456             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17457                 return;
17458             }
17459             var c = e.getCharCode();
17460             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17461                 e.stopEvent();
17462             }
17463         };
17464         this.el.on("keypress", keyPress, this);
17465     },
17466
17467     // private
17468     validateValue : function(value){
17469         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17470             return false;
17471         }
17472         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17473              return true;
17474         }
17475         var num = this.parseValue(value);
17476         if(isNaN(num)){
17477             this.markInvalid(String.format(this.nanText, value));
17478             return false;
17479         }
17480         if(num < this.minValue){
17481             this.markInvalid(String.format(this.minText, this.minValue));
17482             return false;
17483         }
17484         if(num > this.maxValue){
17485             this.markInvalid(String.format(this.maxText, this.maxValue));
17486             return false;
17487         }
17488         return true;
17489     },
17490
17491     getValue : function(){
17492         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17493     },
17494
17495     // private
17496     parseValue : function(value){
17497         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17498         return isNaN(value) ? '' : value;
17499     },
17500
17501     // private
17502     fixPrecision : function(value){
17503         var nan = isNaN(value);
17504         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17505             return nan ? '' : value;
17506         }
17507         return parseFloat(value).toFixed(this.decimalPrecision);
17508     },
17509
17510     setValue : function(v){
17511         v = this.fixPrecision(v);
17512         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17513     },
17514
17515     // private
17516     decimalPrecisionFcn : function(v){
17517         return Math.floor(v);
17518     },
17519
17520     beforeBlur : function(){
17521         var v = this.parseValue(this.getRawValue());
17522         if(v){
17523             this.setValue(v);
17524         }
17525     }
17526 });/*
17527  * Based on:
17528  * Ext JS Library 1.1.1
17529  * Copyright(c) 2006-2007, Ext JS, LLC.
17530  *
17531  * Originally Released Under LGPL - original licence link has changed is not relivant.
17532  *
17533  * Fork - LGPL
17534  * <script type="text/javascript">
17535  */
17536  
17537 /**
17538  * @class Roo.form.DateField
17539  * @extends Roo.form.TriggerField
17540  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17541 * @constructor
17542 * Create a new DateField
17543 * @param {Object} config
17544  */
17545 Roo.form.DateField = function(config)
17546 {
17547     Roo.form.DateField.superclass.constructor.call(this, config);
17548     
17549       this.addEvents({
17550          
17551         /**
17552          * @event select
17553          * Fires when a date is selected
17554              * @param {Roo.form.DateField} combo This combo box
17555              * @param {Date} date The date selected
17556              */
17557         'select' : true
17558          
17559     });
17560     
17561     
17562     if(typeof this.minValue == "string") {
17563         this.minValue = this.parseDate(this.minValue);
17564     }
17565     if(typeof this.maxValue == "string") {
17566         this.maxValue = this.parseDate(this.maxValue);
17567     }
17568     this.ddMatch = null;
17569     if(this.disabledDates){
17570         var dd = this.disabledDates;
17571         var re = "(?:";
17572         for(var i = 0; i < dd.length; i++){
17573             re += dd[i];
17574             if(i != dd.length-1) {
17575                 re += "|";
17576             }
17577         }
17578         this.ddMatch = new RegExp(re + ")");
17579     }
17580 };
17581
17582 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17583     /**
17584      * @cfg {String} format
17585      * The default date format string which can be overriden for localization support.  The format must be
17586      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17587      */
17588     format : "m/d/y",
17589     /**
17590      * @cfg {String} altFormats
17591      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17592      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17593      */
17594     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17595     /**
17596      * @cfg {Array} disabledDays
17597      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17598      */
17599     disabledDays : null,
17600     /**
17601      * @cfg {String} disabledDaysText
17602      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17603      */
17604     disabledDaysText : "Disabled",
17605     /**
17606      * @cfg {Array} disabledDates
17607      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17608      * expression so they are very powerful. Some examples:
17609      * <ul>
17610      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17611      * <li>["03/08", "09/16"] would disable those days for every year</li>
17612      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17613      * <li>["03/../2006"] would disable every day in March 2006</li>
17614      * <li>["^03"] would disable every day in every March</li>
17615      * </ul>
17616      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17617      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17618      */
17619     disabledDates : null,
17620     /**
17621      * @cfg {String} disabledDatesText
17622      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17623      */
17624     disabledDatesText : "Disabled",
17625         
17626         
17627         /**
17628      * @cfg {Date/String} zeroValue
17629      * if the date is less that this number, then the field is rendered as empty
17630      * default is 1800
17631      */
17632         zeroValue : '1800-01-01',
17633         
17634         
17635     /**
17636      * @cfg {Date/String} minValue
17637      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17638      * valid format (defaults to null).
17639      */
17640     minValue : null,
17641     /**
17642      * @cfg {Date/String} maxValue
17643      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17644      * valid format (defaults to null).
17645      */
17646     maxValue : null,
17647     /**
17648      * @cfg {String} minText
17649      * The error text to display when the date in the cell is before minValue (defaults to
17650      * 'The date in this field must be after {minValue}').
17651      */
17652     minText : "The date in this field must be equal to or after {0}",
17653     /**
17654      * @cfg {String} maxText
17655      * The error text to display when the date in the cell is after maxValue (defaults to
17656      * 'The date in this field must be before {maxValue}').
17657      */
17658     maxText : "The date in this field must be equal to or before {0}",
17659     /**
17660      * @cfg {String} invalidText
17661      * The error text to display when the date in the field is invalid (defaults to
17662      * '{value} is not a valid date - it must be in the format {format}').
17663      */
17664     invalidText : "{0} is not a valid date - it must be in the format {1}",
17665     /**
17666      * @cfg {String} triggerClass
17667      * An additional CSS class used to style the trigger button.  The trigger will always get the
17668      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17669      * which displays a calendar icon).
17670      */
17671     triggerClass : 'x-form-date-trigger',
17672     
17673
17674     /**
17675      * @cfg {Boolean} useIso
17676      * if enabled, then the date field will use a hidden field to store the 
17677      * real value as iso formated date. default (false)
17678      */ 
17679     useIso : false,
17680     /**
17681      * @cfg {String/Object} autoCreate
17682      * A DomHelper element spec, or true for a default element spec (defaults to
17683      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17684      */ 
17685     // private
17686     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17687     
17688     // private
17689     hiddenField: false,
17690     
17691     onRender : function(ct, position)
17692     {
17693         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17694         if (this.useIso) {
17695             //this.el.dom.removeAttribute('name'); 
17696             Roo.log("Changing name?");
17697             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17698             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17699                     'before', true);
17700             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17701             // prevent input submission
17702             this.hiddenName = this.name;
17703         }
17704             
17705             
17706     },
17707     
17708     // private
17709     validateValue : function(value)
17710     {
17711         value = this.formatDate(value);
17712         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17713             Roo.log('super failed');
17714             return false;
17715         }
17716         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17717              return true;
17718         }
17719         var svalue = value;
17720         value = this.parseDate(value);
17721         if(!value){
17722             Roo.log('parse date failed' + svalue);
17723             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17724             return false;
17725         }
17726         var time = value.getTime();
17727         if(this.minValue && time < this.minValue.getTime()){
17728             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17729             return false;
17730         }
17731         if(this.maxValue && time > this.maxValue.getTime()){
17732             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17733             return false;
17734         }
17735         if(this.disabledDays){
17736             var day = value.getDay();
17737             for(var i = 0; i < this.disabledDays.length; i++) {
17738                 if(day === this.disabledDays[i]){
17739                     this.markInvalid(this.disabledDaysText);
17740                     return false;
17741                 }
17742             }
17743         }
17744         var fvalue = this.formatDate(value);
17745         if(this.ddMatch && this.ddMatch.test(fvalue)){
17746             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17747             return false;
17748         }
17749         return true;
17750     },
17751
17752     // private
17753     // Provides logic to override the default TriggerField.validateBlur which just returns true
17754     validateBlur : function(){
17755         return !this.menu || !this.menu.isVisible();
17756     },
17757     
17758     getName: function()
17759     {
17760         // returns hidden if it's set..
17761         if (!this.rendered) {return ''};
17762         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17763         
17764     },
17765
17766     /**
17767      * Returns the current date value of the date field.
17768      * @return {Date} The date value
17769      */
17770     getValue : function(){
17771         
17772         return  this.hiddenField ?
17773                 this.hiddenField.value :
17774                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17775     },
17776
17777     /**
17778      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17779      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17780      * (the default format used is "m/d/y").
17781      * <br />Usage:
17782      * <pre><code>
17783 //All of these calls set the same date value (May 4, 2006)
17784
17785 //Pass a date object:
17786 var dt = new Date('5/4/06');
17787 dateField.setValue(dt);
17788
17789 //Pass a date string (default format):
17790 dateField.setValue('5/4/06');
17791
17792 //Pass a date string (custom format):
17793 dateField.format = 'Y-m-d';
17794 dateField.setValue('2006-5-4');
17795 </code></pre>
17796      * @param {String/Date} date The date or valid date string
17797      */
17798     setValue : function(date){
17799         if (this.hiddenField) {
17800             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17801         }
17802         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17803         // make sure the value field is always stored as a date..
17804         this.value = this.parseDate(date);
17805         
17806         
17807     },
17808
17809     // private
17810     parseDate : function(value){
17811                 
17812                 if (value instanceof Date) {
17813                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17814                                 return  '';
17815                         }
17816                         return value;
17817                 }
17818                 
17819                 
17820         if(!value || value instanceof Date){
17821             return value;
17822         }
17823         var v = Date.parseDate(value, this.format);
17824          if (!v && this.useIso) {
17825             v = Date.parseDate(value, 'Y-m-d');
17826         }
17827         if(!v && this.altFormats){
17828             if(!this.altFormatsArray){
17829                 this.altFormatsArray = this.altFormats.split("|");
17830             }
17831             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17832                 v = Date.parseDate(value, this.altFormatsArray[i]);
17833             }
17834         }
17835                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17836                         v = '';
17837                 }
17838         return v;
17839     },
17840
17841     // private
17842     formatDate : function(date, fmt){
17843         return (!date || !(date instanceof Date)) ?
17844                date : date.dateFormat(fmt || this.format);
17845     },
17846
17847     // private
17848     menuListeners : {
17849         select: function(m, d){
17850             
17851             this.setValue(d);
17852             this.fireEvent('select', this, d);
17853         },
17854         show : function(){ // retain focus styling
17855             this.onFocus();
17856         },
17857         hide : function(){
17858             this.focus.defer(10, this);
17859             var ml = this.menuListeners;
17860             this.menu.un("select", ml.select,  this);
17861             this.menu.un("show", ml.show,  this);
17862             this.menu.un("hide", ml.hide,  this);
17863         }
17864     },
17865
17866     // private
17867     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17868     onTriggerClick : function(){
17869         if(this.disabled){
17870             return;
17871         }
17872         if(this.menu == null){
17873             this.menu = new Roo.menu.DateMenu();
17874         }
17875         Roo.apply(this.menu.picker,  {
17876             showClear: this.allowBlank,
17877             minDate : this.minValue,
17878             maxDate : this.maxValue,
17879             disabledDatesRE : this.ddMatch,
17880             disabledDatesText : this.disabledDatesText,
17881             disabledDays : this.disabledDays,
17882             disabledDaysText : this.disabledDaysText,
17883             format : this.useIso ? 'Y-m-d' : this.format,
17884             minText : String.format(this.minText, this.formatDate(this.minValue)),
17885             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17886         });
17887         this.menu.on(Roo.apply({}, this.menuListeners, {
17888             scope:this
17889         }));
17890         this.menu.picker.setValue(this.getValue() || new Date());
17891         this.menu.show(this.el, "tl-bl?");
17892     },
17893
17894     beforeBlur : function(){
17895         var v = this.parseDate(this.getRawValue());
17896         if(v){
17897             this.setValue(v);
17898         }
17899     },
17900
17901     /*@
17902      * overide
17903      * 
17904      */
17905     isDirty : function() {
17906         if(this.disabled) {
17907             return false;
17908         }
17909         
17910         if(typeof(this.startValue) === 'undefined'){
17911             return false;
17912         }
17913         
17914         return String(this.getValue()) !== String(this.startValue);
17915         
17916     },
17917     // @overide
17918     cleanLeadingSpace : function(e)
17919     {
17920        return;
17921     }
17922     
17923 });/*
17924  * Based on:
17925  * Ext JS Library 1.1.1
17926  * Copyright(c) 2006-2007, Ext JS, LLC.
17927  *
17928  * Originally Released Under LGPL - original licence link has changed is not relivant.
17929  *
17930  * Fork - LGPL
17931  * <script type="text/javascript">
17932  */
17933  
17934 /**
17935  * @class Roo.form.MonthField
17936  * @extends Roo.form.TriggerField
17937  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17938 * @constructor
17939 * Create a new MonthField
17940 * @param {Object} config
17941  */
17942 Roo.form.MonthField = function(config){
17943     
17944     Roo.form.MonthField.superclass.constructor.call(this, config);
17945     
17946       this.addEvents({
17947          
17948         /**
17949          * @event select
17950          * Fires when a date is selected
17951              * @param {Roo.form.MonthFieeld} combo This combo box
17952              * @param {Date} date The date selected
17953              */
17954         'select' : true
17955          
17956     });
17957     
17958     
17959     if(typeof this.minValue == "string") {
17960         this.minValue = this.parseDate(this.minValue);
17961     }
17962     if(typeof this.maxValue == "string") {
17963         this.maxValue = this.parseDate(this.maxValue);
17964     }
17965     this.ddMatch = null;
17966     if(this.disabledDates){
17967         var dd = this.disabledDates;
17968         var re = "(?:";
17969         for(var i = 0; i < dd.length; i++){
17970             re += dd[i];
17971             if(i != dd.length-1) {
17972                 re += "|";
17973             }
17974         }
17975         this.ddMatch = new RegExp(re + ")");
17976     }
17977 };
17978
17979 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17980     /**
17981      * @cfg {String} format
17982      * The default date format string which can be overriden for localization support.  The format must be
17983      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17984      */
17985     format : "M Y",
17986     /**
17987      * @cfg {String} altFormats
17988      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17989      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17990      */
17991     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17992     /**
17993      * @cfg {Array} disabledDays
17994      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17995      */
17996     disabledDays : [0,1,2,3,4,5,6],
17997     /**
17998      * @cfg {String} disabledDaysText
17999      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18000      */
18001     disabledDaysText : "Disabled",
18002     /**
18003      * @cfg {Array} disabledDates
18004      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18005      * expression so they are very powerful. Some examples:
18006      * <ul>
18007      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18008      * <li>["03/08", "09/16"] would disable those days for every year</li>
18009      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18010      * <li>["03/../2006"] would disable every day in March 2006</li>
18011      * <li>["^03"] would disable every day in every March</li>
18012      * </ul>
18013      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18014      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18015      */
18016     disabledDates : null,
18017     /**
18018      * @cfg {String} disabledDatesText
18019      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18020      */
18021     disabledDatesText : "Disabled",
18022     /**
18023      * @cfg {Date/String} minValue
18024      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18025      * valid format (defaults to null).
18026      */
18027     minValue : null,
18028     /**
18029      * @cfg {Date/String} maxValue
18030      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18031      * valid format (defaults to null).
18032      */
18033     maxValue : null,
18034     /**
18035      * @cfg {String} minText
18036      * The error text to display when the date in the cell is before minValue (defaults to
18037      * 'The date in this field must be after {minValue}').
18038      */
18039     minText : "The date in this field must be equal to or after {0}",
18040     /**
18041      * @cfg {String} maxTextf
18042      * The error text to display when the date in the cell is after maxValue (defaults to
18043      * 'The date in this field must be before {maxValue}').
18044      */
18045     maxText : "The date in this field must be equal to or before {0}",
18046     /**
18047      * @cfg {String} invalidText
18048      * The error text to display when the date in the field is invalid (defaults to
18049      * '{value} is not a valid date - it must be in the format {format}').
18050      */
18051     invalidText : "{0} is not a valid date - it must be in the format {1}",
18052     /**
18053      * @cfg {String} triggerClass
18054      * An additional CSS class used to style the trigger button.  The trigger will always get the
18055      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18056      * which displays a calendar icon).
18057      */
18058     triggerClass : 'x-form-date-trigger',
18059     
18060
18061     /**
18062      * @cfg {Boolean} useIso
18063      * if enabled, then the date field will use a hidden field to store the 
18064      * real value as iso formated date. default (true)
18065      */ 
18066     useIso : true,
18067     /**
18068      * @cfg {String/Object} autoCreate
18069      * A DomHelper element spec, or true for a default element spec (defaults to
18070      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18071      */ 
18072     // private
18073     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18074     
18075     // private
18076     hiddenField: false,
18077     
18078     hideMonthPicker : false,
18079     
18080     onRender : function(ct, position)
18081     {
18082         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18083         if (this.useIso) {
18084             this.el.dom.removeAttribute('name'); 
18085             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18086                     'before', true);
18087             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18088             // prevent input submission
18089             this.hiddenName = this.name;
18090         }
18091             
18092             
18093     },
18094     
18095     // private
18096     validateValue : function(value)
18097     {
18098         value = this.formatDate(value);
18099         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18100             return false;
18101         }
18102         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18103              return true;
18104         }
18105         var svalue = value;
18106         value = this.parseDate(value);
18107         if(!value){
18108             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18109             return false;
18110         }
18111         var time = value.getTime();
18112         if(this.minValue && time < this.minValue.getTime()){
18113             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18114             return false;
18115         }
18116         if(this.maxValue && time > this.maxValue.getTime()){
18117             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18118             return false;
18119         }
18120         /*if(this.disabledDays){
18121             var day = value.getDay();
18122             for(var i = 0; i < this.disabledDays.length; i++) {
18123                 if(day === this.disabledDays[i]){
18124                     this.markInvalid(this.disabledDaysText);
18125                     return false;
18126                 }
18127             }
18128         }
18129         */
18130         var fvalue = this.formatDate(value);
18131         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18132             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18133             return false;
18134         }
18135         */
18136         return true;
18137     },
18138
18139     // private
18140     // Provides logic to override the default TriggerField.validateBlur which just returns true
18141     validateBlur : function(){
18142         return !this.menu || !this.menu.isVisible();
18143     },
18144
18145     /**
18146      * Returns the current date value of the date field.
18147      * @return {Date} The date value
18148      */
18149     getValue : function(){
18150         
18151         
18152         
18153         return  this.hiddenField ?
18154                 this.hiddenField.value :
18155                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18156     },
18157
18158     /**
18159      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18160      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18161      * (the default format used is "m/d/y").
18162      * <br />Usage:
18163      * <pre><code>
18164 //All of these calls set the same date value (May 4, 2006)
18165
18166 //Pass a date object:
18167 var dt = new Date('5/4/06');
18168 monthField.setValue(dt);
18169
18170 //Pass a date string (default format):
18171 monthField.setValue('5/4/06');
18172
18173 //Pass a date string (custom format):
18174 monthField.format = 'Y-m-d';
18175 monthField.setValue('2006-5-4');
18176 </code></pre>
18177      * @param {String/Date} date The date or valid date string
18178      */
18179     setValue : function(date){
18180         Roo.log('month setValue' + date);
18181         // can only be first of month..
18182         
18183         var val = this.parseDate(date);
18184         
18185         if (this.hiddenField) {
18186             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18187         }
18188         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18189         this.value = this.parseDate(date);
18190     },
18191
18192     // private
18193     parseDate : function(value){
18194         if(!value || value instanceof Date){
18195             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18196             return value;
18197         }
18198         var v = Date.parseDate(value, this.format);
18199         if (!v && this.useIso) {
18200             v = Date.parseDate(value, 'Y-m-d');
18201         }
18202         if (v) {
18203             // 
18204             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18205         }
18206         
18207         
18208         if(!v && this.altFormats){
18209             if(!this.altFormatsArray){
18210                 this.altFormatsArray = this.altFormats.split("|");
18211             }
18212             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18213                 v = Date.parseDate(value, this.altFormatsArray[i]);
18214             }
18215         }
18216         return v;
18217     },
18218
18219     // private
18220     formatDate : function(date, fmt){
18221         return (!date || !(date instanceof Date)) ?
18222                date : date.dateFormat(fmt || this.format);
18223     },
18224
18225     // private
18226     menuListeners : {
18227         select: function(m, d){
18228             this.setValue(d);
18229             this.fireEvent('select', this, d);
18230         },
18231         show : function(){ // retain focus styling
18232             this.onFocus();
18233         },
18234         hide : function(){
18235             this.focus.defer(10, this);
18236             var ml = this.menuListeners;
18237             this.menu.un("select", ml.select,  this);
18238             this.menu.un("show", ml.show,  this);
18239             this.menu.un("hide", ml.hide,  this);
18240         }
18241     },
18242     // private
18243     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18244     onTriggerClick : function(){
18245         if(this.disabled){
18246             return;
18247         }
18248         if(this.menu == null){
18249             this.menu = new Roo.menu.DateMenu();
18250            
18251         }
18252         
18253         Roo.apply(this.menu.picker,  {
18254             
18255             showClear: this.allowBlank,
18256             minDate : this.minValue,
18257             maxDate : this.maxValue,
18258             disabledDatesRE : this.ddMatch,
18259             disabledDatesText : this.disabledDatesText,
18260             
18261             format : this.useIso ? 'Y-m-d' : this.format,
18262             minText : String.format(this.minText, this.formatDate(this.minValue)),
18263             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18264             
18265         });
18266          this.menu.on(Roo.apply({}, this.menuListeners, {
18267             scope:this
18268         }));
18269        
18270         
18271         var m = this.menu;
18272         var p = m.picker;
18273         
18274         // hide month picker get's called when we called by 'before hide';
18275         
18276         var ignorehide = true;
18277         p.hideMonthPicker  = function(disableAnim){
18278             if (ignorehide) {
18279                 return;
18280             }
18281              if(this.monthPicker){
18282                 Roo.log("hideMonthPicker called");
18283                 if(disableAnim === true){
18284                     this.monthPicker.hide();
18285                 }else{
18286                     this.monthPicker.slideOut('t', {duration:.2});
18287                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18288                     p.fireEvent("select", this, this.value);
18289                     m.hide();
18290                 }
18291             }
18292         }
18293         
18294         Roo.log('picker set value');
18295         Roo.log(this.getValue());
18296         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18297         m.show(this.el, 'tl-bl?');
18298         ignorehide  = false;
18299         // this will trigger hideMonthPicker..
18300         
18301         
18302         // hidden the day picker
18303         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18304         
18305         
18306         
18307       
18308         
18309         p.showMonthPicker.defer(100, p);
18310     
18311         
18312        
18313     },
18314
18315     beforeBlur : function(){
18316         var v = this.parseDate(this.getRawValue());
18317         if(v){
18318             this.setValue(v);
18319         }
18320     }
18321
18322     /** @cfg {Boolean} grow @hide */
18323     /** @cfg {Number} growMin @hide */
18324     /** @cfg {Number} growMax @hide */
18325     /**
18326      * @hide
18327      * @method autoSize
18328      */
18329 });/*
18330  * Based on:
18331  * Ext JS Library 1.1.1
18332  * Copyright(c) 2006-2007, Ext JS, LLC.
18333  *
18334  * Originally Released Under LGPL - original licence link has changed is not relivant.
18335  *
18336  * Fork - LGPL
18337  * <script type="text/javascript">
18338  */
18339  
18340
18341 /**
18342  * @class Roo.form.ComboBox
18343  * @extends Roo.form.TriggerField
18344  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18345  * @constructor
18346  * Create a new ComboBox.
18347  * @param {Object} config Configuration options
18348  */
18349 Roo.form.ComboBox = function(config){
18350     Roo.form.ComboBox.superclass.constructor.call(this, config);
18351     this.addEvents({
18352         /**
18353          * @event expand
18354          * Fires when the dropdown list is expanded
18355              * @param {Roo.form.ComboBox} combo This combo box
18356              */
18357         'expand' : true,
18358         /**
18359          * @event collapse
18360          * Fires when the dropdown list is collapsed
18361              * @param {Roo.form.ComboBox} combo This combo box
18362              */
18363         'collapse' : true,
18364         /**
18365          * @event beforeselect
18366          * Fires before a list item is selected. Return false to cancel the selection.
18367              * @param {Roo.form.ComboBox} combo This combo box
18368              * @param {Roo.data.Record} record The data record returned from the underlying store
18369              * @param {Number} index The index of the selected item in the dropdown list
18370              */
18371         'beforeselect' : true,
18372         /**
18373          * @event select
18374          * Fires when a list item is selected
18375              * @param {Roo.form.ComboBox} combo This combo box
18376              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18377              * @param {Number} index The index of the selected item in the dropdown list
18378              */
18379         'select' : true,
18380         /**
18381          * @event beforequery
18382          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18383          * The event object passed has these properties:
18384              * @param {Roo.form.ComboBox} combo This combo box
18385              * @param {String} query The query
18386              * @param {Boolean} forceAll true to force "all" query
18387              * @param {Boolean} cancel true to cancel the query
18388              * @param {Object} e The query event object
18389              */
18390         'beforequery': true,
18391          /**
18392          * @event add
18393          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18394              * @param {Roo.form.ComboBox} combo This combo box
18395              */
18396         'add' : true,
18397         /**
18398          * @event edit
18399          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18400              * @param {Roo.form.ComboBox} combo This combo box
18401              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18402              */
18403         'edit' : true
18404         
18405         
18406     });
18407     if(this.transform){
18408         this.allowDomMove = false;
18409         var s = Roo.getDom(this.transform);
18410         if(!this.hiddenName){
18411             this.hiddenName = s.name;
18412         }
18413         if(!this.store){
18414             this.mode = 'local';
18415             var d = [], opts = s.options;
18416             for(var i = 0, len = opts.length;i < len; i++){
18417                 var o = opts[i];
18418                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18419                 if(o.selected) {
18420                     this.value = value;
18421                 }
18422                 d.push([value, o.text]);
18423             }
18424             this.store = new Roo.data.SimpleStore({
18425                 'id': 0,
18426                 fields: ['value', 'text'],
18427                 data : d
18428             });
18429             this.valueField = 'value';
18430             this.displayField = 'text';
18431         }
18432         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18433         if(!this.lazyRender){
18434             this.target = true;
18435             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18436             s.parentNode.removeChild(s); // remove it
18437             this.render(this.el.parentNode);
18438         }else{
18439             s.parentNode.removeChild(s); // remove it
18440         }
18441
18442     }
18443     if (this.store) {
18444         this.store = Roo.factory(this.store, Roo.data);
18445     }
18446     
18447     this.selectedIndex = -1;
18448     if(this.mode == 'local'){
18449         if(config.queryDelay === undefined){
18450             this.queryDelay = 10;
18451         }
18452         if(config.minChars === undefined){
18453             this.minChars = 0;
18454         }
18455     }
18456 };
18457
18458 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18459     /**
18460      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18461      */
18462     /**
18463      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18464      * rendering into an Roo.Editor, defaults to false)
18465      */
18466     /**
18467      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18468      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18469      */
18470     /**
18471      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18472      */
18473     /**
18474      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18475      * the dropdown list (defaults to undefined, with no header element)
18476      */
18477
18478      /**
18479      * @cfg {String/Roo.Template} tpl The template to use to render the output
18480      */
18481      
18482     // private
18483     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18484     /**
18485      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18486      */
18487     listWidth: undefined,
18488     /**
18489      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18490      * mode = 'remote' or 'text' if mode = 'local')
18491      */
18492     displayField: undefined,
18493     /**
18494      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18495      * mode = 'remote' or 'value' if mode = 'local'). 
18496      * Note: use of a valueField requires the user make a selection
18497      * in order for a value to be mapped.
18498      */
18499     valueField: undefined,
18500     
18501     
18502     /**
18503      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18504      * field's data value (defaults to the underlying DOM element's name)
18505      */
18506     hiddenName: undefined,
18507     /**
18508      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18509      */
18510     listClass: '',
18511     /**
18512      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18513      */
18514     selectedClass: 'x-combo-selected',
18515     /**
18516      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18517      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18518      * which displays a downward arrow icon).
18519      */
18520     triggerClass : 'x-form-arrow-trigger',
18521     /**
18522      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18523      */
18524     shadow:'sides',
18525     /**
18526      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18527      * anchor positions (defaults to 'tl-bl')
18528      */
18529     listAlign: 'tl-bl?',
18530     /**
18531      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18532      */
18533     maxHeight: 300,
18534     /**
18535      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18536      * query specified by the allQuery config option (defaults to 'query')
18537      */
18538     triggerAction: 'query',
18539     /**
18540      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18541      * (defaults to 4, does not apply if editable = false)
18542      */
18543     minChars : 4,
18544     /**
18545      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18546      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18547      */
18548     typeAhead: false,
18549     /**
18550      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18551      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18552      */
18553     queryDelay: 500,
18554     /**
18555      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18556      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18557      */
18558     pageSize: 0,
18559     /**
18560      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18561      * when editable = true (defaults to false)
18562      */
18563     selectOnFocus:false,
18564     /**
18565      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18566      */
18567     queryParam: 'query',
18568     /**
18569      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18570      * when mode = 'remote' (defaults to 'Loading...')
18571      */
18572     loadingText: 'Loading...',
18573     /**
18574      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18575      */
18576     resizable: false,
18577     /**
18578      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18579      */
18580     handleHeight : 8,
18581     /**
18582      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18583      * traditional select (defaults to true)
18584      */
18585     editable: true,
18586     /**
18587      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18588      */
18589     allQuery: '',
18590     /**
18591      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18592      */
18593     mode: 'remote',
18594     /**
18595      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18596      * listWidth has a higher value)
18597      */
18598     minListWidth : 70,
18599     /**
18600      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18601      * allow the user to set arbitrary text into the field (defaults to false)
18602      */
18603     forceSelection:false,
18604     /**
18605      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18606      * if typeAhead = true (defaults to 250)
18607      */
18608     typeAheadDelay : 250,
18609     /**
18610      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18611      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18612      */
18613     valueNotFoundText : undefined,
18614     /**
18615      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18616      */
18617     blockFocus : false,
18618     
18619     /**
18620      * @cfg {Boolean} disableClear Disable showing of clear button.
18621      */
18622     disableClear : false,
18623     /**
18624      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18625      */
18626     alwaysQuery : false,
18627     
18628     //private
18629     addicon : false,
18630     editicon: false,
18631     
18632     // element that contains real text value.. (when hidden is used..)
18633      
18634     // private
18635     onRender : function(ct, position)
18636     {
18637         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18638         
18639         if(this.hiddenName){
18640             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18641                     'before', true);
18642             this.hiddenField.value =
18643                 this.hiddenValue !== undefined ? this.hiddenValue :
18644                 this.value !== undefined ? this.value : '';
18645
18646             // prevent input submission
18647             this.el.dom.removeAttribute('name');
18648              
18649              
18650         }
18651         
18652         if(Roo.isGecko){
18653             this.el.dom.setAttribute('autocomplete', 'off');
18654         }
18655
18656         var cls = 'x-combo-list';
18657
18658         this.list = new Roo.Layer({
18659             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18660         });
18661
18662         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18663         this.list.setWidth(lw);
18664         this.list.swallowEvent('mousewheel');
18665         this.assetHeight = 0;
18666
18667         if(this.title){
18668             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18669             this.assetHeight += this.header.getHeight();
18670         }
18671
18672         this.innerList = this.list.createChild({cls:cls+'-inner'});
18673         this.innerList.on('mouseover', this.onViewOver, this);
18674         this.innerList.on('mousemove', this.onViewMove, this);
18675         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18676         
18677         if(this.allowBlank && !this.pageSize && !this.disableClear){
18678             this.footer = this.list.createChild({cls:cls+'-ft'});
18679             this.pageTb = new Roo.Toolbar(this.footer);
18680            
18681         }
18682         if(this.pageSize){
18683             this.footer = this.list.createChild({cls:cls+'-ft'});
18684             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18685                     {pageSize: this.pageSize});
18686             
18687         }
18688         
18689         if (this.pageTb && this.allowBlank && !this.disableClear) {
18690             var _this = this;
18691             this.pageTb.add(new Roo.Toolbar.Fill(), {
18692                 cls: 'x-btn-icon x-btn-clear',
18693                 text: '&#160;',
18694                 handler: function()
18695                 {
18696                     _this.collapse();
18697                     _this.clearValue();
18698                     _this.onSelect(false, -1);
18699                 }
18700             });
18701         }
18702         if (this.footer) {
18703             this.assetHeight += this.footer.getHeight();
18704         }
18705         
18706
18707         if(!this.tpl){
18708             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18709         }
18710
18711         this.view = new Roo.View(this.innerList, this.tpl, {
18712             singleSelect:true,
18713             store: this.store,
18714             selectedClass: this.selectedClass
18715         });
18716
18717         this.view.on('click', this.onViewClick, this);
18718
18719         this.store.on('beforeload', this.onBeforeLoad, this);
18720         this.store.on('load', this.onLoad, this);
18721         this.store.on('loadexception', this.onLoadException, this);
18722
18723         if(this.resizable){
18724             this.resizer = new Roo.Resizable(this.list,  {
18725                pinned:true, handles:'se'
18726             });
18727             this.resizer.on('resize', function(r, w, h){
18728                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18729                 this.listWidth = w;
18730                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18731                 this.restrictHeight();
18732             }, this);
18733             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18734         }
18735         if(!this.editable){
18736             this.editable = true;
18737             this.setEditable(false);
18738         }  
18739         
18740         
18741         if (typeof(this.events.add.listeners) != 'undefined') {
18742             
18743             this.addicon = this.wrap.createChild(
18744                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18745        
18746             this.addicon.on('click', function(e) {
18747                 this.fireEvent('add', this);
18748             }, this);
18749         }
18750         if (typeof(this.events.edit.listeners) != 'undefined') {
18751             
18752             this.editicon = this.wrap.createChild(
18753                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18754             if (this.addicon) {
18755                 this.editicon.setStyle('margin-left', '40px');
18756             }
18757             this.editicon.on('click', function(e) {
18758                 
18759                 // we fire even  if inothing is selected..
18760                 this.fireEvent('edit', this, this.lastData );
18761                 
18762             }, this);
18763         }
18764         
18765         
18766         
18767     },
18768
18769     // private
18770     initEvents : function(){
18771         Roo.form.ComboBox.superclass.initEvents.call(this);
18772
18773         this.keyNav = new Roo.KeyNav(this.el, {
18774             "up" : function(e){
18775                 this.inKeyMode = true;
18776                 this.selectPrev();
18777             },
18778
18779             "down" : function(e){
18780                 if(!this.isExpanded()){
18781                     this.onTriggerClick();
18782                 }else{
18783                     this.inKeyMode = true;
18784                     this.selectNext();
18785                 }
18786             },
18787
18788             "enter" : function(e){
18789                 this.onViewClick();
18790                 //return true;
18791             },
18792
18793             "esc" : function(e){
18794                 this.collapse();
18795             },
18796
18797             "tab" : function(e){
18798                 this.onViewClick(false);
18799                 this.fireEvent("specialkey", this, e);
18800                 return true;
18801             },
18802
18803             scope : this,
18804
18805             doRelay : function(foo, bar, hname){
18806                 if(hname == 'down' || this.scope.isExpanded()){
18807                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18808                 }
18809                 return true;
18810             },
18811
18812             forceKeyDown: true
18813         });
18814         this.queryDelay = Math.max(this.queryDelay || 10,
18815                 this.mode == 'local' ? 10 : 250);
18816         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18817         if(this.typeAhead){
18818             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18819         }
18820         if(this.editable !== false){
18821             this.el.on("keyup", this.onKeyUp, this);
18822         }
18823         if(this.forceSelection){
18824             this.on('blur', this.doForce, this);
18825         }
18826     },
18827
18828     onDestroy : function(){
18829         if(this.view){
18830             this.view.setStore(null);
18831             this.view.el.removeAllListeners();
18832             this.view.el.remove();
18833             this.view.purgeListeners();
18834         }
18835         if(this.list){
18836             this.list.destroy();
18837         }
18838         if(this.store){
18839             this.store.un('beforeload', this.onBeforeLoad, this);
18840             this.store.un('load', this.onLoad, this);
18841             this.store.un('loadexception', this.onLoadException, this);
18842         }
18843         Roo.form.ComboBox.superclass.onDestroy.call(this);
18844     },
18845
18846     // private
18847     fireKey : function(e){
18848         if(e.isNavKeyPress() && !this.list.isVisible()){
18849             this.fireEvent("specialkey", this, e);
18850         }
18851     },
18852
18853     // private
18854     onResize: function(w, h){
18855         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18856         
18857         if(typeof w != 'number'){
18858             // we do not handle it!?!?
18859             return;
18860         }
18861         var tw = this.trigger.getWidth();
18862         tw += this.addicon ? this.addicon.getWidth() : 0;
18863         tw += this.editicon ? this.editicon.getWidth() : 0;
18864         var x = w - tw;
18865         this.el.setWidth( this.adjustWidth('input', x));
18866             
18867         this.trigger.setStyle('left', x+'px');
18868         
18869         if(this.list && this.listWidth === undefined){
18870             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18871             this.list.setWidth(lw);
18872             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18873         }
18874         
18875     
18876         
18877     },
18878
18879     /**
18880      * Allow or prevent the user from directly editing the field text.  If false is passed,
18881      * the user will only be able to select from the items defined in the dropdown list.  This method
18882      * is the runtime equivalent of setting the 'editable' config option at config time.
18883      * @param {Boolean} value True to allow the user to directly edit the field text
18884      */
18885     setEditable : function(value){
18886         if(value == this.editable){
18887             return;
18888         }
18889         this.editable = value;
18890         if(!value){
18891             this.el.dom.setAttribute('readOnly', true);
18892             this.el.on('mousedown', this.onTriggerClick,  this);
18893             this.el.addClass('x-combo-noedit');
18894         }else{
18895             this.el.dom.setAttribute('readOnly', false);
18896             this.el.un('mousedown', this.onTriggerClick,  this);
18897             this.el.removeClass('x-combo-noedit');
18898         }
18899     },
18900
18901     // private
18902     onBeforeLoad : function(){
18903         if(!this.hasFocus){
18904             return;
18905         }
18906         this.innerList.update(this.loadingText ?
18907                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18908         this.restrictHeight();
18909         this.selectedIndex = -1;
18910     },
18911
18912     // private
18913     onLoad : function(){
18914         if(!this.hasFocus){
18915             return;
18916         }
18917         if(this.store.getCount() > 0){
18918             this.expand();
18919             this.restrictHeight();
18920             if(this.lastQuery == this.allQuery){
18921                 if(this.editable){
18922                     this.el.dom.select();
18923                 }
18924                 if(!this.selectByValue(this.value, true)){
18925                     this.select(0, true);
18926                 }
18927             }else{
18928                 this.selectNext();
18929                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18930                     this.taTask.delay(this.typeAheadDelay);
18931                 }
18932             }
18933         }else{
18934             this.onEmptyResults();
18935         }
18936         //this.el.focus();
18937     },
18938     // private
18939     onLoadException : function()
18940     {
18941         this.collapse();
18942         Roo.log(this.store.reader.jsonData);
18943         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18944             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18945         }
18946         
18947         
18948     },
18949     // private
18950     onTypeAhead : function(){
18951         if(this.store.getCount() > 0){
18952             var r = this.store.getAt(0);
18953             var newValue = r.data[this.displayField];
18954             var len = newValue.length;
18955             var selStart = this.getRawValue().length;
18956             if(selStart != len){
18957                 this.setRawValue(newValue);
18958                 this.selectText(selStart, newValue.length);
18959             }
18960         }
18961     },
18962
18963     // private
18964     onSelect : function(record, index){
18965         if(this.fireEvent('beforeselect', this, record, index) !== false){
18966             this.setFromData(index > -1 ? record.data : false);
18967             this.collapse();
18968             this.fireEvent('select', this, record, index);
18969         }
18970     },
18971
18972     /**
18973      * Returns the currently selected field value or empty string if no value is set.
18974      * @return {String} value The selected value
18975      */
18976     getValue : function(){
18977         if(this.valueField){
18978             return typeof this.value != 'undefined' ? this.value : '';
18979         }
18980         return Roo.form.ComboBox.superclass.getValue.call(this);
18981     },
18982
18983     /**
18984      * Clears any text/value currently set in the field
18985      */
18986     clearValue : function(){
18987         if(this.hiddenField){
18988             this.hiddenField.value = '';
18989         }
18990         this.value = '';
18991         this.setRawValue('');
18992         this.lastSelectionText = '';
18993         
18994     },
18995
18996     /**
18997      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18998      * will be displayed in the field.  If the value does not match the data value of an existing item,
18999      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19000      * Otherwise the field will be blank (although the value will still be set).
19001      * @param {String} value The value to match
19002      */
19003     setValue : function(v){
19004         var text = v;
19005         if(this.valueField){
19006             var r = this.findRecord(this.valueField, v);
19007             if(r){
19008                 text = r.data[this.displayField];
19009             }else if(this.valueNotFoundText !== undefined){
19010                 text = this.valueNotFoundText;
19011             }
19012         }
19013         this.lastSelectionText = text;
19014         if(this.hiddenField){
19015             this.hiddenField.value = v;
19016         }
19017         Roo.form.ComboBox.superclass.setValue.call(this, text);
19018         this.value = v;
19019     },
19020     /**
19021      * @property {Object} the last set data for the element
19022      */
19023     
19024     lastData : false,
19025     /**
19026      * Sets the value of the field based on a object which is related to the record format for the store.
19027      * @param {Object} value the value to set as. or false on reset?
19028      */
19029     setFromData : function(o){
19030         var dv = ''; // display value
19031         var vv = ''; // value value..
19032         this.lastData = o;
19033         if (this.displayField) {
19034             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19035         } else {
19036             // this is an error condition!!!
19037             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19038         }
19039         
19040         if(this.valueField){
19041             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19042         }
19043         if(this.hiddenField){
19044             this.hiddenField.value = vv;
19045             
19046             this.lastSelectionText = dv;
19047             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19048             this.value = vv;
19049             return;
19050         }
19051         // no hidden field.. - we store the value in 'value', but still display
19052         // display field!!!!
19053         this.lastSelectionText = dv;
19054         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19055         this.value = vv;
19056         
19057         
19058     },
19059     // private
19060     reset : function(){
19061         // overridden so that last data is reset..
19062         this.setValue(this.resetValue);
19063         this.originalValue = this.getValue();
19064         this.clearInvalid();
19065         this.lastData = false;
19066         if (this.view) {
19067             this.view.clearSelections();
19068         }
19069     },
19070     // private
19071     findRecord : function(prop, value){
19072         var record;
19073         if(this.store.getCount() > 0){
19074             this.store.each(function(r){
19075                 if(r.data[prop] == value){
19076                     record = r;
19077                     return false;
19078                 }
19079                 return true;
19080             });
19081         }
19082         return record;
19083     },
19084     
19085     getName: function()
19086     {
19087         // returns hidden if it's set..
19088         if (!this.rendered) {return ''};
19089         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19090         
19091     },
19092     // private
19093     onViewMove : function(e, t){
19094         this.inKeyMode = false;
19095     },
19096
19097     // private
19098     onViewOver : function(e, t){
19099         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19100             return;
19101         }
19102         var item = this.view.findItemFromChild(t);
19103         if(item){
19104             var index = this.view.indexOf(item);
19105             this.select(index, false);
19106         }
19107     },
19108
19109     // private
19110     onViewClick : function(doFocus)
19111     {
19112         var index = this.view.getSelectedIndexes()[0];
19113         var r = this.store.getAt(index);
19114         if(r){
19115             this.onSelect(r, index);
19116         }
19117         if(doFocus !== false && !this.blockFocus){
19118             this.el.focus();
19119         }
19120     },
19121
19122     // private
19123     restrictHeight : function(){
19124         this.innerList.dom.style.height = '';
19125         var inner = this.innerList.dom;
19126         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19127         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19128         this.list.beginUpdate();
19129         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19130         this.list.alignTo(this.el, this.listAlign);
19131         this.list.endUpdate();
19132     },
19133
19134     // private
19135     onEmptyResults : function(){
19136         this.collapse();
19137     },
19138
19139     /**
19140      * Returns true if the dropdown list is expanded, else false.
19141      */
19142     isExpanded : function(){
19143         return this.list.isVisible();
19144     },
19145
19146     /**
19147      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19148      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19149      * @param {String} value The data value of the item to select
19150      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19151      * selected item if it is not currently in view (defaults to true)
19152      * @return {Boolean} True if the value matched an item in the list, else false
19153      */
19154     selectByValue : function(v, scrollIntoView){
19155         if(v !== undefined && v !== null){
19156             var r = this.findRecord(this.valueField || this.displayField, v);
19157             if(r){
19158                 this.select(this.store.indexOf(r), scrollIntoView);
19159                 return true;
19160             }
19161         }
19162         return false;
19163     },
19164
19165     /**
19166      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19167      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19168      * @param {Number} index The zero-based index of the list item to select
19169      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19170      * selected item if it is not currently in view (defaults to true)
19171      */
19172     select : function(index, scrollIntoView){
19173         this.selectedIndex = index;
19174         this.view.select(index);
19175         if(scrollIntoView !== false){
19176             var el = this.view.getNode(index);
19177             if(el){
19178                 this.innerList.scrollChildIntoView(el, false);
19179             }
19180         }
19181     },
19182
19183     // private
19184     selectNext : function(){
19185         var ct = this.store.getCount();
19186         if(ct > 0){
19187             if(this.selectedIndex == -1){
19188                 this.select(0);
19189             }else if(this.selectedIndex < ct-1){
19190                 this.select(this.selectedIndex+1);
19191             }
19192         }
19193     },
19194
19195     // private
19196     selectPrev : function(){
19197         var ct = this.store.getCount();
19198         if(ct > 0){
19199             if(this.selectedIndex == -1){
19200                 this.select(0);
19201             }else if(this.selectedIndex != 0){
19202                 this.select(this.selectedIndex-1);
19203             }
19204         }
19205     },
19206
19207     // private
19208     onKeyUp : function(e){
19209         if(this.editable !== false && !e.isSpecialKey()){
19210             this.lastKey = e.getKey();
19211             this.dqTask.delay(this.queryDelay);
19212         }
19213     },
19214
19215     // private
19216     validateBlur : function(){
19217         return !this.list || !this.list.isVisible();   
19218     },
19219
19220     // private
19221     initQuery : function(){
19222         this.doQuery(this.getRawValue());
19223     },
19224
19225     // private
19226     doForce : function(){
19227         if(this.el.dom.value.length > 0){
19228             this.el.dom.value =
19229                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19230              
19231         }
19232     },
19233
19234     /**
19235      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19236      * query allowing the query action to be canceled if needed.
19237      * @param {String} query The SQL query to execute
19238      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19239      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19240      * saved in the current store (defaults to false)
19241      */
19242     doQuery : function(q, forceAll){
19243         if(q === undefined || q === null){
19244             q = '';
19245         }
19246         var qe = {
19247             query: q,
19248             forceAll: forceAll,
19249             combo: this,
19250             cancel:false
19251         };
19252         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19253             return false;
19254         }
19255         q = qe.query;
19256         forceAll = qe.forceAll;
19257         if(forceAll === true || (q.length >= this.minChars)){
19258             if(this.lastQuery != q || this.alwaysQuery){
19259                 this.lastQuery = q;
19260                 if(this.mode == 'local'){
19261                     this.selectedIndex = -1;
19262                     if(forceAll){
19263                         this.store.clearFilter();
19264                     }else{
19265                         this.store.filter(this.displayField, q);
19266                     }
19267                     this.onLoad();
19268                 }else{
19269                     this.store.baseParams[this.queryParam] = q;
19270                     this.store.load({
19271                         params: this.getParams(q)
19272                     });
19273                     this.expand();
19274                 }
19275             }else{
19276                 this.selectedIndex = -1;
19277                 this.onLoad();   
19278             }
19279         }
19280     },
19281
19282     // private
19283     getParams : function(q){
19284         var p = {};
19285         //p[this.queryParam] = q;
19286         if(this.pageSize){
19287             p.start = 0;
19288             p.limit = this.pageSize;
19289         }
19290         return p;
19291     },
19292
19293     /**
19294      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19295      */
19296     collapse : function(){
19297         if(!this.isExpanded()){
19298             return;
19299         }
19300         this.list.hide();
19301         Roo.get(document).un('mousedown', this.collapseIf, this);
19302         Roo.get(document).un('mousewheel', this.collapseIf, this);
19303         if (!this.editable) {
19304             Roo.get(document).un('keydown', this.listKeyPress, this);
19305         }
19306         this.fireEvent('collapse', this);
19307     },
19308
19309     // private
19310     collapseIf : function(e){
19311         if(!e.within(this.wrap) && !e.within(this.list)){
19312             this.collapse();
19313         }
19314     },
19315
19316     /**
19317      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19318      */
19319     expand : function(){
19320         if(this.isExpanded() || !this.hasFocus){
19321             return;
19322         }
19323         this.list.alignTo(this.el, this.listAlign);
19324         this.list.show();
19325         Roo.get(document).on('mousedown', this.collapseIf, this);
19326         Roo.get(document).on('mousewheel', this.collapseIf, this);
19327         if (!this.editable) {
19328             Roo.get(document).on('keydown', this.listKeyPress, this);
19329         }
19330         
19331         this.fireEvent('expand', this);
19332     },
19333
19334     // private
19335     // Implements the default empty TriggerField.onTriggerClick function
19336     onTriggerClick : function(){
19337         if(this.disabled){
19338             return;
19339         }
19340         if(this.isExpanded()){
19341             this.collapse();
19342             if (!this.blockFocus) {
19343                 this.el.focus();
19344             }
19345             
19346         }else {
19347             this.hasFocus = true;
19348             if(this.triggerAction == 'all') {
19349                 this.doQuery(this.allQuery, true);
19350             } else {
19351                 this.doQuery(this.getRawValue());
19352             }
19353             if (!this.blockFocus) {
19354                 this.el.focus();
19355             }
19356         }
19357     },
19358     listKeyPress : function(e)
19359     {
19360         //Roo.log('listkeypress');
19361         // scroll to first matching element based on key pres..
19362         if (e.isSpecialKey()) {
19363             return false;
19364         }
19365         var k = String.fromCharCode(e.getKey()).toUpperCase();
19366         //Roo.log(k);
19367         var match  = false;
19368         var csel = this.view.getSelectedNodes();
19369         var cselitem = false;
19370         if (csel.length) {
19371             var ix = this.view.indexOf(csel[0]);
19372             cselitem  = this.store.getAt(ix);
19373             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19374                 cselitem = false;
19375             }
19376             
19377         }
19378         
19379         this.store.each(function(v) { 
19380             if (cselitem) {
19381                 // start at existing selection.
19382                 if (cselitem.id == v.id) {
19383                     cselitem = false;
19384                 }
19385                 return;
19386             }
19387                 
19388             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19389                 match = this.store.indexOf(v);
19390                 return false;
19391             }
19392         }, this);
19393         
19394         if (match === false) {
19395             return true; // no more action?
19396         }
19397         // scroll to?
19398         this.view.select(match);
19399         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19400         sn.scrollIntoView(sn.dom.parentNode, false);
19401     } 
19402
19403     /** 
19404     * @cfg {Boolean} grow 
19405     * @hide 
19406     */
19407     /** 
19408     * @cfg {Number} growMin 
19409     * @hide 
19410     */
19411     /** 
19412     * @cfg {Number} growMax 
19413     * @hide 
19414     */
19415     /**
19416      * @hide
19417      * @method autoSize
19418      */
19419 });/*
19420  * Copyright(c) 2010-2012, Roo J Solutions Limited
19421  *
19422  * Licence LGPL
19423  *
19424  */
19425
19426 /**
19427  * @class Roo.form.ComboBoxArray
19428  * @extends Roo.form.TextField
19429  * A facebook style adder... for lists of email / people / countries  etc...
19430  * pick multiple items from a combo box, and shows each one.
19431  *
19432  *  Fred [x]  Brian [x]  [Pick another |v]
19433  *
19434  *
19435  *  For this to work: it needs various extra information
19436  *    - normal combo problay has
19437  *      name, hiddenName
19438  *    + displayField, valueField
19439  *
19440  *    For our purpose...
19441  *
19442  *
19443  *   If we change from 'extends' to wrapping...
19444  *   
19445  *  
19446  *
19447  
19448  
19449  * @constructor
19450  * Create a new ComboBoxArray.
19451  * @param {Object} config Configuration options
19452  */
19453  
19454
19455 Roo.form.ComboBoxArray = function(config)
19456 {
19457     this.addEvents({
19458         /**
19459          * @event beforeremove
19460          * Fires before remove the value from the list
19461              * @param {Roo.form.ComboBoxArray} _self This combo box array
19462              * @param {Roo.form.ComboBoxArray.Item} item removed item
19463              */
19464         'beforeremove' : true,
19465         /**
19466          * @event remove
19467          * Fires when remove the value from the list
19468              * @param {Roo.form.ComboBoxArray} _self This combo box array
19469              * @param {Roo.form.ComboBoxArray.Item} item removed item
19470              */
19471         'remove' : true
19472         
19473         
19474     });
19475     
19476     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19477     
19478     this.items = new Roo.util.MixedCollection(false);
19479     
19480     // construct the child combo...
19481     
19482     
19483     
19484     
19485    
19486     
19487 }
19488
19489  
19490 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19491
19492     /**
19493      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19494      */
19495     
19496     lastData : false,
19497     
19498     // behavies liek a hiddne field
19499     inputType:      'hidden',
19500     /**
19501      * @cfg {Number} width The width of the box that displays the selected element
19502      */ 
19503     width:          300,
19504
19505     
19506     
19507     /**
19508      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19509      */
19510     name : false,
19511     /**
19512      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19513      */
19514     hiddenName : false,
19515       /**
19516      * @cfg {String} seperator    The value seperator normally ',' 
19517      */
19518     seperator : ',',
19519     
19520     // private the array of items that are displayed..
19521     items  : false,
19522     // private - the hidden field el.
19523     hiddenEl : false,
19524     // private - the filed el..
19525     el : false,
19526     
19527     //validateValue : function() { return true; }, // all values are ok!
19528     //onAddClick: function() { },
19529     
19530     onRender : function(ct, position) 
19531     {
19532         
19533         // create the standard hidden element
19534         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19535         
19536         
19537         // give fake names to child combo;
19538         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19539         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19540         
19541         this.combo = Roo.factory(this.combo, Roo.form);
19542         this.combo.onRender(ct, position);
19543         if (typeof(this.combo.width) != 'undefined') {
19544             this.combo.onResize(this.combo.width,0);
19545         }
19546         
19547         this.combo.initEvents();
19548         
19549         // assigned so form know we need to do this..
19550         this.store          = this.combo.store;
19551         this.valueField     = this.combo.valueField;
19552         this.displayField   = this.combo.displayField ;
19553         
19554         
19555         this.combo.wrap.addClass('x-cbarray-grp');
19556         
19557         var cbwrap = this.combo.wrap.createChild(
19558             {tag: 'div', cls: 'x-cbarray-cb'},
19559             this.combo.el.dom
19560         );
19561         
19562              
19563         this.hiddenEl = this.combo.wrap.createChild({
19564             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19565         });
19566         this.el = this.combo.wrap.createChild({
19567             tag: 'input',  type:'hidden' , name: this.name, value : ''
19568         });
19569          //   this.el.dom.removeAttribute("name");
19570         
19571         
19572         this.outerWrap = this.combo.wrap;
19573         this.wrap = cbwrap;
19574         
19575         this.outerWrap.setWidth(this.width);
19576         this.outerWrap.dom.removeChild(this.el.dom);
19577         
19578         this.wrap.dom.appendChild(this.el.dom);
19579         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19580         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19581         
19582         this.combo.trigger.setStyle('position','relative');
19583         this.combo.trigger.setStyle('left', '0px');
19584         this.combo.trigger.setStyle('top', '2px');
19585         
19586         this.combo.el.setStyle('vertical-align', 'text-bottom');
19587         
19588         //this.trigger.setStyle('vertical-align', 'top');
19589         
19590         // this should use the code from combo really... on('add' ....)
19591         if (this.adder) {
19592             
19593         
19594             this.adder = this.outerWrap.createChild(
19595                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19596             var _t = this;
19597             this.adder.on('click', function(e) {
19598                 _t.fireEvent('adderclick', this, e);
19599             }, _t);
19600         }
19601         //var _t = this;
19602         //this.adder.on('click', this.onAddClick, _t);
19603         
19604         
19605         this.combo.on('select', function(cb, rec, ix) {
19606             this.addItem(rec.data);
19607             
19608             cb.setValue('');
19609             cb.el.dom.value = '';
19610             //cb.lastData = rec.data;
19611             // add to list
19612             
19613         }, this);
19614         
19615         
19616     },
19617     
19618     
19619     getName: function()
19620     {
19621         // returns hidden if it's set..
19622         if (!this.rendered) {return ''};
19623         return  this.hiddenName ? this.hiddenName : this.name;
19624         
19625     },
19626     
19627     
19628     onResize: function(w, h){
19629         
19630         return;
19631         // not sure if this is needed..
19632         //this.combo.onResize(w,h);
19633         
19634         if(typeof w != 'number'){
19635             // we do not handle it!?!?
19636             return;
19637         }
19638         var tw = this.combo.trigger.getWidth();
19639         tw += this.addicon ? this.addicon.getWidth() : 0;
19640         tw += this.editicon ? this.editicon.getWidth() : 0;
19641         var x = w - tw;
19642         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19643             
19644         this.combo.trigger.setStyle('left', '0px');
19645         
19646         if(this.list && this.listWidth === undefined){
19647             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19648             this.list.setWidth(lw);
19649             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19650         }
19651         
19652     
19653         
19654     },
19655     
19656     addItem: function(rec)
19657     {
19658         var valueField = this.combo.valueField;
19659         var displayField = this.combo.displayField;
19660         
19661         if (this.items.indexOfKey(rec[valueField]) > -1) {
19662             //console.log("GOT " + rec.data.id);
19663             return;
19664         }
19665         
19666         var x = new Roo.form.ComboBoxArray.Item({
19667             //id : rec[this.idField],
19668             data : rec,
19669             displayField : displayField ,
19670             tipField : displayField ,
19671             cb : this
19672         });
19673         // use the 
19674         this.items.add(rec[valueField],x);
19675         // add it before the element..
19676         this.updateHiddenEl();
19677         x.render(this.outerWrap, this.wrap.dom);
19678         // add the image handler..
19679     },
19680     
19681     updateHiddenEl : function()
19682     {
19683         this.validate();
19684         if (!this.hiddenEl) {
19685             return;
19686         }
19687         var ar = [];
19688         var idField = this.combo.valueField;
19689         
19690         this.items.each(function(f) {
19691             ar.push(f.data[idField]);
19692         });
19693         this.hiddenEl.dom.value = ar.join(this.seperator);
19694         this.validate();
19695     },
19696     
19697     reset : function()
19698     {
19699         this.items.clear();
19700         
19701         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19702            el.remove();
19703         });
19704         
19705         this.el.dom.value = '';
19706         if (this.hiddenEl) {
19707             this.hiddenEl.dom.value = '';
19708         }
19709         
19710     },
19711     getValue: function()
19712     {
19713         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19714     },
19715     setValue: function(v) // not a valid action - must use addItems..
19716     {
19717         
19718         this.reset();
19719          
19720         if (this.store.isLocal && (typeof(v) == 'string')) {
19721             // then we can use the store to find the values..
19722             // comma seperated at present.. this needs to allow JSON based encoding..
19723             this.hiddenEl.value  = v;
19724             var v_ar = [];
19725             Roo.each(v.split(this.seperator), function(k) {
19726                 Roo.log("CHECK " + this.valueField + ',' + k);
19727                 var li = this.store.query(this.valueField, k);
19728                 if (!li.length) {
19729                     return;
19730                 }
19731                 var add = {};
19732                 add[this.valueField] = k;
19733                 add[this.displayField] = li.item(0).data[this.displayField];
19734                 
19735                 this.addItem(add);
19736             }, this) 
19737              
19738         }
19739         if (typeof(v) == 'object' ) {
19740             // then let's assume it's an array of objects..
19741             Roo.each(v, function(l) {
19742                 var add = l;
19743                 if (typeof(l) == 'string') {
19744                     add = {};
19745                     add[this.valueField] = l;
19746                     add[this.displayField] = l
19747                 }
19748                 this.addItem(add);
19749             }, this);
19750              
19751         }
19752         
19753         
19754     },
19755     setFromData: function(v)
19756     {
19757         // this recieves an object, if setValues is called.
19758         this.reset();
19759         this.el.dom.value = v[this.displayField];
19760         this.hiddenEl.dom.value = v[this.valueField];
19761         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19762             return;
19763         }
19764         var kv = v[this.valueField];
19765         var dv = v[this.displayField];
19766         kv = typeof(kv) != 'string' ? '' : kv;
19767         dv = typeof(dv) != 'string' ? '' : dv;
19768         
19769         
19770         var keys = kv.split(this.seperator);
19771         var display = dv.split(this.seperator);
19772         for (var i = 0 ; i < keys.length; i++) {
19773             add = {};
19774             add[this.valueField] = keys[i];
19775             add[this.displayField] = display[i];
19776             this.addItem(add);
19777         }
19778       
19779         
19780     },
19781     
19782     /**
19783      * Validates the combox array value
19784      * @return {Boolean} True if the value is valid, else false
19785      */
19786     validate : function(){
19787         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19788             this.clearInvalid();
19789             return true;
19790         }
19791         return false;
19792     },
19793     
19794     validateValue : function(value){
19795         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19796         
19797     },
19798     
19799     /*@
19800      * overide
19801      * 
19802      */
19803     isDirty : function() {
19804         if(this.disabled) {
19805             return false;
19806         }
19807         
19808         try {
19809             var d = Roo.decode(String(this.originalValue));
19810         } catch (e) {
19811             return String(this.getValue()) !== String(this.originalValue);
19812         }
19813         
19814         var originalValue = [];
19815         
19816         for (var i = 0; i < d.length; i++){
19817             originalValue.push(d[i][this.valueField]);
19818         }
19819         
19820         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19821         
19822     }
19823     
19824 });
19825
19826
19827
19828 /**
19829  * @class Roo.form.ComboBoxArray.Item
19830  * @extends Roo.BoxComponent
19831  * A selected item in the list
19832  *  Fred [x]  Brian [x]  [Pick another |v]
19833  * 
19834  * @constructor
19835  * Create a new item.
19836  * @param {Object} config Configuration options
19837  */
19838  
19839 Roo.form.ComboBoxArray.Item = function(config) {
19840     config.id = Roo.id();
19841     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19842 }
19843
19844 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19845     data : {},
19846     cb: false,
19847     displayField : false,
19848     tipField : false,
19849     
19850     
19851     defaultAutoCreate : {
19852         tag: 'div',
19853         cls: 'x-cbarray-item',
19854         cn : [ 
19855             { tag: 'div' },
19856             {
19857                 tag: 'img',
19858                 width:16,
19859                 height : 16,
19860                 src : Roo.BLANK_IMAGE_URL ,
19861                 align: 'center'
19862             }
19863         ]
19864         
19865     },
19866     
19867  
19868     onRender : function(ct, position)
19869     {
19870         Roo.form.Field.superclass.onRender.call(this, ct, position);
19871         
19872         if(!this.el){
19873             var cfg = this.getAutoCreate();
19874             this.el = ct.createChild(cfg, position);
19875         }
19876         
19877         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19878         
19879         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19880             this.cb.renderer(this.data) :
19881             String.format('{0}',this.data[this.displayField]);
19882         
19883             
19884         this.el.child('div').dom.setAttribute('qtip',
19885                         String.format('{0}',this.data[this.tipField])
19886         );
19887         
19888         this.el.child('img').on('click', this.remove, this);
19889         
19890     },
19891    
19892     remove : function()
19893     {
19894         if(this.cb.disabled){
19895             return;
19896         }
19897         
19898         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19899             this.cb.items.remove(this);
19900             this.el.child('img').un('click', this.remove, this);
19901             this.el.remove();
19902             this.cb.updateHiddenEl();
19903
19904             this.cb.fireEvent('remove', this.cb, this);
19905         }
19906         
19907     }
19908 });/*
19909  * RooJS Library 1.1.1
19910  * Copyright(c) 2008-2011  Alan Knowles
19911  *
19912  * License - LGPL
19913  */
19914  
19915
19916 /**
19917  * @class Roo.form.ComboNested
19918  * @extends Roo.form.ComboBox
19919  * A combobox for that allows selection of nested items in a list,
19920  * eg.
19921  *
19922  *  Book
19923  *    -> red
19924  *    -> green
19925  *  Table
19926  *    -> square
19927  *      ->red
19928  *      ->green
19929  *    -> rectangle
19930  *      ->green
19931  *      
19932  * 
19933  * @constructor
19934  * Create a new ComboNested
19935  * @param {Object} config Configuration options
19936  */
19937 Roo.form.ComboNested = function(config){
19938     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19939     // should verify some data...
19940     // like
19941     // hiddenName = required..
19942     // displayField = required
19943     // valudField == required
19944     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19945     var _t = this;
19946     Roo.each(req, function(e) {
19947         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19948             throw "Roo.form.ComboNested : missing value for: " + e;
19949         }
19950     });
19951      
19952     
19953 };
19954
19955 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19956    
19957     /*
19958      * @config {Number} max Number of columns to show
19959      */
19960     
19961     maxColumns : 3,
19962    
19963     list : null, // the outermost div..
19964     innerLists : null, // the
19965     views : null,
19966     stores : null,
19967     // private
19968     loadingChildren : false,
19969     
19970     onRender : function(ct, position)
19971     {
19972         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19973         
19974         if(this.hiddenName){
19975             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19976                     'before', true);
19977             this.hiddenField.value =
19978                 this.hiddenValue !== undefined ? this.hiddenValue :
19979                 this.value !== undefined ? this.value : '';
19980
19981             // prevent input submission
19982             this.el.dom.removeAttribute('name');
19983              
19984              
19985         }
19986         
19987         if(Roo.isGecko){
19988             this.el.dom.setAttribute('autocomplete', 'off');
19989         }
19990
19991         var cls = 'x-combo-list';
19992
19993         this.list = new Roo.Layer({
19994             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19995         });
19996
19997         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19998         this.list.setWidth(lw);
19999         this.list.swallowEvent('mousewheel');
20000         this.assetHeight = 0;
20001
20002         if(this.title){
20003             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20004             this.assetHeight += this.header.getHeight();
20005         }
20006         this.innerLists = [];
20007         this.views = [];
20008         this.stores = [];
20009         for (var i =0 ; i < this.maxColumns; i++) {
20010             this.onRenderList( cls, i);
20011         }
20012         
20013         // always needs footer, as we are going to have an 'OK' button.
20014         this.footer = this.list.createChild({cls:cls+'-ft'});
20015         this.pageTb = new Roo.Toolbar(this.footer);  
20016         var _this = this;
20017         this.pageTb.add(  {
20018             
20019             text: 'Done',
20020             handler: function()
20021             {
20022                 _this.collapse();
20023             }
20024         });
20025         
20026         if ( this.allowBlank && !this.disableClear) {
20027             
20028             this.pageTb.add(new Roo.Toolbar.Fill(), {
20029                 cls: 'x-btn-icon x-btn-clear',
20030                 text: '&#160;',
20031                 handler: function()
20032                 {
20033                     _this.collapse();
20034                     _this.clearValue();
20035                     _this.onSelect(false, -1);
20036                 }
20037             });
20038         }
20039         if (this.footer) {
20040             this.assetHeight += this.footer.getHeight();
20041         }
20042         
20043     },
20044     onRenderList : function (  cls, i)
20045     {
20046         
20047         var lw = Math.floor(
20048                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20049         );
20050         
20051         this.list.setWidth(lw); // default to '1'
20052
20053         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20054         //il.on('mouseover', this.onViewOver, this, { list:  i });
20055         //il.on('mousemove', this.onViewMove, this, { list:  i });
20056         il.setWidth(lw);
20057         il.setStyle({ 'overflow-x' : 'hidden'});
20058
20059         if(!this.tpl){
20060             this.tpl = new Roo.Template({
20061                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20062                 isEmpty: function (value, allValues) {
20063                     //Roo.log(value);
20064                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20065                     return dl ? 'has-children' : 'no-children'
20066                 }
20067             });
20068         }
20069         
20070         var store  = this.store;
20071         if (i > 0) {
20072             store  = new Roo.data.SimpleStore({
20073                 //fields : this.store.reader.meta.fields,
20074                 reader : this.store.reader,
20075                 data : [ ]
20076             });
20077         }
20078         this.stores[i]  = store;
20079                   
20080         var view = this.views[i] = new Roo.View(
20081             il,
20082             this.tpl,
20083             {
20084                 singleSelect:true,
20085                 store: store,
20086                 selectedClass: this.selectedClass
20087             }
20088         );
20089         view.getEl().setWidth(lw);
20090         view.getEl().setStyle({
20091             position: i < 1 ? 'relative' : 'absolute',
20092             top: 0,
20093             left: (i * lw ) + 'px',
20094             display : i > 0 ? 'none' : 'block'
20095         });
20096         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20097         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20098         //view.on('click', this.onViewClick, this, { list : i });
20099
20100         store.on('beforeload', this.onBeforeLoad, this);
20101         store.on('load',  this.onLoad, this, { list  : i});
20102         store.on('loadexception', this.onLoadException, this);
20103
20104         // hide the other vies..
20105         
20106         
20107         
20108     },
20109       
20110     restrictHeight : function()
20111     {
20112         var mh = 0;
20113         Roo.each(this.innerLists, function(il,i) {
20114             var el = this.views[i].getEl();
20115             el.dom.style.height = '';
20116             var inner = el.dom;
20117             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20118             // only adjust heights on other ones..
20119             mh = Math.max(h, mh);
20120             if (i < 1) {
20121                 
20122                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20123                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20124                
20125             }
20126             
20127             
20128         }, this);
20129         
20130         this.list.beginUpdate();
20131         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20132         this.list.alignTo(this.el, this.listAlign);
20133         this.list.endUpdate();
20134         
20135     },
20136      
20137     
20138     // -- store handlers..
20139     // private
20140     onBeforeLoad : function()
20141     {
20142         if(!this.hasFocus){
20143             return;
20144         }
20145         this.innerLists[0].update(this.loadingText ?
20146                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20147         this.restrictHeight();
20148         this.selectedIndex = -1;
20149     },
20150     // private
20151     onLoad : function(a,b,c,d)
20152     {
20153         if (!this.loadingChildren) {
20154             // then we are loading the top level. - hide the children
20155             for (var i = 1;i < this.views.length; i++) {
20156                 this.views[i].getEl().setStyle({ display : 'none' });
20157             }
20158             var lw = Math.floor(
20159                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20160             );
20161         
20162              this.list.setWidth(lw); // default to '1'
20163
20164             
20165         }
20166         if(!this.hasFocus){
20167             return;
20168         }
20169         
20170         if(this.store.getCount() > 0) {
20171             this.expand();
20172             this.restrictHeight();   
20173         } else {
20174             this.onEmptyResults();
20175         }
20176         
20177         if (!this.loadingChildren) {
20178             this.selectActive();
20179         }
20180         /*
20181         this.stores[1].loadData([]);
20182         this.stores[2].loadData([]);
20183         this.views
20184         */    
20185     
20186         //this.el.focus();
20187     },
20188     
20189     
20190     // private
20191     onLoadException : function()
20192     {
20193         this.collapse();
20194         Roo.log(this.store.reader.jsonData);
20195         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20196             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20197         }
20198         
20199         
20200     },
20201     // no cleaning of leading spaces on blur here.
20202     cleanLeadingSpace : function(e) { },
20203     
20204
20205     onSelectChange : function (view, sels, opts )
20206     {
20207         var ix = view.getSelectedIndexes();
20208          
20209         if (opts.list > this.maxColumns - 2) {
20210             if (view.store.getCount()<  1) {
20211                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20212
20213             } else  {
20214                 if (ix.length) {
20215                     // used to clear ?? but if we are loading unselected 
20216                     this.setFromData(view.store.getAt(ix[0]).data);
20217                 }
20218                 
20219             }
20220             
20221             return;
20222         }
20223         
20224         if (!ix.length) {
20225             // this get's fired when trigger opens..
20226            // this.setFromData({});
20227             var str = this.stores[opts.list+1];
20228             str.data.clear(); // removeall wihtout the fire events..
20229             return;
20230         }
20231         
20232         var rec = view.store.getAt(ix[0]);
20233          
20234         this.setFromData(rec.data);
20235         this.fireEvent('select', this, rec, ix[0]);
20236         
20237         var lw = Math.floor(
20238              (
20239                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20240              ) / this.maxColumns
20241         );
20242         this.loadingChildren = true;
20243         this.stores[opts.list+1].loadDataFromChildren( rec );
20244         this.loadingChildren = false;
20245         var dl = this.stores[opts.list+1]. getTotalCount();
20246         
20247         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20248         
20249         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20250         for (var i = opts.list+2; i < this.views.length;i++) {
20251             this.views[i].getEl().setStyle({ display : 'none' });
20252         }
20253         
20254         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20255         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20256         
20257         if (this.isLoading) {
20258            // this.selectActive(opts.list);
20259         }
20260          
20261     },
20262     
20263     
20264     
20265     
20266     onDoubleClick : function()
20267     {
20268         this.collapse(); //??
20269     },
20270     
20271      
20272     
20273     
20274     
20275     // private
20276     recordToStack : function(store, prop, value, stack)
20277     {
20278         var cstore = new Roo.data.SimpleStore({
20279             //fields : this.store.reader.meta.fields, // we need array reader.. for
20280             reader : this.store.reader,
20281             data : [ ]
20282         });
20283         var _this = this;
20284         var record  = false;
20285         var srec = false;
20286         if(store.getCount() < 1){
20287             return false;
20288         }
20289         store.each(function(r){
20290             if(r.data[prop] == value){
20291                 record = r;
20292             srec = r;
20293                 return false;
20294             }
20295             if (r.data.cn && r.data.cn.length) {
20296                 cstore.loadDataFromChildren( r);
20297                 var cret = _this.recordToStack(cstore, prop, value, stack);
20298                 if (cret !== false) {
20299                     record = cret;
20300                     srec = r;
20301                     return false;
20302                 }
20303             }
20304              
20305             return true;
20306         });
20307         if (record == false) {
20308             return false
20309         }
20310         stack.unshift(srec);
20311         return record;
20312     },
20313     
20314     /*
20315      * find the stack of stores that match our value.
20316      *
20317      * 
20318      */
20319     
20320     selectActive : function ()
20321     {
20322         // if store is not loaded, then we will need to wait for that to happen first.
20323         var stack = [];
20324         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20325         for (var i = 0; i < stack.length; i++ ) {
20326             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20327         }
20328         
20329     }
20330         
20331          
20332     
20333     
20334     
20335     
20336 });/*
20337  * Based on:
20338  * Ext JS Library 1.1.1
20339  * Copyright(c) 2006-2007, Ext JS, LLC.
20340  *
20341  * Originally Released Under LGPL - original licence link has changed is not relivant.
20342  *
20343  * Fork - LGPL
20344  * <script type="text/javascript">
20345  */
20346 /**
20347  * @class Roo.form.Checkbox
20348  * @extends Roo.form.Field
20349  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20350  * @constructor
20351  * Creates a new Checkbox
20352  * @param {Object} config Configuration options
20353  */
20354 Roo.form.Checkbox = function(config){
20355     Roo.form.Checkbox.superclass.constructor.call(this, config);
20356     this.addEvents({
20357         /**
20358          * @event check
20359          * Fires when the checkbox is checked or unchecked.
20360              * @param {Roo.form.Checkbox} this This checkbox
20361              * @param {Boolean} checked The new checked value
20362              */
20363         check : true
20364     });
20365 };
20366
20367 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20368     /**
20369      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20370      */
20371     focusClass : undefined,
20372     /**
20373      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20374      */
20375     fieldClass: "x-form-field",
20376     /**
20377      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20378      */
20379     checked: false,
20380     /**
20381      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20382      * {tag: "input", type: "checkbox", autocomplete: "off"})
20383      */
20384     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20385     /**
20386      * @cfg {String} boxLabel The text that appears beside the checkbox
20387      */
20388     boxLabel : "",
20389     /**
20390      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20391      */  
20392     inputValue : '1',
20393     /**
20394      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20395      */
20396      valueOff: '0', // value when not checked..
20397
20398     actionMode : 'viewEl', 
20399     //
20400     // private
20401     itemCls : 'x-menu-check-item x-form-item',
20402     groupClass : 'x-menu-group-item',
20403     inputType : 'hidden',
20404     
20405     
20406     inSetChecked: false, // check that we are not calling self...
20407     
20408     inputElement: false, // real input element?
20409     basedOn: false, // ????
20410     
20411     isFormField: true, // not sure where this is needed!!!!
20412
20413     onResize : function(){
20414         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20415         if(!this.boxLabel){
20416             this.el.alignTo(this.wrap, 'c-c');
20417         }
20418     },
20419
20420     initEvents : function(){
20421         Roo.form.Checkbox.superclass.initEvents.call(this);
20422         this.el.on("click", this.onClick,  this);
20423         this.el.on("change", this.onClick,  this);
20424     },
20425
20426
20427     getResizeEl : function(){
20428         return this.wrap;
20429     },
20430
20431     getPositionEl : function(){
20432         return this.wrap;
20433     },
20434
20435     // private
20436     onRender : function(ct, position){
20437         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20438         /*
20439         if(this.inputValue !== undefined){
20440             this.el.dom.value = this.inputValue;
20441         }
20442         */
20443         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20444         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20445         var viewEl = this.wrap.createChild({ 
20446             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20447         this.viewEl = viewEl;   
20448         this.wrap.on('click', this.onClick,  this); 
20449         
20450         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20451         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20452         
20453         
20454         
20455         if(this.boxLabel){
20456             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20457         //    viewEl.on('click', this.onClick,  this); 
20458         }
20459         //if(this.checked){
20460             this.setChecked(this.checked);
20461         //}else{
20462             //this.checked = this.el.dom;
20463         //}
20464
20465     },
20466
20467     // private
20468     initValue : Roo.emptyFn,
20469
20470     /**
20471      * Returns the checked state of the checkbox.
20472      * @return {Boolean} True if checked, else false
20473      */
20474     getValue : function(){
20475         if(this.el){
20476             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20477         }
20478         return this.valueOff;
20479         
20480     },
20481
20482         // private
20483     onClick : function(){ 
20484         if (this.disabled) {
20485             return;
20486         }
20487         this.setChecked(!this.checked);
20488
20489         //if(this.el.dom.checked != this.checked){
20490         //    this.setValue(this.el.dom.checked);
20491        // }
20492     },
20493
20494     /**
20495      * Sets the checked state of the checkbox.
20496      * On is always based on a string comparison between inputValue and the param.
20497      * @param {Boolean/String} value - the value to set 
20498      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20499      */
20500     setValue : function(v,suppressEvent){
20501         
20502         
20503         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20504         //if(this.el && this.el.dom){
20505         //    this.el.dom.checked = this.checked;
20506         //    this.el.dom.defaultChecked = this.checked;
20507         //}
20508         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20509         //this.fireEvent("check", this, this.checked);
20510     },
20511     // private..
20512     setChecked : function(state,suppressEvent)
20513     {
20514         if (this.inSetChecked) {
20515             this.checked = state;
20516             return;
20517         }
20518         
20519     
20520         if(this.wrap){
20521             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20522         }
20523         this.checked = state;
20524         if(suppressEvent !== true){
20525             this.fireEvent('check', this, state);
20526         }
20527         this.inSetChecked = true;
20528         this.el.dom.value = state ? this.inputValue : this.valueOff;
20529         this.inSetChecked = false;
20530         
20531     },
20532     // handle setting of hidden value by some other method!!?!?
20533     setFromHidden: function()
20534     {
20535         if(!this.el){
20536             return;
20537         }
20538         //console.log("SET FROM HIDDEN");
20539         //alert('setFrom hidden');
20540         this.setValue(this.el.dom.value);
20541     },
20542     
20543     onDestroy : function()
20544     {
20545         if(this.viewEl){
20546             Roo.get(this.viewEl).remove();
20547         }
20548          
20549         Roo.form.Checkbox.superclass.onDestroy.call(this);
20550     },
20551     
20552     setBoxLabel : function(str)
20553     {
20554         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20555     }
20556
20557 });/*
20558  * Based on:
20559  * Ext JS Library 1.1.1
20560  * Copyright(c) 2006-2007, Ext JS, LLC.
20561  *
20562  * Originally Released Under LGPL - original licence link has changed is not relivant.
20563  *
20564  * Fork - LGPL
20565  * <script type="text/javascript">
20566  */
20567  
20568 /**
20569  * @class Roo.form.Radio
20570  * @extends Roo.form.Checkbox
20571  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20572  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20573  * @constructor
20574  * Creates a new Radio
20575  * @param {Object} config Configuration options
20576  */
20577 Roo.form.Radio = function(){
20578     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20579 };
20580 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20581     inputType: 'radio',
20582
20583     /**
20584      * If this radio is part of a group, it will return the selected value
20585      * @return {String}
20586      */
20587     getGroupValue : function(){
20588         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20589     },
20590     
20591     
20592     onRender : function(ct, position){
20593         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20594         
20595         if(this.inputValue !== undefined){
20596             this.el.dom.value = this.inputValue;
20597         }
20598          
20599         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20600         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20601         //var viewEl = this.wrap.createChild({ 
20602         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20603         //this.viewEl = viewEl;   
20604         //this.wrap.on('click', this.onClick,  this); 
20605         
20606         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20607         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20608         
20609         
20610         
20611         if(this.boxLabel){
20612             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20613         //    viewEl.on('click', this.onClick,  this); 
20614         }
20615          if(this.checked){
20616             this.el.dom.checked =   'checked' ;
20617         }
20618          
20619     } 
20620     
20621     
20622 });Roo.rtf = {}; // namespace
20623 Roo.rtf.Hex = function(hex)
20624 {
20625     this.hexstr = hex;
20626 };
20627 Roo.rtf.Paragraph = function(opts)
20628 {
20629     this.content = []; ///??? is that used?
20630 };Roo.rtf.Span = function(opts)
20631 {
20632     this.value = opts.value;
20633 };
20634
20635 Roo.rtf.Group = function(parent)
20636 {
20637     // we dont want to acutally store parent - it will make debug a nightmare..
20638     this.content = [];
20639     this.cn  = [];
20640      
20641        
20642     
20643 };
20644
20645 Roo.rtf.Group.prototype = {
20646     ignorable : false,
20647     content: false,
20648     cn: false,
20649     addContent : function(node) {
20650         // could set styles...
20651         this.content.push(node);
20652     },
20653     addChild : function(cn)
20654     {
20655         this.cn.push(cn);
20656     },
20657     // only for images really...
20658     toDataURL : function()
20659     {
20660         var mimetype = false;
20661         switch(true) {
20662             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20663                 mimetype = "image/png";
20664                 break;
20665              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20666                 mimetype = "image/jpeg";
20667                 break;
20668             default :
20669                 return 'about:blank'; // ?? error?
20670         }
20671         
20672         
20673         var hexstring = this.content[this.content.length-1].value;
20674         
20675         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20676             return String.fromCharCode(parseInt(a, 16));
20677         }).join(""));
20678     }
20679     
20680 };
20681 // this looks like it's normally the {rtf{ .... }}
20682 Roo.rtf.Document = function()
20683 {
20684     // we dont want to acutally store parent - it will make debug a nightmare..
20685     this.rtlch  = [];
20686     this.content = [];
20687     this.cn = [];
20688     
20689 };
20690 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20691     addChild : function(cn)
20692     {
20693         this.cn.push(cn);
20694         switch(cn.type) {
20695             case 'rtlch': // most content seems to be inside this??
20696             case 'listtext':
20697             case 'shpinst':
20698                 this.rtlch.push(cn);
20699                 return;
20700             default:
20701                 this[cn.type] = cn;
20702         }
20703         
20704     },
20705     
20706     getElementsByType : function(type)
20707     {
20708         var ret =  [];
20709         this._getElementsByType(type, ret, this.cn, 'rtf');
20710         return ret;
20711     },
20712     _getElementsByType : function (type, ret, search_array, path)
20713     {
20714         search_array.forEach(function(n,i) {
20715             if (n.type == type) {
20716                 n.path = path + '/' + n.type + ':' + i;
20717                 ret.push(n);
20718             }
20719             if (n.cn.length > 0) {
20720                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20721             }
20722         },this);
20723     }
20724     
20725 });
20726  
20727 Roo.rtf.Ctrl = function(opts)
20728 {
20729     this.value = opts.value;
20730     this.param = opts.param;
20731 };
20732 /**
20733  *
20734  *
20735  * based on this https://github.com/iarna/rtf-parser
20736  * it's really only designed to extract pict from pasted RTF 
20737  *
20738  * usage:
20739  *
20740  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20741  *  
20742  *
20743  */
20744
20745  
20746
20747
20748
20749 Roo.rtf.Parser = function(text) {
20750     //super({objectMode: true})
20751     this.text = '';
20752     this.parserState = this.parseText;
20753     
20754     // these are for interpeter...
20755     this.doc = {};
20756     ///this.parserState = this.parseTop
20757     this.groupStack = [];
20758     this.hexStore = [];
20759     this.doc = false;
20760     
20761     this.groups = []; // where we put the return.
20762     
20763     for (var ii = 0; ii < text.length; ++ii) {
20764         ++this.cpos;
20765         
20766         if (text[ii] === '\n') {
20767             ++this.row;
20768             this.col = 1;
20769         } else {
20770             ++this.col;
20771         }
20772         this.parserState(text[ii]);
20773     }
20774     
20775     
20776     
20777 };
20778 Roo.rtf.Parser.prototype = {
20779     text : '', // string being parsed..
20780     controlWord : '',
20781     controlWordParam :  '',
20782     hexChar : '',
20783     doc : false,
20784     group: false,
20785     groupStack : false,
20786     hexStore : false,
20787     
20788     
20789     cpos : 0, 
20790     row : 1, // reportin?
20791     col : 1, //
20792
20793      
20794     push : function (el)
20795     {
20796         var m = 'cmd'+ el.type;
20797         if (typeof(this[m]) == 'undefined') {
20798             Roo.log('invalid cmd:' + el.type);
20799             return;
20800         }
20801         this[m](el);
20802         //Roo.log(el);
20803     },
20804     flushHexStore : function()
20805     {
20806         if (this.hexStore.length < 1) {
20807             return;
20808         }
20809         var hexstr = this.hexStore.map(
20810             function(cmd) {
20811                 return cmd.value;
20812         }).join('');
20813         
20814         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20815               
20816             
20817         this.hexStore.splice(0)
20818         
20819     },
20820     
20821     cmdgroupstart : function()
20822     {
20823         this.flushHexStore();
20824         if (this.group) {
20825             this.groupStack.push(this.group);
20826         }
20827          // parent..
20828         if (this.doc === false) {
20829             this.group = this.doc = new Roo.rtf.Document();
20830             return;
20831             
20832         }
20833         this.group = new Roo.rtf.Group(this.group);
20834     },
20835     cmdignorable : function()
20836     {
20837         this.flushHexStore();
20838         this.group.ignorable = true;
20839     },
20840     cmdendparagraph : function()
20841     {
20842         this.flushHexStore();
20843         this.group.addContent(new Roo.rtf.Paragraph());
20844     },
20845     cmdgroupend : function ()
20846     {
20847         this.flushHexStore();
20848         var endingGroup = this.group;
20849         
20850         
20851         this.group = this.groupStack.pop();
20852         if (this.group) {
20853             this.group.addChild(endingGroup);
20854         }
20855         
20856         
20857         
20858         var doc = this.group || this.doc;
20859         //if (endingGroup instanceof FontTable) {
20860         //  doc.fonts = endingGroup.table
20861         //} else if (endingGroup instanceof ColorTable) {
20862         //  doc.colors = endingGroup.table
20863         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20864         if (endingGroup.ignorable === false) {
20865             //code
20866             this.groups.push(endingGroup);
20867            // Roo.log( endingGroup );
20868         }
20869             //Roo.each(endingGroup.content, function(item)) {
20870             //    doc.addContent(item);
20871             //}
20872             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20873         //}
20874     },
20875     cmdtext : function (cmd)
20876     {
20877         this.flushHexStore();
20878         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20879             //this.group = this.doc
20880             return;  // we really don't care about stray text...
20881         }
20882         this.group.addContent(new Roo.rtf.Span(cmd));
20883     },
20884     cmdcontrolword : function (cmd)
20885     {
20886         this.flushHexStore();
20887         if (!this.group.type) {
20888             this.group.type = cmd.value;
20889             return;
20890         }
20891         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20892         // we actually don't care about ctrl words...
20893         return ;
20894         /*
20895         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20896         if (this[method]) {
20897             this[method](cmd.param)
20898         } else {
20899             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20900         }
20901         */
20902     },
20903     cmdhexchar : function(cmd) {
20904         this.hexStore.push(cmd);
20905     },
20906     cmderror : function(cmd) {
20907         throw cmd.value;
20908     },
20909     
20910     /*
20911       _flush (done) {
20912         if (this.text !== '\u0000') this.emitText()
20913         done()
20914       }
20915       */
20916       
20917       
20918     parseText : function(c)
20919     {
20920         if (c === '\\') {
20921             this.parserState = this.parseEscapes;
20922         } else if (c === '{') {
20923             this.emitStartGroup();
20924         } else if (c === '}') {
20925             this.emitEndGroup();
20926         } else if (c === '\x0A' || c === '\x0D') {
20927             // cr/lf are noise chars
20928         } else {
20929             this.text += c;
20930         }
20931     },
20932     
20933     parseEscapes: function (c)
20934     {
20935         if (c === '\\' || c === '{' || c === '}') {
20936             this.text += c;
20937             this.parserState = this.parseText;
20938         } else {
20939             this.parserState = this.parseControlSymbol;
20940             this.parseControlSymbol(c);
20941         }
20942     },
20943     parseControlSymbol: function(c)
20944     {
20945         if (c === '~') {
20946             this.text += '\u00a0'; // nbsp
20947             this.parserState = this.parseText
20948         } else if (c === '-') {
20949              this.text += '\u00ad'; // soft hyphen
20950         } else if (c === '_') {
20951             this.text += '\u2011'; // non-breaking hyphen
20952         } else if (c === '*') {
20953             this.emitIgnorable();
20954             this.parserState = this.parseText;
20955         } else if (c === "'") {
20956             this.parserState = this.parseHexChar;
20957         } else if (c === '|') { // formula cacter
20958             this.emitFormula();
20959             this.parserState = this.parseText;
20960         } else if (c === ':') { // subentry in an index entry
20961             this.emitIndexSubEntry();
20962             this.parserState = this.parseText;
20963         } else if (c === '\x0a') {
20964             this.emitEndParagraph();
20965             this.parserState = this.parseText;
20966         } else if (c === '\x0d') {
20967             this.emitEndParagraph();
20968             this.parserState = this.parseText;
20969         } else {
20970             this.parserState = this.parseControlWord;
20971             this.parseControlWord(c);
20972         }
20973     },
20974     parseHexChar: function (c)
20975     {
20976         if (/^[A-Fa-f0-9]$/.test(c)) {
20977             this.hexChar += c;
20978             if (this.hexChar.length >= 2) {
20979               this.emitHexChar();
20980               this.parserState = this.parseText;
20981             }
20982             return;
20983         }
20984         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20985         this.parserState = this.parseText;
20986         
20987     },
20988     parseControlWord : function(c)
20989     {
20990         if (c === ' ') {
20991             this.emitControlWord();
20992             this.parserState = this.parseText;
20993         } else if (/^[-\d]$/.test(c)) {
20994             this.parserState = this.parseControlWordParam;
20995             this.controlWordParam += c;
20996         } else if (/^[A-Za-z]$/.test(c)) {
20997           this.controlWord += c;
20998         } else {
20999           this.emitControlWord();
21000           this.parserState = this.parseText;
21001           this.parseText(c);
21002         }
21003     },
21004     parseControlWordParam : function (c) {
21005         if (/^\d$/.test(c)) {
21006           this.controlWordParam += c;
21007         } else if (c === ' ') {
21008           this.emitControlWord();
21009           this.parserState = this.parseText;
21010         } else {
21011           this.emitControlWord();
21012           this.parserState = this.parseText;
21013           this.parseText(c);
21014         }
21015     },
21016     
21017     
21018     
21019     
21020     emitText : function () {
21021         if (this.text === '') {
21022             return;
21023         }
21024         this.push({
21025             type: 'text',
21026             value: this.text,
21027             pos: this.cpos,
21028             row: this.row,
21029             col: this.col
21030         });
21031         this.text = ''
21032     },
21033     emitControlWord : function ()
21034     {
21035         this.emitText();
21036         if (this.controlWord === '') {
21037             // do we want to track this - it seems just to cause problems.
21038             //this.emitError('empty control word');
21039         } else {
21040             this.push({
21041                   type: 'controlword',
21042                   value: this.controlWord,
21043                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
21044                   pos: this.cpos,
21045                   row: this.row,
21046                   col: this.col
21047             });
21048         }
21049         this.controlWord = '';
21050         this.controlWordParam = '';
21051     },
21052     emitStartGroup : function ()
21053     {
21054         this.emitText();
21055         this.push({
21056             type: 'groupstart',
21057             pos: this.cpos,
21058             row: this.row,
21059             col: this.col
21060         });
21061     },
21062     emitEndGroup : function ()
21063     {
21064         this.emitText();
21065         this.push({
21066             type: 'groupend',
21067             pos: this.cpos,
21068             row: this.row,
21069             col: this.col
21070         });
21071     },
21072     emitIgnorable : function ()
21073     {
21074         this.emitText();
21075         this.push({
21076             type: 'ignorable',
21077             pos: this.cpos,
21078             row: this.row,
21079             col: this.col
21080         });
21081     },
21082     emitHexChar : function ()
21083     {
21084         this.emitText();
21085         this.push({
21086             type: 'hexchar',
21087             value: this.hexChar,
21088             pos: this.cpos,
21089             row: this.row,
21090             col: this.col
21091         });
21092         this.hexChar = ''
21093     },
21094     emitError : function (message)
21095     {
21096       this.emitText();
21097       this.push({
21098             type: 'error',
21099             value: message,
21100             row: this.row,
21101             col: this.col,
21102             char: this.cpos //,
21103             //stack: new Error().stack
21104         });
21105     },
21106     emitEndParagraph : function () {
21107         this.emitText();
21108         this.push({
21109             type: 'endparagraph',
21110             pos: this.cpos,
21111             row: this.row,
21112             col: this.col
21113         });
21114     }
21115      
21116 } ;
21117 Roo.htmleditor = {};
21118  
21119 /**
21120  * @class Roo.htmleditor.Filter
21121  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21122  * @cfg {DomElement} node The node to iterate and filter
21123  * @cfg {boolean|String|Array} tag Tags to replace 
21124  * @constructor
21125  * Create a new Filter.
21126  * @param {Object} config Configuration options
21127  */
21128
21129
21130
21131 Roo.htmleditor.Filter = function(cfg) {
21132     Roo.apply(this.cfg);
21133     // this does not actually call walk as it's really just a abstract class
21134 }
21135
21136
21137 Roo.htmleditor.Filter.prototype = {
21138     
21139     node: false,
21140     
21141     tag: false,
21142
21143     // overrride to do replace comments.
21144     replaceComment : false,
21145     
21146     // overrride to do replace or do stuff with tags..
21147     replaceTag : false,
21148     
21149     walk : function(dom)
21150     {
21151         Roo.each( Array.from(dom.childNodes), function( e ) {
21152             switch(true) {
21153                 
21154                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
21155                     this.replaceComment(e);
21156                     return;
21157                 
21158                 case e.nodeType != 1: //not a node.
21159                     return;
21160                 
21161                 case this.tag === true: // everything
21162                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21163                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21164                     if (this.replaceTag && false === this.replaceTag(e)) {
21165                         return;
21166                     }
21167                     if (e.hasChildNodes()) {
21168                         this.walk(e);
21169                     }
21170                     return;
21171                 
21172                 default:    // tags .. that do not match.
21173                     if (e.hasChildNodes()) {
21174                         this.walk(e);
21175                     }
21176             }
21177             
21178         }, this);
21179         
21180     }
21181 }; 
21182
21183 /**
21184  * @class Roo.htmleditor.FilterAttributes
21185  * clean attributes and  styles including http:// etc.. in attribute
21186  * @constructor
21187 * Run a new Attribute Filter
21188 * @param {Object} config Configuration options
21189  */
21190 Roo.htmleditor.FilterAttributes = function(cfg)
21191 {
21192     Roo.apply(this, cfg);
21193     this.attrib_black = this.attrib_black || [];
21194     this.attrib_white = this.attrib_white || [];
21195
21196     this.attrib_clean = this.attrib_clean || [];
21197     this.style_white = this.style_white || [];
21198     this.style_black = this.style_black || [];
21199     this.walk(cfg.node);
21200 }
21201
21202 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21203 {
21204     tag: true, // all tags
21205     
21206     attrib_black : false, // array
21207     attrib_clean : false,
21208     attrib_white : false,
21209
21210     style_white : false,
21211     style_black : false,
21212      
21213      
21214     replaceTag : function(node)
21215     {
21216         if (!node.attributes || !node.attributes.length) {
21217             return true;
21218         }
21219         
21220         for (var i = node.attributes.length-1; i > -1 ; i--) {
21221             var a = node.attributes[i];
21222             //console.log(a);
21223             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21224                 node.removeAttribute(a.name);
21225                 continue;
21226             }
21227             
21228             
21229             
21230             if (a.name.toLowerCase().substr(0,2)=='on')  {
21231                 node.removeAttribute(a.name);
21232                 continue;
21233             }
21234             
21235             
21236             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21237                 node.removeAttribute(a.name);
21238                 continue;
21239             }
21240             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21241                 this.cleanAttr(node,a.name,a.value); // fixme..
21242                 continue;
21243             }
21244             if (a.name == 'style') {
21245                 this.cleanStyle(node,a.name,a.value);
21246                 continue;
21247             }
21248             /// clean up MS crap..
21249             // tecnically this should be a list of valid class'es..
21250             
21251             
21252             if (a.name == 'class') {
21253                 if (a.value.match(/^Mso/)) {
21254                     node.removeAttribute('class');
21255                 }
21256                 
21257                 if (a.value.match(/^body$/)) {
21258                     node.removeAttribute('class');
21259                 }
21260                 continue;
21261             }
21262             
21263             
21264             // style cleanup!?
21265             // class cleanup?
21266             
21267         }
21268         return true; // clean children
21269     },
21270         
21271     cleanAttr: function(node, n,v)
21272     {
21273         
21274         if (v.match(/^\./) || v.match(/^\//)) {
21275             return;
21276         }
21277         if (v.match(/^(http|https):\/\//)
21278             || v.match(/^mailto:/) 
21279             || v.match(/^ftp:/)
21280             || v.match(/^data:/)
21281             ) {
21282             return;
21283         }
21284         if (v.match(/^#/)) {
21285             return;
21286         }
21287         if (v.match(/^\{/)) { // allow template editing.
21288             return;
21289         }
21290 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21291         node.removeAttribute(n);
21292         
21293     },
21294     cleanStyle : function(node,  n,v)
21295     {
21296         if (v.match(/expression/)) { //XSS?? should we even bother..
21297             node.removeAttribute(n);
21298             return;
21299         }
21300         
21301         var parts = v.split(/;/);
21302         var clean = [];
21303         
21304         Roo.each(parts, function(p) {
21305             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21306             if (!p.length) {
21307                 return true;
21308             }
21309             var l = p.split(':').shift().replace(/\s+/g,'');
21310             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21311             
21312             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21313                 return true;
21314             }
21315             //Roo.log()
21316             // only allow 'c whitelisted system attributes'
21317             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21318                 return true;
21319             }
21320             
21321             
21322             clean.push(p);
21323             return true;
21324         },this);
21325         if (clean.length) { 
21326             node.setAttribute(n, clean.join(';'));
21327         } else {
21328             node.removeAttribute(n);
21329         }
21330         
21331     }
21332         
21333         
21334         
21335     
21336 });/**
21337  * @class Roo.htmleditor.FilterBlack
21338  * remove blacklisted elements.
21339  * @constructor
21340  * Run a new Blacklisted Filter
21341  * @param {Object} config Configuration options
21342  */
21343
21344 Roo.htmleditor.FilterBlack = function(cfg)
21345 {
21346     Roo.apply(this, cfg);
21347     this.walk(cfg.node);
21348 }
21349
21350 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21351 {
21352     tag : true, // all elements.
21353    
21354     replaceTag : function(n)
21355     {
21356         n.parentNode.removeChild(n);
21357     }
21358 });
21359 /**
21360  * @class Roo.htmleditor.FilterComment
21361  * remove comments.
21362  * @constructor
21363 * Run a new Comments Filter
21364 * @param {Object} config Configuration options
21365  */
21366 Roo.htmleditor.FilterComment = function(cfg)
21367 {
21368     this.walk(cfg.node);
21369 }
21370
21371 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21372 {
21373   
21374     replaceComment : function(n)
21375     {
21376         n.parentNode.removeChild(n);
21377     }
21378 });/**
21379  * @class Roo.htmleditor.FilterKeepChildren
21380  * remove tags but keep children
21381  * @constructor
21382  * Run a new Keep Children Filter
21383  * @param {Object} config Configuration options
21384  */
21385
21386 Roo.htmleditor.FilterKeepChildren = function(cfg)
21387 {
21388     Roo.apply(this, cfg);
21389     if (this.tag === false) {
21390         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21391     }
21392     this.walk(cfg.node);
21393 }
21394
21395 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21396 {
21397     
21398   
21399     replaceTag : function(node)
21400     {
21401         // walk children...
21402         //Roo.log(node);
21403         var ar = Array.from(node.childNodes);
21404         //remove first..
21405         for (var i = 0; i < ar.length; i++) {
21406             if (ar[i].nodeType == 1) {
21407                 if (
21408                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21409                     || // array and it matches
21410                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21411                 ) {
21412                     this.replaceTag(ar[i]); // child is blacklisted as well...
21413                     continue;
21414                 }
21415             }
21416         }  
21417         ar = Array.from(node.childNodes);
21418         for (var i = 0; i < ar.length; i++) {
21419          
21420             node.removeChild(ar[i]);
21421             // what if we need to walk these???
21422             node.parentNode.insertBefore(ar[i], node);
21423             if (this.tag !== false) {
21424                 this.walk(ar[i]);
21425                 
21426             }
21427         }
21428         node.parentNode.removeChild(node);
21429         return false; // don't walk children
21430         
21431         
21432     }
21433 });/**
21434  * @class Roo.htmleditor.FilterParagraph
21435  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21436  * like on 'push' to remove the <p> tags and replace them with line breaks.
21437  * @constructor
21438  * Run a new Paragraph Filter
21439  * @param {Object} config Configuration options
21440  */
21441
21442 Roo.htmleditor.FilterParagraph = function(cfg)
21443 {
21444     // no need to apply config.
21445     this.walk(cfg.node);
21446 }
21447
21448 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21449 {
21450     
21451      
21452     tag : 'P',
21453     
21454      
21455     replaceTag : function(node)
21456     {
21457         
21458         if (node.childNodes.length == 1 &&
21459             node.childNodes[0].nodeType == 3 &&
21460             node.childNodes[0].textContent.trim().length < 1
21461             ) {
21462             // remove and replace with '<BR>';
21463             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21464             return false; // no need to walk..
21465         }
21466         var ar = Array.from(node.childNodes);
21467         for (var i = 0; i < ar.length; i++) {
21468             node.removeChild(ar[i]);
21469             // what if we need to walk these???
21470             node.parentNode.insertBefore(ar[i], node);
21471         }
21472         // now what about this?
21473         // <p> &nbsp; </p>
21474         
21475         // double BR.
21476         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21477         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21478         node.parentNode.removeChild(node);
21479         
21480         return false;
21481
21482     }
21483     
21484 });/**
21485  * @class Roo.htmleditor.FilterSpan
21486  * filter span's with no attributes out..
21487  * @constructor
21488  * Run a new Span Filter
21489  * @param {Object} config Configuration options
21490  */
21491
21492 Roo.htmleditor.FilterSpan = function(cfg)
21493 {
21494     // no need to apply config.
21495     this.walk(cfg.node);
21496 }
21497
21498 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21499 {
21500      
21501     tag : 'SPAN',
21502      
21503  
21504     replaceTag : function(node)
21505     {
21506         if (node.attributes && node.attributes.length > 0) {
21507             return true; // walk if there are any.
21508         }
21509         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21510         return false;
21511      
21512     }
21513     
21514 });/**
21515  * @class Roo.htmleditor.FilterTableWidth
21516   try and remove table width data - as that frequently messes up other stuff.
21517  * 
21518  *      was cleanTableWidths.
21519  *
21520  * Quite often pasting from word etc.. results in tables with column and widths.
21521  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21522  *
21523  * @constructor
21524  * Run a new Table Filter
21525  * @param {Object} config Configuration options
21526  */
21527
21528 Roo.htmleditor.FilterTableWidth = function(cfg)
21529 {
21530     // no need to apply config.
21531     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21532     this.walk(cfg.node);
21533 }
21534
21535 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21536 {
21537      
21538      
21539     
21540     replaceTag: function(node) {
21541         
21542         
21543       
21544         if (node.hasAttribute('width')) {
21545             node.removeAttribute('width');
21546         }
21547         
21548          
21549         if (node.hasAttribute("style")) {
21550             // pretty basic...
21551             
21552             var styles = node.getAttribute("style").split(";");
21553             var nstyle = [];
21554             Roo.each(styles, function(s) {
21555                 if (!s.match(/:/)) {
21556                     return;
21557                 }
21558                 var kv = s.split(":");
21559                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21560                     return;
21561                 }
21562                 // what ever is left... we allow.
21563                 nstyle.push(s);
21564             });
21565             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21566             if (!nstyle.length) {
21567                 node.removeAttribute('style');
21568             }
21569         }
21570         
21571         return true; // continue doing children..
21572     }
21573 });/**
21574  * @class Roo.htmleditor.FilterWord
21575  * try and clean up all the mess that Word generates.
21576  * 
21577  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21578  
21579  * @constructor
21580  * Run a new Span Filter
21581  * @param {Object} config Configuration options
21582  */
21583
21584 Roo.htmleditor.FilterWord = function(cfg)
21585 {
21586     // no need to apply config.
21587     this.replaceDocBullets(cfg.node);
21588     
21589    // this.walk(cfg.node);
21590     
21591     
21592 }
21593
21594 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21595 {
21596     tag: true,
21597      
21598     
21599     /**
21600      * Clean up MS wordisms...
21601      */
21602     replaceTag : function(node)
21603     {
21604          
21605         // no idea what this does - span with text, replaceds with just text.
21606         if(
21607                 node.nodeName == 'SPAN' &&
21608                 !node.hasAttributes() &&
21609                 node.childNodes.length == 1 &&
21610                 node.firstChild.nodeName == "#text"  
21611         ) {
21612             var textNode = node.firstChild;
21613             node.removeChild(textNode);
21614             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21615                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21616             }
21617             node.parentNode.insertBefore(textNode, node);
21618             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21619                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21620             }
21621             
21622             node.parentNode.removeChild(node);
21623             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21624         }
21625         
21626    
21627         
21628         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21629             node.parentNode.removeChild(node);
21630             return false; // dont do chidlren
21631         }
21632         //Roo.log(node.tagName);
21633         // remove - but keep children..
21634         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21635             //Roo.log('-- removed');
21636             while (node.childNodes.length) {
21637                 var cn = node.childNodes[0];
21638                 node.removeChild(cn);
21639                 node.parentNode.insertBefore(cn, node);
21640                 // move node to parent - and clean it..
21641                 if (cn.nodeType == 1) {
21642                     this.replaceTag(cn);
21643                 }
21644                 
21645             }
21646             node.parentNode.removeChild(node);
21647             /// no need to iterate chidlren = it's got none..
21648             //this.iterateChildren(node, this.cleanWord);
21649             return false; // no need to iterate children.
21650         }
21651         // clean styles
21652         if (node.className.length) {
21653             
21654             var cn = node.className.split(/\W+/);
21655             var cna = [];
21656             Roo.each(cn, function(cls) {
21657                 if (cls.match(/Mso[a-zA-Z]+/)) {
21658                     return;
21659                 }
21660                 cna.push(cls);
21661             });
21662             node.className = cna.length ? cna.join(' ') : '';
21663             if (!cna.length) {
21664                 node.removeAttribute("class");
21665             }
21666         }
21667         
21668         if (node.hasAttribute("lang")) {
21669             node.removeAttribute("lang");
21670         }
21671         
21672         if (node.hasAttribute("style")) {
21673             
21674             var styles = node.getAttribute("style").split(";");
21675             var nstyle = [];
21676             Roo.each(styles, function(s) {
21677                 if (!s.match(/:/)) {
21678                     return;
21679                 }
21680                 var kv = s.split(":");
21681                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21682                     return;
21683                 }
21684                 // what ever is left... we allow.
21685                 nstyle.push(s);
21686             });
21687             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21688             if (!nstyle.length) {
21689                 node.removeAttribute('style');
21690             }
21691         }
21692         return true; // do children
21693         
21694         
21695         
21696     },
21697     
21698     styleToObject: function(node)
21699     {
21700         var styles = (node.getAttribute("style") || '').split(";");
21701         var ret = {};
21702         Roo.each(styles, function(s) {
21703             if (!s.match(/:/)) {
21704                 return;
21705             }
21706             var kv = s.split(":");
21707              
21708             // what ever is left... we allow.
21709             ret[kv[0]] = kv[1];
21710         });
21711         return ret;
21712     },
21713     
21714     
21715     replaceDocBullets : function(doc)
21716     {
21717         // this is a bit odd - but it appears some indents use ql-indent-1
21718         
21719         var listpara = doc.getElementsByClassName('ql-indent-1');
21720         while(listpara.length) {
21721             this.replaceDocBullet(listpara.item(0));
21722         }
21723         
21724         var listpara = doc.getElementsByClassName('MsoListParagraph');
21725         while(listpara.length) {
21726             this.replaceDocBullet(listpara.item(0));
21727         }
21728     },
21729     
21730     replaceDocBullet : function(p)
21731     {
21732         // gather all the siblings.
21733         var ns = p,
21734             parent = p.parentNode,
21735             doc = parent.ownerDocument,
21736             items = []; 
21737         while (ns) {
21738             if (ns.nodeType != 1) {
21739                 ns = ns.nextSibling;
21740                 continue;
21741             }
21742             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
21743                 break;
21744             }
21745             items.push(ns);
21746             ns = ns.nextSibling;
21747             
21748         }
21749         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
21750         parent.insertBefore(ul, p);
21751         var lvl = 0;
21752         var stack = [ ul ];
21753         var last_li = false;
21754         items.forEach(function(n) {
21755             //Roo.log("got innertHMLT=" + n.innerHTML);
21756             
21757             var spans = n.getElementsByTagName('span');
21758             if (!spans.length) {
21759                 //Roo.log("No spans found");
21760
21761                 parent.removeChild(n);
21762                 return; // skip it...
21763             }
21764            
21765                 
21766             
21767             var style = {};
21768             for(var i = 0; i < spans.length; i++) {
21769             
21770                 style = this.styleToObject(spans[i]);
21771                 if (typeof(style['mso-list']) == 'undefined') {
21772                     continue;
21773                 }
21774                 
21775                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
21776                 break;
21777             }
21778             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
21779             style = this.styleToObject(n); // mo-list is from the parent node.
21780             if (typeof(style['mso-list']) == 'undefined') {
21781                 //Roo.log("parent is missing level");
21782                 parent.removeChild(n);
21783                 return;
21784             }
21785             
21786             var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1;
21787             if (nlvl > lvl) {
21788                 //new indent
21789                 var nul = doc.createElement('ul'); // what about number lists...
21790                 last_li.appendChild(nul);
21791                 stack[nlvl] = nul;
21792             }
21793             lvl = nlvl;
21794             
21795             var nli = stack[nlvl].appendChild(doc.createElement('li'));
21796             last_li = nli;
21797             nli.innerHTML = n.innerHTML;
21798             //Roo.log("innerHTML = " + n.innerHTML);
21799             parent.removeChild(n);
21800             
21801             // copy children of p into nli
21802             /*while(n.firstChild) {
21803                 var fc = n.firstChild;
21804                 n.removeChild(fc);
21805                 nli.appendChild(fc);
21806             }*/
21807              
21808             
21809         },this);
21810         
21811         
21812         
21813         
21814     }
21815     
21816     
21817     
21818 });
21819 /**
21820  * @class Roo.htmleditor.FilterStyleToTag
21821  * part of the word stuff... - certain 'styles' should be converted to tags.
21822  * eg.
21823  *   font-weight: bold -> bold
21824  *   ?? super / subscrit etc..
21825  * 
21826  * @constructor
21827 * Run a new style to tag filter.
21828 * @param {Object} config Configuration options
21829  */
21830 Roo.htmleditor.FilterStyleToTag = function(cfg)
21831 {
21832     
21833     this.tags = {
21834         B  : [ 'fontWeight' , 'bold'],
21835         I :  [ 'fontStyle' , 'italic'],
21836         //pre :  [ 'font-style' , 'italic'],
21837         // h1.. h6 ?? font-size?
21838         SUP : [ 'verticalAlign' , 'super' ],
21839         SUB : [ 'verticalAlign' , 'sub' ]
21840         
21841         
21842     };
21843     
21844     Roo.apply(this, cfg);
21845      
21846     
21847     this.walk(cfg.node);
21848     
21849     
21850     
21851 }
21852
21853
21854 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21855 {
21856     tag: true, // all tags
21857     
21858     tags : false,
21859     
21860     
21861     replaceTag : function(node)
21862     {
21863         
21864         
21865         if (node.getAttribute("style") === null) {
21866             return true;
21867         }
21868         var inject = [];
21869         for (var k in this.tags) {
21870             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21871                 inject.push(k);
21872                 node.style.removeProperty(this.tags[k][0]);
21873             }
21874         }
21875         if (!inject.length) {
21876             return true; 
21877         }
21878         var cn = Array.from(node.childNodes);
21879         var nn = node;
21880         Roo.each(inject, function(t) {
21881             var nc = node.ownerDocument.createElement(t);
21882             nn.appendChild(nc);
21883             nn = nc;
21884         });
21885         for(var i = 0;i < cn.length;cn++) {
21886             node.removeChild(cn[i]);
21887             nn.appendChild(cn[i]);
21888         }
21889         return true /// iterate thru
21890     }
21891     
21892 })/**
21893  * @class Roo.htmleditor.FilterLongBr
21894  * BR/BR/BR - keep a maximum of 2...
21895  * @constructor
21896  * Run a new Long BR Filter
21897  * @param {Object} config Configuration options
21898  */
21899
21900 Roo.htmleditor.FilterLongBr = function(cfg)
21901 {
21902     // no need to apply config.
21903     this.walk(cfg.node);
21904 }
21905
21906 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21907 {
21908     
21909      
21910     tag : 'BR',
21911     
21912      
21913     replaceTag : function(node)
21914     {
21915         
21916         var ps = node.nextSibling;
21917         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21918             ps = ps.nextSibling;
21919         }
21920         
21921         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21922             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21923             return false;
21924         }
21925         
21926         if (!ps || ps.nodeType != 1) {
21927             return false;
21928         }
21929         
21930         if (!ps || ps.tagName != 'BR') {
21931            
21932             return false;
21933         }
21934         
21935         
21936         
21937         
21938         
21939         if (!node.previousSibling) {
21940             return false;
21941         }
21942         var ps = node.previousSibling;
21943         
21944         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21945             ps = ps.previousSibling;
21946         }
21947         if (!ps || ps.nodeType != 1) {
21948             return false;
21949         }
21950         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21951         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21952             return false;
21953         }
21954         
21955         node.parentNode.removeChild(node); // remove me...
21956         
21957         return false; // no need to do children
21958
21959     }
21960     
21961 }); 
21962
21963 /**
21964  * @class Roo.htmleditor.FilterBlock
21965  * removes id / data-block and contenteditable that are associated with blocks
21966  * usage should be done on a cloned copy of the dom
21967  * @constructor
21968 * Run a new Attribute Filter { node : xxxx }}
21969 * @param {Object} config Configuration options
21970  */
21971 Roo.htmleditor.FilterBlock = function(cfg)
21972 {
21973     Roo.apply(this, cfg);
21974     var qa = cfg.node.querySelectorAll;
21975     this.removeAttributes('data-block');
21976     this.removeAttributes('contenteditable');
21977     this.removeAttributes('id');
21978     
21979 }
21980
21981 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21982 {
21983     node: true, // all tags
21984      
21985      
21986     removeAttributes : function(attr)
21987     {
21988         var ar = this.node.querySelectorAll('*[' + attr + ']');
21989         for (var i =0;i<ar.length;i++) {
21990             ar[i].removeAttribute(attr);
21991         }
21992     }
21993         
21994         
21995         
21996     
21997 });
21998 /***
21999  * This is based loosely on tinymce 
22000  * @class Roo.htmleditor.TidySerializer
22001  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22002  * @constructor
22003  * @method Serializer
22004  * @param {Object} settings Name/value settings object.
22005  */
22006
22007
22008 Roo.htmleditor.TidySerializer = function(settings)
22009 {
22010     Roo.apply(this, settings);
22011     
22012     this.writer = new Roo.htmleditor.TidyWriter(settings);
22013     
22014     
22015
22016 };
22017 Roo.htmleditor.TidySerializer.prototype = {
22018     
22019     /**
22020      * @param {boolean} inner do the inner of the node.
22021      */
22022     inner : false,
22023     
22024     writer : false,
22025     
22026     /**
22027     * Serializes the specified node into a string.
22028     *
22029     * @example
22030     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
22031     * @method serialize
22032     * @param {DomElement} node Node instance to serialize.
22033     * @return {String} String with HTML based on DOM tree.
22034     */
22035     serialize : function(node) {
22036         
22037         // = settings.validate;
22038         var writer = this.writer;
22039         var self  = this;
22040         this.handlers = {
22041             // #text
22042             3: function(node) {
22043                 
22044                 writer.text(node.nodeValue, node);
22045             },
22046             // #comment
22047             8: function(node) {
22048                 writer.comment(node.nodeValue);
22049             },
22050             // Processing instruction
22051             7: function(node) {
22052                 writer.pi(node.name, node.nodeValue);
22053             },
22054             // Doctype
22055             10: function(node) {
22056                 writer.doctype(node.nodeValue);
22057             },
22058             // CDATA
22059             4: function(node) {
22060                 writer.cdata(node.nodeValue);
22061             },
22062             // Document fragment
22063             11: function(node) {
22064                 node = node.firstChild;
22065                 if (!node) {
22066                     return;
22067                 }
22068                 while(node) {
22069                     self.walk(node);
22070                     node = node.nextSibling
22071                 }
22072             }
22073         };
22074         writer.reset();
22075         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
22076         return writer.getContent();
22077     },
22078
22079     walk: function(node)
22080     {
22081         var attrName, attrValue, sortedAttrs, i, l, elementRule,
22082             handler = this.handlers[node.nodeType];
22083             
22084         if (handler) {
22085             handler(node);
22086             return;
22087         }
22088     
22089         var name = node.nodeName;
22090         var isEmpty = node.childNodes.length < 1;
22091       
22092         var writer = this.writer;
22093         var attrs = node.attributes;
22094         // Sort attributes
22095         
22096         writer.start(node.nodeName, attrs, isEmpty, node);
22097         if (isEmpty) {
22098             return;
22099         }
22100         node = node.firstChild;
22101         if (!node) {
22102             writer.end(name);
22103             return;
22104         }
22105         while (node) {
22106             this.walk(node);
22107             node = node.nextSibling;
22108         }
22109         writer.end(name);
22110         
22111     
22112     }
22113     // Serialize element and treat all non elements as fragments
22114    
22115 }; 
22116
22117 /***
22118  * This is based loosely on tinymce 
22119  * @class Roo.htmleditor.TidyWriter
22120  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22121  *
22122  * Known issues?
22123  * - not tested much with 'PRE' formated elements.
22124  * 
22125  *
22126  *
22127  */
22128
22129 Roo.htmleditor.TidyWriter = function(settings)
22130 {
22131     
22132     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
22133     Roo.apply(this, settings);
22134     this.html = [];
22135     this.state = [];
22136      
22137     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
22138   
22139 }
22140 Roo.htmleditor.TidyWriter.prototype = {
22141
22142  
22143     state : false,
22144     
22145     indent :  '  ',
22146     
22147     // part of state...
22148     indentstr : '',
22149     in_pre: false,
22150     in_inline : false,
22151     last_inline : false,
22152     encode : false,
22153      
22154     
22155             /**
22156     * Writes the a start element such as <p id="a">.
22157     *
22158     * @method start
22159     * @param {String} name Name of the element.
22160     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22161     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22162     */
22163     start: function(name, attrs, empty, node)
22164     {
22165         var i, l, attr, value;
22166         
22167         // there are some situations where adding line break && indentation will not work. will not work.
22168         // <span / b / i ... formating?
22169         
22170         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22171         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22172         
22173         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22174         
22175         var add_lb = name == 'BR' ? false : in_inline;
22176         
22177         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22178             i_inline = false;
22179         }
22180
22181         var indentstr =  this.indentstr;
22182         
22183         // e_inline = elements that can be inline, but still allow \n before and after?
22184         // only 'BR' ??? any others?
22185         
22186         // ADD LINE BEFORE tage
22187         if (!this.in_pre) {
22188             if (in_inline) {
22189                 //code
22190                 if (name == 'BR') {
22191                     this.addLine();
22192                 } else if (this.lastElementEndsWS()) {
22193                     this.addLine();
22194                 } else{
22195                     // otherwise - no new line. (and dont indent.)
22196                     indentstr = '';
22197                 }
22198                 
22199             } else {
22200                 this.addLine();
22201             }
22202         } else {
22203             indentstr = '';
22204         }
22205         
22206         this.html.push(indentstr + '<', name.toLowerCase());
22207         
22208         if (attrs) {
22209             for (i = 0, l = attrs.length; i < l; i++) {
22210                 attr = attrs[i];
22211                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22212             }
22213         }
22214      
22215         if (empty) {
22216             if (is_short) {
22217                 this.html[this.html.length] = '/>';
22218             } else {
22219                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22220             }
22221             var e_inline = name == 'BR' ? false : this.in_inline;
22222             
22223             if (!e_inline && !this.in_pre) {
22224                 this.addLine();
22225             }
22226             return;
22227         
22228         }
22229         // not empty..
22230         this.html[this.html.length] = '>';
22231         
22232         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22233         /*
22234         if (!in_inline && !in_pre) {
22235             var cn = node.firstChild;
22236             while(cn) {
22237                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22238                     in_inline = true
22239                     break;
22240                 }
22241                 cn = cn.nextSibling;
22242             }
22243              
22244         }
22245         */
22246         
22247         
22248         this.pushState({
22249             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
22250             in_pre : in_pre,
22251             in_inline :  in_inline
22252         });
22253         // add a line after if we are not in a
22254         
22255         if (!in_inline && !in_pre) {
22256             this.addLine();
22257         }
22258         
22259             
22260          
22261         
22262     },
22263     
22264     lastElementEndsWS : function()
22265     {
22266         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22267         if (value === false) {
22268             return true;
22269         }
22270         return value.match(/\s+$/);
22271         
22272     },
22273     
22274     /**
22275      * Writes the a end element such as </p>.
22276      *
22277      * @method end
22278      * @param {String} name Name of the element.
22279      */
22280     end: function(name) {
22281         var value;
22282         this.popState();
22283         var indentstr = '';
22284         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22285         
22286         if (!this.in_pre && !in_inline) {
22287             this.addLine();
22288             indentstr  = this.indentstr;
22289         }
22290         this.html.push(indentstr + '</', name.toLowerCase(), '>');
22291         this.last_inline = in_inline;
22292         
22293         // pop the indent state..
22294     },
22295     /**
22296      * Writes a text node.
22297      *
22298      * In pre - we should not mess with the contents.
22299      * 
22300      *
22301      * @method text
22302      * @param {String} text String to write out.
22303      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22304      */
22305     text: function(in_text, node)
22306     {
22307         // if not in whitespace critical
22308         if (in_text.length < 1) {
22309             return;
22310         }
22311         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
22312         
22313         if (this.in_pre) {
22314             this.html[this.html.length] =  text;
22315             return;   
22316         }
22317         
22318         if (this.in_inline) {
22319             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22320             if (text != ' ') {
22321                 text = text.replace(/\s+/,' ');  // all white space to single white space
22322                 
22323                     
22324                 // if next tag is '<BR>', then we can trim right..
22325                 if (node.nextSibling &&
22326                     node.nextSibling.nodeType == 1 &&
22327                     node.nextSibling.nodeName == 'BR' )
22328                 {
22329                     text = text.replace(/\s+$/g,'');
22330                 }
22331                 // if previous tag was a BR, we can also trim..
22332                 if (node.previousSibling &&
22333                     node.previousSibling.nodeType == 1 &&
22334                     node.previousSibling.nodeName == 'BR' )
22335                 {
22336                     text = this.indentstr +  text.replace(/^\s+/g,'');
22337                 }
22338                 if (text.match(/\n/)) {
22339                     text = text.replace(
22340                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22341                     );
22342                     // remoeve the last whitespace / line break.
22343                     text = text.replace(/\n\s+$/,'');
22344                 }
22345                 // repace long lines
22346                 
22347             }
22348              
22349             this.html[this.html.length] =  text;
22350             return;   
22351         }
22352         // see if previous element was a inline element.
22353         var indentstr = this.indentstr;
22354    
22355         text = text.replace(/\s+/g," "); // all whitespace into single white space.
22356         
22357         // should trim left?
22358         if (node.previousSibling &&
22359             node.previousSibling.nodeType == 1 &&
22360             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22361         {
22362             indentstr = '';
22363             
22364         } else {
22365             this.addLine();
22366             text = text.replace(/^\s+/,''); // trim left
22367           
22368         }
22369         // should trim right?
22370         if (node.nextSibling &&
22371             node.nextSibling.nodeType == 1 &&
22372             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22373         {
22374           // noop
22375             
22376         }  else {
22377             text = text.replace(/\s+$/,''); // trim right
22378         }
22379          
22380               
22381         
22382         
22383         
22384         if (text.length < 1) {
22385             return;
22386         }
22387         if (!text.match(/\n/)) {
22388             this.html.push(indentstr + text);
22389             return;
22390         }
22391         
22392         text = this.indentstr + text.replace(
22393             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22394         );
22395         // remoeve the last whitespace / line break.
22396         text = text.replace(/\s+$/,''); 
22397         
22398         this.html.push(text);
22399         
22400         // split and indent..
22401         
22402         
22403     },
22404     /**
22405      * Writes a cdata node such as <![CDATA[data]]>.
22406      *
22407      * @method cdata
22408      * @param {String} text String to write out inside the cdata.
22409      */
22410     cdata: function(text) {
22411         this.html.push('<![CDATA[', text, ']]>');
22412     },
22413     /**
22414     * Writes a comment node such as <!-- Comment -->.
22415     *
22416     * @method cdata
22417     * @param {String} text String to write out inside the comment.
22418     */
22419    comment: function(text) {
22420        this.html.push('<!--', text, '-->');
22421    },
22422     /**
22423      * Writes a PI node such as <?xml attr="value" ?>.
22424      *
22425      * @method pi
22426      * @param {String} name Name of the pi.
22427      * @param {String} text String to write out inside the pi.
22428      */
22429     pi: function(name, text) {
22430         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22431         this.indent != '' && this.html.push('\n');
22432     },
22433     /**
22434      * Writes a doctype node such as <!DOCTYPE data>.
22435      *
22436      * @method doctype
22437      * @param {String} text String to write out inside the doctype.
22438      */
22439     doctype: function(text) {
22440         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22441     },
22442     /**
22443      * Resets the internal buffer if one wants to reuse the writer.
22444      *
22445      * @method reset
22446      */
22447     reset: function() {
22448         this.html.length = 0;
22449         this.state = [];
22450         this.pushState({
22451             indentstr : '',
22452             in_pre : false, 
22453             in_inline : false
22454         })
22455     },
22456     /**
22457      * Returns the contents that got serialized.
22458      *
22459      * @method getContent
22460      * @return {String} HTML contents that got written down.
22461      */
22462     getContent: function() {
22463         return this.html.join('').replace(/\n$/, '');
22464     },
22465     
22466     pushState : function(cfg)
22467     {
22468         this.state.push(cfg);
22469         Roo.apply(this, cfg);
22470     },
22471     
22472     popState : function()
22473     {
22474         if (this.state.length < 1) {
22475             return; // nothing to push
22476         }
22477         var cfg = {
22478             in_pre: false,
22479             indentstr : ''
22480         };
22481         this.state.pop();
22482         if (this.state.length > 0) {
22483             cfg = this.state[this.state.length-1]; 
22484         }
22485         Roo.apply(this, cfg);
22486     },
22487     
22488     addLine: function()
22489     {
22490         if (this.html.length < 1) {
22491             return;
22492         }
22493         
22494         
22495         var value = this.html[this.html.length - 1];
22496         if (value.length > 0 && '\n' !== value) {
22497             this.html.push('\n');
22498         }
22499     }
22500     
22501     
22502 //'pre script noscript style textarea video audio iframe object code'
22503 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
22504 // inline 
22505 };
22506
22507 Roo.htmleditor.TidyWriter.inline_elements = [
22508         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22509         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
22510 ];
22511 Roo.htmleditor.TidyWriter.shortend_elements = [
22512     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22513     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22514 ];
22515
22516 Roo.htmleditor.TidyWriter.whitespace_elements = [
22517     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22518 ];/***
22519  * This is based loosely on tinymce 
22520  * @class Roo.htmleditor.TidyEntities
22521  * @static
22522  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22523  *
22524  * Not 100% sure this is actually used or needed.
22525  */
22526
22527 Roo.htmleditor.TidyEntities = {
22528     
22529     /**
22530      * initialize data..
22531      */
22532     init : function (){
22533      
22534         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22535        
22536     },
22537
22538
22539     buildEntitiesLookup: function(items, radix) {
22540         var i, chr, entity, lookup = {};
22541         if (!items) {
22542             return {};
22543         }
22544         items = typeof(items) == 'string' ? items.split(',') : items;
22545         radix = radix || 10;
22546         // Build entities lookup table
22547         for (i = 0; i < items.length; i += 2) {
22548             chr = String.fromCharCode(parseInt(items[i], radix));
22549             // Only add non base entities
22550             if (!this.baseEntities[chr]) {
22551                 entity = '&' + items[i + 1] + ';';
22552                 lookup[chr] = entity;
22553                 lookup[entity] = chr;
22554             }
22555         }
22556         return lookup;
22557         
22558     },
22559     
22560     asciiMap : {
22561             128: '€',
22562             130: '‚',
22563             131: 'ƒ',
22564             132: '„',
22565             133: '…',
22566             134: '†',
22567             135: '‡',
22568             136: 'ˆ',
22569             137: '‰',
22570             138: 'Š',
22571             139: '‹',
22572             140: 'Œ',
22573             142: 'Ž',
22574             145: '‘',
22575             146: '’',
22576             147: '“',
22577             148: '”',
22578             149: '•',
22579             150: '–',
22580             151: '—',
22581             152: '˜',
22582             153: '™',
22583             154: 'š',
22584             155: '›',
22585             156: 'œ',
22586             158: 'ž',
22587             159: 'Ÿ'
22588     },
22589     // Raw entities
22590     baseEntities : {
22591         '"': '&quot;',
22592         // Needs to be escaped since the YUI compressor would otherwise break the code
22593         '\'': '&#39;',
22594         '<': '&lt;',
22595         '>': '&gt;',
22596         '&': '&amp;',
22597         '`': '&#96;'
22598     },
22599     // Reverse lookup table for raw entities
22600     reverseEntities : {
22601         '&lt;': '<',
22602         '&gt;': '>',
22603         '&amp;': '&',
22604         '&quot;': '"',
22605         '&apos;': '\''
22606     },
22607     
22608     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22609     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22610     rawCharsRegExp : /[<>&\"\']/g,
22611     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22612     namedEntities  : false,
22613     namedEntitiesData : [ 
22614         '50',
22615         'nbsp',
22616         '51',
22617         'iexcl',
22618         '52',
22619         'cent',
22620         '53',
22621         'pound',
22622         '54',
22623         'curren',
22624         '55',
22625         'yen',
22626         '56',
22627         'brvbar',
22628         '57',
22629         'sect',
22630         '58',
22631         'uml',
22632         '59',
22633         'copy',
22634         '5a',
22635         'ordf',
22636         '5b',
22637         'laquo',
22638         '5c',
22639         'not',
22640         '5d',
22641         'shy',
22642         '5e',
22643         'reg',
22644         '5f',
22645         'macr',
22646         '5g',
22647         'deg',
22648         '5h',
22649         'plusmn',
22650         '5i',
22651         'sup2',
22652         '5j',
22653         'sup3',
22654         '5k',
22655         'acute',
22656         '5l',
22657         'micro',
22658         '5m',
22659         'para',
22660         '5n',
22661         'middot',
22662         '5o',
22663         'cedil',
22664         '5p',
22665         'sup1',
22666         '5q',
22667         'ordm',
22668         '5r',
22669         'raquo',
22670         '5s',
22671         'frac14',
22672         '5t',
22673         'frac12',
22674         '5u',
22675         'frac34',
22676         '5v',
22677         'iquest',
22678         '60',
22679         'Agrave',
22680         '61',
22681         'Aacute',
22682         '62',
22683         'Acirc',
22684         '63',
22685         'Atilde',
22686         '64',
22687         'Auml',
22688         '65',
22689         'Aring',
22690         '66',
22691         'AElig',
22692         '67',
22693         'Ccedil',
22694         '68',
22695         'Egrave',
22696         '69',
22697         'Eacute',
22698         '6a',
22699         'Ecirc',
22700         '6b',
22701         'Euml',
22702         '6c',
22703         'Igrave',
22704         '6d',
22705         'Iacute',
22706         '6e',
22707         'Icirc',
22708         '6f',
22709         'Iuml',
22710         '6g',
22711         'ETH',
22712         '6h',
22713         'Ntilde',
22714         '6i',
22715         'Ograve',
22716         '6j',
22717         'Oacute',
22718         '6k',
22719         'Ocirc',
22720         '6l',
22721         'Otilde',
22722         '6m',
22723         'Ouml',
22724         '6n',
22725         'times',
22726         '6o',
22727         'Oslash',
22728         '6p',
22729         'Ugrave',
22730         '6q',
22731         'Uacute',
22732         '6r',
22733         'Ucirc',
22734         '6s',
22735         'Uuml',
22736         '6t',
22737         'Yacute',
22738         '6u',
22739         'THORN',
22740         '6v',
22741         'szlig',
22742         '70',
22743         'agrave',
22744         '71',
22745         'aacute',
22746         '72',
22747         'acirc',
22748         '73',
22749         'atilde',
22750         '74',
22751         'auml',
22752         '75',
22753         'aring',
22754         '76',
22755         'aelig',
22756         '77',
22757         'ccedil',
22758         '78',
22759         'egrave',
22760         '79',
22761         'eacute',
22762         '7a',
22763         'ecirc',
22764         '7b',
22765         'euml',
22766         '7c',
22767         'igrave',
22768         '7d',
22769         'iacute',
22770         '7e',
22771         'icirc',
22772         '7f',
22773         'iuml',
22774         '7g',
22775         'eth',
22776         '7h',
22777         'ntilde',
22778         '7i',
22779         'ograve',
22780         '7j',
22781         'oacute',
22782         '7k',
22783         'ocirc',
22784         '7l',
22785         'otilde',
22786         '7m',
22787         'ouml',
22788         '7n',
22789         'divide',
22790         '7o',
22791         'oslash',
22792         '7p',
22793         'ugrave',
22794         '7q',
22795         'uacute',
22796         '7r',
22797         'ucirc',
22798         '7s',
22799         'uuml',
22800         '7t',
22801         'yacute',
22802         '7u',
22803         'thorn',
22804         '7v',
22805         'yuml',
22806         'ci',
22807         'fnof',
22808         'sh',
22809         'Alpha',
22810         'si',
22811         'Beta',
22812         'sj',
22813         'Gamma',
22814         'sk',
22815         'Delta',
22816         'sl',
22817         'Epsilon',
22818         'sm',
22819         'Zeta',
22820         'sn',
22821         'Eta',
22822         'so',
22823         'Theta',
22824         'sp',
22825         'Iota',
22826         'sq',
22827         'Kappa',
22828         'sr',
22829         'Lambda',
22830         'ss',
22831         'Mu',
22832         'st',
22833         'Nu',
22834         'su',
22835         'Xi',
22836         'sv',
22837         'Omicron',
22838         't0',
22839         'Pi',
22840         't1',
22841         'Rho',
22842         't3',
22843         'Sigma',
22844         't4',
22845         'Tau',
22846         't5',
22847         'Upsilon',
22848         't6',
22849         'Phi',
22850         't7',
22851         'Chi',
22852         't8',
22853         'Psi',
22854         't9',
22855         'Omega',
22856         'th',
22857         'alpha',
22858         'ti',
22859         'beta',
22860         'tj',
22861         'gamma',
22862         'tk',
22863         'delta',
22864         'tl',
22865         'epsilon',
22866         'tm',
22867         'zeta',
22868         'tn',
22869         'eta',
22870         'to',
22871         'theta',
22872         'tp',
22873         'iota',
22874         'tq',
22875         'kappa',
22876         'tr',
22877         'lambda',
22878         'ts',
22879         'mu',
22880         'tt',
22881         'nu',
22882         'tu',
22883         'xi',
22884         'tv',
22885         'omicron',
22886         'u0',
22887         'pi',
22888         'u1',
22889         'rho',
22890         'u2',
22891         'sigmaf',
22892         'u3',
22893         'sigma',
22894         'u4',
22895         'tau',
22896         'u5',
22897         'upsilon',
22898         'u6',
22899         'phi',
22900         'u7',
22901         'chi',
22902         'u8',
22903         'psi',
22904         'u9',
22905         'omega',
22906         'uh',
22907         'thetasym',
22908         'ui',
22909         'upsih',
22910         'um',
22911         'piv',
22912         '812',
22913         'bull',
22914         '816',
22915         'hellip',
22916         '81i',
22917         'prime',
22918         '81j',
22919         'Prime',
22920         '81u',
22921         'oline',
22922         '824',
22923         'frasl',
22924         '88o',
22925         'weierp',
22926         '88h',
22927         'image',
22928         '88s',
22929         'real',
22930         '892',
22931         'trade',
22932         '89l',
22933         'alefsym',
22934         '8cg',
22935         'larr',
22936         '8ch',
22937         'uarr',
22938         '8ci',
22939         'rarr',
22940         '8cj',
22941         'darr',
22942         '8ck',
22943         'harr',
22944         '8dl',
22945         'crarr',
22946         '8eg',
22947         'lArr',
22948         '8eh',
22949         'uArr',
22950         '8ei',
22951         'rArr',
22952         '8ej',
22953         'dArr',
22954         '8ek',
22955         'hArr',
22956         '8g0',
22957         'forall',
22958         '8g2',
22959         'part',
22960         '8g3',
22961         'exist',
22962         '8g5',
22963         'empty',
22964         '8g7',
22965         'nabla',
22966         '8g8',
22967         'isin',
22968         '8g9',
22969         'notin',
22970         '8gb',
22971         'ni',
22972         '8gf',
22973         'prod',
22974         '8gh',
22975         'sum',
22976         '8gi',
22977         'minus',
22978         '8gn',
22979         'lowast',
22980         '8gq',
22981         'radic',
22982         '8gt',
22983         'prop',
22984         '8gu',
22985         'infin',
22986         '8h0',
22987         'ang',
22988         '8h7',
22989         'and',
22990         '8h8',
22991         'or',
22992         '8h9',
22993         'cap',
22994         '8ha',
22995         'cup',
22996         '8hb',
22997         'int',
22998         '8hk',
22999         'there4',
23000         '8hs',
23001         'sim',
23002         '8i5',
23003         'cong',
23004         '8i8',
23005         'asymp',
23006         '8j0',
23007         'ne',
23008         '8j1',
23009         'equiv',
23010         '8j4',
23011         'le',
23012         '8j5',
23013         'ge',
23014         '8k2',
23015         'sub',
23016         '8k3',
23017         'sup',
23018         '8k4',
23019         'nsub',
23020         '8k6',
23021         'sube',
23022         '8k7',
23023         'supe',
23024         '8kl',
23025         'oplus',
23026         '8kn',
23027         'otimes',
23028         '8l5',
23029         'perp',
23030         '8m5',
23031         'sdot',
23032         '8o8',
23033         'lceil',
23034         '8o9',
23035         'rceil',
23036         '8oa',
23037         'lfloor',
23038         '8ob',
23039         'rfloor',
23040         '8p9',
23041         'lang',
23042         '8pa',
23043         'rang',
23044         '9ea',
23045         'loz',
23046         '9j0',
23047         'spades',
23048         '9j3',
23049         'clubs',
23050         '9j5',
23051         'hearts',
23052         '9j6',
23053         'diams',
23054         'ai',
23055         'OElig',
23056         'aj',
23057         'oelig',
23058         'b0',
23059         'Scaron',
23060         'b1',
23061         'scaron',
23062         'bo',
23063         'Yuml',
23064         'm6',
23065         'circ',
23066         'ms',
23067         'tilde',
23068         '802',
23069         'ensp',
23070         '803',
23071         'emsp',
23072         '809',
23073         'thinsp',
23074         '80c',
23075         'zwnj',
23076         '80d',
23077         'zwj',
23078         '80e',
23079         'lrm',
23080         '80f',
23081         'rlm',
23082         '80j',
23083         'ndash',
23084         '80k',
23085         'mdash',
23086         '80o',
23087         'lsquo',
23088         '80p',
23089         'rsquo',
23090         '80q',
23091         'sbquo',
23092         '80s',
23093         'ldquo',
23094         '80t',
23095         'rdquo',
23096         '80u',
23097         'bdquo',
23098         '810',
23099         'dagger',
23100         '811',
23101         'Dagger',
23102         '81g',
23103         'permil',
23104         '81p',
23105         'lsaquo',
23106         '81q',
23107         'rsaquo',
23108         '85c',
23109         'euro'
23110     ],
23111
23112          
23113     /**
23114      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
23115      *
23116      * @method encodeRaw
23117      * @param {String} text Text to encode.
23118      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23119      * @return {String} Entity encoded text.
23120      */
23121     encodeRaw: function(text, attr)
23122     {
23123         var t = this;
23124         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23125             return t.baseEntities[chr] || chr;
23126         });
23127     },
23128     /**
23129      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
23130      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
23131      * and is exposed as the DOMUtils.encode function.
23132      *
23133      * @method encodeAllRaw
23134      * @param {String} text Text to encode.
23135      * @return {String} Entity encoded text.
23136      */
23137     encodeAllRaw: function(text) {
23138         var t = this;
23139         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
23140             return t.baseEntities[chr] || chr;
23141         });
23142     },
23143     /**
23144      * Encodes the specified string using numeric entities. The core entities will be
23145      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
23146      *
23147      * @method encodeNumeric
23148      * @param {String} text Text to encode.
23149      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23150      * @return {String} Entity encoded text.
23151      */
23152     encodeNumeric: function(text, attr) {
23153         var t = this;
23154         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23155             // Multi byte sequence convert it to a single entity
23156             if (chr.length > 1) {
23157                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23158             }
23159             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23160         });
23161     },
23162     /**
23163      * Encodes the specified string using named entities. The core entities will be encoded
23164      * as named ones but all non lower ascii characters will be encoded into named entities.
23165      *
23166      * @method encodeNamed
23167      * @param {String} text Text to encode.
23168      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23169      * @param {Object} entities Optional parameter with entities to use.
23170      * @return {String} Entity encoded text.
23171      */
23172     encodeNamed: function(text, attr, entities) {
23173         var t = this;
23174         entities = entities || this.namedEntities;
23175         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23176             return t.baseEntities[chr] || entities[chr] || chr;
23177         });
23178     },
23179     /**
23180      * Returns an encode function based on the name(s) and it's optional entities.
23181      *
23182      * @method getEncodeFunc
23183      * @param {String} name Comma separated list of encoders for example named,numeric.
23184      * @param {String} entities Optional parameter with entities to use instead of the built in set.
23185      * @return {function} Encode function to be used.
23186      */
23187     getEncodeFunc: function(name, entities) {
23188         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23189         var t = this;
23190         function encodeNamedAndNumeric(text, attr) {
23191             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23192                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23193             });
23194         }
23195
23196         function encodeCustomNamed(text, attr) {
23197             return t.encodeNamed(text, attr, entities);
23198         }
23199         // Replace + with , to be compatible with previous TinyMCE versions
23200         name = this.makeMap(name.replace(/\+/g, ','));
23201         // Named and numeric encoder
23202         if (name.named && name.numeric) {
23203             return this.encodeNamedAndNumeric;
23204         }
23205         // Named encoder
23206         if (name.named) {
23207             // Custom names
23208             if (entities) {
23209                 return encodeCustomNamed;
23210             }
23211             return this.encodeNamed;
23212         }
23213         // Numeric
23214         if (name.numeric) {
23215             return this.encodeNumeric;
23216         }
23217         // Raw encoder
23218         return this.encodeRaw;
23219     },
23220     /**
23221      * Decodes the specified string, this will replace entities with raw UTF characters.
23222      *
23223      * @method decode
23224      * @param {String} text Text to entity decode.
23225      * @return {String} Entity decoded string.
23226      */
23227     decode: function(text)
23228     {
23229         var  t = this;
23230         return text.replace(this.entityRegExp, function(all, numeric) {
23231             if (numeric) {
23232                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23233                 // Support upper UTF
23234                 if (numeric > 65535) {
23235                     numeric -= 65536;
23236                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23237                 }
23238                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23239             }
23240             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23241         });
23242     },
23243     nativeDecode : function (text) {
23244         return text;
23245     },
23246     makeMap : function (items, delim, map) {
23247                 var i;
23248                 items = items || [];
23249                 delim = delim || ',';
23250                 if (typeof items == "string") {
23251                         items = items.split(delim);
23252                 }
23253                 map = map || {};
23254                 i = items.length;
23255                 while (i--) {
23256                         map[items[i]] = {};
23257                 }
23258                 return map;
23259         }
23260 };
23261     
23262     
23263     
23264 Roo.htmleditor.TidyEntities.init();
23265 /**
23266  * @class Roo.htmleditor.KeyEnter
23267  * Handle Enter press..
23268  * @cfg {Roo.HtmlEditorCore} core the editor.
23269  * @constructor
23270  * Create a new Filter.
23271  * @param {Object} config Configuration options
23272  */
23273
23274
23275
23276
23277
23278 Roo.htmleditor.KeyEnter = function(cfg) {
23279     Roo.apply(this, cfg);
23280     // this does not actually call walk as it's really just a abstract class
23281  
23282     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23283 }
23284
23285 //Roo.htmleditor.KeyEnter.i = 0;
23286
23287
23288 Roo.htmleditor.KeyEnter.prototype = {
23289     
23290     core : false,
23291     
23292     keypress : function(e)
23293     {
23294         if (e.charCode != 13 && e.charCode != 10) {
23295             Roo.log([e.charCode,e]);
23296             return true;
23297         }
23298         e.preventDefault();
23299         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23300         var doc = this.core.doc;
23301           //add a new line
23302        
23303     
23304         var sel = this.core.getSelection();
23305         var range = sel.getRangeAt(0);
23306         var n = range.commonAncestorContainer;
23307         var pc = range.closest([ 'ol', 'ul']);
23308         var pli = range.closest('li');
23309         if (!pc || e.ctrlKey) {
23310             sel.insertNode('br', 'after'); 
23311          
23312             this.core.undoManager.addEvent();
23313             this.core.fireEditorEvent(e);
23314             return false;
23315         }
23316         
23317         // deal with <li> insetion
23318         if (pli.innerText.trim() == '' &&
23319             pli.previousSibling &&
23320             pli.previousSibling.nodeName == 'LI' &&
23321             pli.previousSibling.innerText.trim() ==  '') {
23322             pli.parentNode.removeChild(pli.previousSibling);
23323             sel.cursorAfter(pc);
23324             this.core.undoManager.addEvent();
23325             this.core.fireEditorEvent(e);
23326             return false;
23327         }
23328     
23329         var li = doc.createElement('LI');
23330         li.innerHTML = '&nbsp;';
23331         if (!pli || !pli.firstSibling) {
23332             pc.appendChild(li);
23333         } else {
23334             pli.parentNode.insertBefore(li, pli.firstSibling);
23335         }
23336         sel.cursorText (li.firstChild);
23337       
23338         this.core.undoManager.addEvent();
23339         this.core.fireEditorEvent(e);
23340
23341         return false;
23342         
23343     
23344         
23345         
23346          
23347     }
23348 };
23349      
23350 /**
23351  * @class Roo.htmleditor.Block
23352  * Base class for html editor blocks - do not use it directly .. extend it..
23353  * @cfg {DomElement} node The node to apply stuff to.
23354  * @cfg {String} friendly_name the name that appears in the context bar about this block
23355  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23356  
23357  * @constructor
23358  * Create a new Filter.
23359  * @param {Object} config Configuration options
23360  */
23361
23362 Roo.htmleditor.Block  = function(cfg)
23363 {
23364     // do nothing .. should not be called really.
23365 }
23366 /**
23367  * factory method to get the block from an element (using cache if necessary)
23368  * @static
23369  * @param {HtmlElement} the dom element
23370  */
23371 Roo.htmleditor.Block.factory = function(node)
23372 {
23373     var cc = Roo.htmleditor.Block.cache;
23374     var id = Roo.get(node).id;
23375     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23376         Roo.htmleditor.Block.cache[id].readElement(node);
23377         return Roo.htmleditor.Block.cache[id];
23378     }
23379     var db  = node.getAttribute('data-block');
23380     if (!db) {
23381         db = node.nodeName.toLowerCase().toUpperCaseFirst();
23382     }
23383     var cls = Roo.htmleditor['Block' + db];
23384     if (typeof(cls) == 'undefined') {
23385         //Roo.log(node.getAttribute('data-block'));
23386         Roo.log("OOps missing block : " + 'Block' + db);
23387         return false;
23388     }
23389     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23390     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
23391 };
23392
23393 /**
23394  * initalize all Elements from content that are 'blockable'
23395  * @static
23396  * @param the body element
23397  */
23398 Roo.htmleditor.Block.initAll = function(body, type)
23399 {
23400     if (typeof(type) == 'undefined') {
23401         var ia = Roo.htmleditor.Block.initAll;
23402         ia(body,'table');
23403         ia(body,'td');
23404         ia(body,'figure');
23405         return;
23406     }
23407     Roo.each(Roo.get(body).query(type), function(e) {
23408         Roo.htmleditor.Block.factory(e);    
23409     },this);
23410 };
23411 // question goes here... do we need to clear out this cache sometimes?
23412 // or show we make it relivant to the htmleditor.
23413 Roo.htmleditor.Block.cache = {};
23414
23415 Roo.htmleditor.Block.prototype = {
23416     
23417     node : false,
23418     
23419      // used by context menu
23420     friendly_name : 'Based Block',
23421     
23422     // text for button to delete this element
23423     deleteTitle : false,
23424     
23425     context : false,
23426     /**
23427      * Update a node with values from this object
23428      * @param {DomElement} node
23429      */
23430     updateElement : function(node)
23431     {
23432         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23433     },
23434      /**
23435      * convert to plain HTML for calling insertAtCursor..
23436      */
23437     toHTML : function()
23438     {
23439         return Roo.DomHelper.markup(this.toObject());
23440     },
23441     /**
23442      * used by readEleemnt to extract data from a node
23443      * may need improving as it's pretty basic
23444      
23445      * @param {DomElement} node
23446      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23447      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
23448      * @param {String} style the style property - eg. text-align
23449      */
23450     getVal : function(node, tag, attr, style)
23451     {
23452         var n = node;
23453         if (tag !== true && n.tagName != tag.toUpperCase()) {
23454             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23455             // but kiss for now.
23456             n = node.getElementsByTagName(tag).item(0);
23457         }
23458         if (!n) {
23459             return '';
23460         }
23461         if (attr === false) {
23462             return n;
23463         }
23464         if (attr == 'html') {
23465             return n.innerHTML;
23466         }
23467         if (attr == 'style') {
23468             return n.style[style]; 
23469         }
23470         
23471         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23472             
23473     },
23474     /**
23475      * create a DomHelper friendly object - for use with 
23476      * Roo.DomHelper.markup / overwrite / etc..
23477      * (override this)
23478      */
23479     toObject : function()
23480     {
23481         return {};
23482     },
23483       /**
23484      * Read a node that has a 'data-block' property - and extract the values from it.
23485      * @param {DomElement} node - the node
23486      */
23487     readElement : function(node)
23488     {
23489         
23490     } 
23491     
23492     
23493 };
23494
23495  
23496
23497 /**
23498  * @class Roo.htmleditor.BlockFigure
23499  * Block that has an image and a figcaption
23500  * @cfg {String} image_src the url for the image
23501  * @cfg {String} align (left|right) alignment for the block default left
23502  * @cfg {String} caption the text to appear below  (and in the alt tag)
23503  * @cfg {String} caption_display (block|none) display or not the caption
23504  * @cfg {String|number} image_width the width of the image number or %?
23505  * @cfg {String|number} image_height the height of the image number or %?
23506  * 
23507  * @constructor
23508  * Create a new Filter.
23509  * @param {Object} config Configuration options
23510  */
23511
23512 Roo.htmleditor.BlockFigure = function(cfg)
23513 {
23514     if (cfg.node) {
23515         this.readElement(cfg.node);
23516         this.updateElement(cfg.node);
23517     }
23518     Roo.apply(this, cfg);
23519 }
23520 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23521  
23522     
23523     // setable values.
23524     image_src: '',
23525     align: 'center',
23526     caption : '',
23527     caption_display : 'block',
23528     width : '100%',
23529     cls : '',
23530     href: '',
23531     video_url : '',
23532     
23533     // margin: '2%', not used
23534     
23535     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
23536
23537     
23538     // used by context menu
23539     friendly_name : 'Image with caption',
23540     deleteTitle : "Delete Image and Caption",
23541     
23542     contextMenu : function(toolbar)
23543     {
23544         
23545         var block = function() {
23546             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23547         };
23548         
23549         
23550         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23551         
23552         var syncValue = toolbar.editorcore.syncValue;
23553         
23554         var fields = {};
23555         
23556         return [
23557              {
23558                 xtype : 'TextItem',
23559                 text : "Source: ",
23560                 xns : rooui.Toolbar  //Boostrap?
23561             },
23562             {
23563                 xtype : 'Button',
23564                 text: 'Change Image URL',
23565                  
23566                 listeners : {
23567                     click: function (btn, state)
23568                     {
23569                         var b = block();
23570                         
23571                         Roo.MessageBox.show({
23572                             title : "Image Source URL",
23573                             msg : "Enter the url for the image",
23574                             buttons: Roo.MessageBox.OKCANCEL,
23575                             fn: function(btn, val){
23576                                 if (btn != 'ok') {
23577                                     return;
23578                                 }
23579                                 b.image_src = val;
23580                                 b.updateElement();
23581                                 syncValue();
23582                                 toolbar.editorcore.onEditorEvent();
23583                             },
23584                             minWidth:250,
23585                             prompt:true,
23586                             //multiline: multiline,
23587                             modal : true,
23588                             value : b.image_src
23589                         });
23590                     }
23591                 },
23592                 xns : rooui.Toolbar
23593             },
23594          
23595             {
23596                 xtype : 'Button',
23597                 text: 'Change Link URL',
23598                  
23599                 listeners : {
23600                     click: function (btn, state)
23601                     {
23602                         var b = block();
23603                         
23604                         Roo.MessageBox.show({
23605                             title : "Link URL",
23606                             msg : "Enter the url for the link - leave blank to have no link",
23607                             buttons: Roo.MessageBox.OKCANCEL,
23608                             fn: function(btn, val){
23609                                 if (btn != 'ok') {
23610                                     return;
23611                                 }
23612                                 b.href = val;
23613                                 b.updateElement();
23614                                 syncValue();
23615                                 toolbar.editorcore.onEditorEvent();
23616                             },
23617                             minWidth:250,
23618                             prompt:true,
23619                             //multiline: multiline,
23620                             modal : true,
23621                             value : b.href
23622                         });
23623                     }
23624                 },
23625                 xns : rooui.Toolbar
23626             },
23627             {
23628                 xtype : 'Button',
23629                 text: 'Show Video URL',
23630                  
23631                 listeners : {
23632                     click: function (btn, state)
23633                     {
23634                         Roo.MessageBox.alert("Video URL",
23635                             block().video_url == '' ? 'This image is not linked ot a video' :
23636                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23637                     }
23638                 },
23639                 xns : rooui.Toolbar
23640             },
23641             
23642             
23643             {
23644                 xtype : 'TextItem',
23645                 text : "Width: ",
23646                 xns : rooui.Toolbar  //Boostrap?
23647             },
23648             {
23649                 xtype : 'ComboBox',
23650                 allowBlank : false,
23651                 displayField : 'val',
23652                 editable : true,
23653                 listWidth : 100,
23654                 triggerAction : 'all',
23655                 typeAhead : true,
23656                 valueField : 'val',
23657                 width : 70,
23658                 name : 'width',
23659                 listeners : {
23660                     select : function (combo, r, index)
23661                     {
23662                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23663                         var b = block();
23664                         b.width = r.get('val');
23665                         b.updateElement();
23666                         syncValue();
23667                         toolbar.editorcore.onEditorEvent();
23668                     }
23669                 },
23670                 xns : rooui.form,
23671                 store : {
23672                     xtype : 'SimpleStore',
23673                     data : [
23674                         ['50%'],
23675                         ['80%'],
23676                         ['100%']
23677                     ],
23678                     fields : [ 'val'],
23679                     xns : Roo.data
23680                 }
23681             },
23682             {
23683                 xtype : 'TextItem',
23684                 text : "Align: ",
23685                 xns : rooui.Toolbar  //Boostrap?
23686             },
23687             {
23688                 xtype : 'ComboBox',
23689                 allowBlank : false,
23690                 displayField : 'val',
23691                 editable : true,
23692                 listWidth : 100,
23693                 triggerAction : 'all',
23694                 typeAhead : true,
23695                 valueField : 'val',
23696                 width : 70,
23697                 name : 'align',
23698                 listeners : {
23699                     select : function (combo, r, index)
23700                     {
23701                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23702                         var b = block();
23703                         b.align = r.get('val');
23704                         b.updateElement();
23705                         syncValue();
23706                         toolbar.editorcore.onEditorEvent();
23707                     }
23708                 },
23709                 xns : rooui.form,
23710                 store : {
23711                     xtype : 'SimpleStore',
23712                     data : [
23713                         ['left'],
23714                         ['right'],
23715                         ['center']
23716                     ],
23717                     fields : [ 'val'],
23718                     xns : Roo.data
23719                 }
23720             },
23721             
23722             
23723             {
23724                 xtype : 'Button',
23725                 text: 'Hide Caption',
23726                 name : 'caption_display',
23727                 pressed : false,
23728                 enableToggle : true,
23729                 setValue : function(v) {
23730                     // this trigger toggle.
23731                      
23732                     this.setText(v ? "Hide Caption" : "Show Caption");
23733                     this.setPressed(v != 'block');
23734                 },
23735                 listeners : {
23736                     toggle: function (btn, state)
23737                     {
23738                         var b  = block();
23739                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23740                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23741                         b.updateElement();
23742                         syncValue();
23743                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23744                         toolbar.editorcore.onEditorEvent();
23745                     }
23746                 },
23747                 xns : rooui.Toolbar
23748             }
23749         ];
23750         
23751     },
23752     /**
23753      * create a DomHelper friendly object - for use with
23754      * Roo.DomHelper.markup / overwrite / etc..
23755      */
23756     toObject : function()
23757     {
23758         var d = document.createElement('div');
23759         d.innerHTML = this.caption;
23760         
23761         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
23762         
23763         var iw = this.align == 'center' ? this.width : '100%';
23764         var img =   {
23765             tag : 'img',
23766             contenteditable : 'false',
23767             src : this.image_src,
23768             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23769             style: {
23770                 width : iw,
23771                 maxWidth : iw + ' !important', // this is not getting rendered?
23772                 margin : m  
23773                 
23774             }
23775         };
23776         /*
23777         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23778                     '<a href="{2}">' + 
23779                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
23780                     '</a>' + 
23781                 '</div>',
23782         */
23783                 
23784         if (this.href.length > 0) {
23785             img = {
23786                 tag : 'a',
23787                 href: this.href,
23788                 contenteditable : 'true',
23789                 cn : [
23790                     img
23791                 ]
23792             };
23793         }
23794         
23795         
23796         if (this.video_url.length > 0) {
23797             img = {
23798                 tag : 'div',
23799                 cls : this.cls,
23800                 frameborder : 0,
23801                 allowfullscreen : true,
23802                 width : 420,  // these are for video tricks - that we replace the outer
23803                 height : 315,
23804                 src : this.video_url,
23805                 cn : [
23806                     img
23807                 ]
23808             };
23809         }
23810         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
23811         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
23812         
23813   
23814         var ret =   {
23815             tag: 'figure',
23816             'data-block' : 'Figure',
23817             'data-width' : this.width, 
23818             contenteditable : 'false',
23819             
23820             style : {
23821                 display: 'block',
23822                 float :  this.align ,
23823                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
23824                 width : this.align == 'center' ? '100%' : this.width,
23825                 margin:  '0px',
23826                 padding: this.align == 'center' ? '0' : '0 10px' ,
23827                 textAlign : this.align   // seems to work for email..
23828                 
23829             },
23830            
23831             
23832             align : this.align,
23833             cn : [
23834                 img,
23835               
23836                 {
23837                     tag: 'figcaption',
23838                     'data-display' : this.caption_display,
23839                     style : {
23840                         textAlign : 'left',
23841                         fontSize : '16px',
23842                         lineHeight : '24px',
23843                         display : this.caption_display,
23844                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
23845                         margin: m,
23846                         width: this.align == 'center' ?  this.width : '100%' 
23847                     
23848                          
23849                     },
23850                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
23851                     cn : [
23852                         {
23853                             tag: 'div',
23854                             style  : {
23855                                 marginTop : '16px',
23856                                 textAlign : 'left'
23857                             },
23858                             align: 'left',
23859                             cn : [
23860                                 {
23861                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
23862                                     tag : 'i',
23863                                     contenteditable : true,
23864                                     html : captionhtml
23865                                 }
23866                                 
23867                             ]
23868                         }
23869                         
23870                     ]
23871                     
23872                 }
23873             ]
23874         };
23875         return ret;
23876          
23877     },
23878     
23879     readElement : function(node)
23880     {
23881         // this should not really come from the link...
23882         this.video_url = this.getVal(node, 'div', 'src');
23883         this.cls = this.getVal(node, 'div', 'class');
23884         this.href = this.getVal(node, 'a', 'href');
23885         
23886         
23887         this.image_src = this.getVal(node, 'img', 'src');
23888          
23889         this.align = this.getVal(node, 'figure', 'align');
23890         var figcaption = this.getVal(node, 'figcaption', false);
23891         if (figcaption !== '') {
23892             this.caption = this.getVal(figcaption, 'i', 'html');
23893         }
23894         
23895
23896         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
23897         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23898         this.width = this.getVal(node, true, 'data-width');
23899         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23900         
23901     },
23902     removeNode : function()
23903     {
23904         return this.node;
23905     }
23906     
23907   
23908    
23909      
23910     
23911     
23912     
23913     
23914 })
23915
23916  
23917
23918 /**
23919  * @class Roo.htmleditor.BlockTable
23920  * Block that manages a table
23921  * 
23922  * @constructor
23923  * Create a new Filter.
23924  * @param {Object} config Configuration options
23925  */
23926
23927 Roo.htmleditor.BlockTable = function(cfg)
23928 {
23929     if (cfg.node) {
23930         this.readElement(cfg.node);
23931         this.updateElement(cfg.node);
23932     }
23933     Roo.apply(this, cfg);
23934     if (!cfg.node) {
23935         this.rows = [];
23936         for(var r = 0; r < this.no_row; r++) {
23937             this.rows[r] = [];
23938             for(var c = 0; c < this.no_col; c++) {
23939                 this.rows[r][c] = this.emptyCell();
23940             }
23941         }
23942     }
23943     
23944     
23945 }
23946 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23947  
23948     rows : false,
23949     no_col : 1,
23950     no_row : 1,
23951     
23952     
23953     width: '100%',
23954     
23955     // used by context menu
23956     friendly_name : 'Table',
23957     deleteTitle : 'Delete Table',
23958     // context menu is drawn once..
23959     
23960     contextMenu : function(toolbar)
23961     {
23962         
23963         var block = function() {
23964             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23965         };
23966         
23967         
23968         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23969         
23970         var syncValue = toolbar.editorcore.syncValue;
23971         
23972         var fields = {};
23973         
23974         return [
23975             {
23976                 xtype : 'TextItem',
23977                 text : "Width: ",
23978                 xns : rooui.Toolbar  //Boostrap?
23979             },
23980             {
23981                 xtype : 'ComboBox',
23982                 allowBlank : false,
23983                 displayField : 'val',
23984                 editable : true,
23985                 listWidth : 100,
23986                 triggerAction : 'all',
23987                 typeAhead : true,
23988                 valueField : 'val',
23989                 width : 100,
23990                 name : 'width',
23991                 listeners : {
23992                     select : function (combo, r, index)
23993                     {
23994                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23995                         var b = block();
23996                         b.width = r.get('val');
23997                         b.updateElement();
23998                         syncValue();
23999                         toolbar.editorcore.onEditorEvent();
24000                     }
24001                 },
24002                 xns : rooui.form,
24003                 store : {
24004                     xtype : 'SimpleStore',
24005                     data : [
24006                         ['100%'],
24007                         ['auto']
24008                     ],
24009                     fields : [ 'val'],
24010                     xns : Roo.data
24011                 }
24012             },
24013             // -------- Cols
24014             
24015             {
24016                 xtype : 'TextItem',
24017                 text : "Columns: ",
24018                 xns : rooui.Toolbar  //Boostrap?
24019             },
24020          
24021             {
24022                 xtype : 'Button',
24023                 text: '-',
24024                 listeners : {
24025                     click : function (_self, e)
24026                     {
24027                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24028                         block().removeColumn();
24029                         syncValue();
24030                         toolbar.editorcore.onEditorEvent();
24031                     }
24032                 },
24033                 xns : rooui.Toolbar
24034             },
24035             {
24036                 xtype : 'Button',
24037                 text: '+',
24038                 listeners : {
24039                     click : function (_self, e)
24040                     {
24041                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24042                         block().addColumn();
24043                         syncValue();
24044                         toolbar.editorcore.onEditorEvent();
24045                     }
24046                 },
24047                 xns : rooui.Toolbar
24048             },
24049             // -------- ROWS
24050             {
24051                 xtype : 'TextItem',
24052                 text : "Rows: ",
24053                 xns : rooui.Toolbar  //Boostrap?
24054             },
24055          
24056             {
24057                 xtype : 'Button',
24058                 text: '-',
24059                 listeners : {
24060                     click : function (_self, e)
24061                     {
24062                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24063                         block().removeRow();
24064                         syncValue();
24065                         toolbar.editorcore.onEditorEvent();
24066                     }
24067                 },
24068                 xns : rooui.Toolbar
24069             },
24070             {
24071                 xtype : 'Button',
24072                 text: '+',
24073                 listeners : {
24074                     click : function (_self, e)
24075                     {
24076                         block().addRow();
24077                         syncValue();
24078                         toolbar.editorcore.onEditorEvent();
24079                     }
24080                 },
24081                 xns : rooui.Toolbar
24082             },
24083             // -------- ROWS
24084             {
24085                 xtype : 'Button',
24086                 text: 'Reset Column Widths',
24087                 listeners : {
24088                     
24089                     click : function (_self, e)
24090                     {
24091                         block().resetWidths();
24092                         syncValue();
24093                         toolbar.editorcore.onEditorEvent();
24094                     }
24095                 },
24096                 xns : rooui.Toolbar
24097             } 
24098             
24099             
24100             
24101         ];
24102         
24103     },
24104     
24105     
24106   /**
24107      * create a DomHelper friendly object - for use with
24108      * Roo.DomHelper.markup / overwrite / etc..
24109      * ?? should it be called with option to hide all editing features?
24110      */
24111     toObject : function()
24112     {
24113         
24114         var ret = {
24115             tag : 'table',
24116             contenteditable : 'false', // this stops cell selection from picking the table.
24117             'data-block' : 'Table',
24118             style : {
24119                 width:  this.width,
24120                 border : 'solid 1px #000', // ??? hard coded?
24121                 'border-collapse' : 'collapse' 
24122             },
24123             cn : [
24124                 { tag : 'tbody' , cn : [] }
24125             ]
24126         };
24127         
24128         // do we have a head = not really 
24129         var ncols = 0;
24130         Roo.each(this.rows, function( row ) {
24131             var tr = {
24132                 tag: 'tr',
24133                 style : {
24134                     margin: '6px',
24135                     border : 'solid 1px #000',
24136                     textAlign : 'left' 
24137                 },
24138                 cn : [ ]
24139             };
24140             
24141             ret.cn[0].cn.push(tr);
24142             // does the row have any properties? ?? height?
24143             var nc = 0;
24144             Roo.each(row, function( cell ) {
24145                 
24146                 var td = {
24147                     tag : 'td',
24148                     contenteditable :  'true',
24149                     'data-block' : 'Td',
24150                     html : cell.html,
24151                     style : cell.style
24152                 };
24153                 if (cell.colspan > 1) {
24154                     td.colspan = cell.colspan ;
24155                     nc += cell.colspan;
24156                 } else {
24157                     nc++;
24158                 }
24159                 if (cell.rowspan > 1) {
24160                     td.rowspan = cell.rowspan ;
24161                 }
24162                 
24163                 
24164                 // widths ?
24165                 tr.cn.push(td);
24166                     
24167                 
24168             }, this);
24169             ncols = Math.max(nc, ncols);
24170             
24171             
24172         }, this);
24173         // add the header row..
24174         
24175         ncols++;
24176          
24177         
24178         return ret;
24179          
24180     },
24181     
24182     readElement : function(node)
24183     {
24184         node  = node ? node : this.node ;
24185         this.width = this.getVal(node, true, 'style', 'width') || '100%';
24186         
24187         this.rows = [];
24188         this.no_row = 0;
24189         var trs = Array.from(node.rows);
24190         trs.forEach(function(tr) {
24191             var row =  [];
24192             this.rows.push(row);
24193             
24194             this.no_row++;
24195             var no_column = 0;
24196             Array.from(tr.cells).forEach(function(td) {
24197                 
24198                 var add = {
24199                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24200                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24201                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24202                     html : td.innerHTML
24203                 };
24204                 no_column += add.colspan;
24205                      
24206                 
24207                 row.push(add);
24208                 
24209                 
24210             },this);
24211             this.no_col = Math.max(this.no_col, no_column);
24212             
24213             
24214         },this);
24215         
24216         
24217     },
24218     normalizeRows: function()
24219     {
24220         var ret= [];
24221         var rid = -1;
24222         this.rows.forEach(function(row) {
24223             rid++;
24224             ret[rid] = [];
24225             row = this.normalizeRow(row);
24226             var cid = 0;
24227             row.forEach(function(c) {
24228                 while (typeof(ret[rid][cid]) != 'undefined') {
24229                     cid++;
24230                 }
24231                 if (typeof(ret[rid]) == 'undefined') {
24232                     ret[rid] = [];
24233                 }
24234                 ret[rid][cid] = c;
24235                 c.row = rid;
24236                 c.col = cid;
24237                 if (c.rowspan < 2) {
24238                     return;
24239                 }
24240                 
24241                 for(var i = 1 ;i < c.rowspan; i++) {
24242                     if (typeof(ret[rid+i]) == 'undefined') {
24243                         ret[rid+i] = [];
24244                     }
24245                     ret[rid+i][cid] = c;
24246                 }
24247             });
24248         }, this);
24249         return ret;
24250     
24251     },
24252     
24253     normalizeRow: function(row)
24254     {
24255         var ret= [];
24256         row.forEach(function(c) {
24257             if (c.colspan < 2) {
24258                 ret.push(c);
24259                 return;
24260             }
24261             for(var i =0 ;i < c.colspan; i++) {
24262                 ret.push(c);
24263             }
24264         });
24265         return ret;
24266     
24267     },
24268     
24269     deleteColumn : function(sel)
24270     {
24271         if (!sel || sel.type != 'col') {
24272             return;
24273         }
24274         if (this.no_col < 2) {
24275             return;
24276         }
24277         
24278         this.rows.forEach(function(row) {
24279             var cols = this.normalizeRow(row);
24280             var col = cols[sel.col];
24281             if (col.colspan > 1) {
24282                 col.colspan --;
24283             } else {
24284                 row.remove(col);
24285             }
24286             
24287         }, this);
24288         this.no_col--;
24289         
24290     },
24291     removeColumn : function()
24292     {
24293         this.deleteColumn({
24294             type: 'col',
24295             col : this.no_col-1
24296         });
24297         this.updateElement();
24298     },
24299     
24300      
24301     addColumn : function()
24302     {
24303         
24304         this.rows.forEach(function(row) {
24305             row.push(this.emptyCell());
24306            
24307         }, this);
24308         this.updateElement();
24309     },
24310     
24311     deleteRow : function(sel)
24312     {
24313         if (!sel || sel.type != 'row') {
24314             return;
24315         }
24316         
24317         if (this.no_row < 2) {
24318             return;
24319         }
24320         
24321         var rows = this.normalizeRows();
24322         
24323         
24324         rows[sel.row].forEach(function(col) {
24325             if (col.rowspan > 1) {
24326                 col.rowspan--;
24327             } else {
24328                 col.remove = 1; // flage it as removed.
24329             }
24330             
24331         }, this);
24332         var newrows = [];
24333         this.rows.forEach(function(row) {
24334             newrow = [];
24335             row.forEach(function(c) {
24336                 if (typeof(c.remove) == 'undefined') {
24337                     newrow.push(c);
24338                 }
24339                 
24340             });
24341             if (newrow.length > 0) {
24342                 newrows.push(row);
24343             }
24344         });
24345         this.rows =  newrows;
24346         
24347         
24348         
24349         this.no_row--;
24350         this.updateElement();
24351         
24352     },
24353     removeRow : function()
24354     {
24355         this.deleteRow({
24356             type: 'row',
24357             row : this.no_row-1
24358         });
24359         
24360     },
24361     
24362      
24363     addRow : function()
24364     {
24365         
24366         var row = [];
24367         for (var i = 0; i < this.no_col; i++ ) {
24368             
24369             row.push(this.emptyCell());
24370            
24371         }
24372         this.rows.push(row);
24373         this.updateElement();
24374         
24375     },
24376      
24377     // the default cell object... at present...
24378     emptyCell : function() {
24379         return (new Roo.htmleditor.BlockTd({})).toObject();
24380         
24381      
24382     },
24383     
24384     removeNode : function()
24385     {
24386         return this.node;
24387     },
24388     
24389     
24390     
24391     resetWidths : function()
24392     {
24393         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24394             var nn = Roo.htmleditor.Block.factory(n);
24395             nn.width = '';
24396             nn.updateElement(n);
24397         });
24398     }
24399     
24400     
24401     
24402     
24403 })
24404
24405 /**
24406  *
24407  * editing a TD?
24408  *
24409  * since selections really work on the table cell, then editing really should work from there
24410  *
24411  * The original plan was to support merging etc... - but that may not be needed yet..
24412  *
24413  * So this simple version will support:
24414  *   add/remove cols
24415  *   adjust the width +/-
24416  *   reset the width...
24417  *   
24418  *
24419  */
24420
24421
24422  
24423
24424 /**
24425  * @class Roo.htmleditor.BlockTable
24426  * Block that manages a table
24427  * 
24428  * @constructor
24429  * Create a new Filter.
24430  * @param {Object} config Configuration options
24431  */
24432
24433 Roo.htmleditor.BlockTd = function(cfg)
24434 {
24435     if (cfg.node) {
24436         this.readElement(cfg.node);
24437         this.updateElement(cfg.node);
24438     }
24439     Roo.apply(this, cfg);
24440      
24441     
24442     
24443 }
24444 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24445  
24446     node : false,
24447     
24448     width: '',
24449     textAlign : 'left',
24450     valign : 'top',
24451     
24452     colspan : 1,
24453     rowspan : 1,
24454     
24455     
24456     // used by context menu
24457     friendly_name : 'Table Cell',
24458     deleteTitle : false, // use our customer delete
24459     
24460     // context menu is drawn once..
24461     
24462     contextMenu : function(toolbar)
24463     {
24464         
24465         var cell = function() {
24466             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24467         };
24468         
24469         var table = function() {
24470             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24471         };
24472         
24473         var lr = false;
24474         var saveSel = function()
24475         {
24476             lr = toolbar.editorcore.getSelection().getRangeAt(0);
24477         }
24478         var restoreSel = function()
24479         {
24480             if (lr) {
24481                 (function() {
24482                     toolbar.editorcore.focus();
24483                     var cr = toolbar.editorcore.getSelection();
24484                     cr.removeAllRanges();
24485                     cr.addRange(lr);
24486                     toolbar.editorcore.onEditorEvent();
24487                 }).defer(10, this);
24488                 
24489                 
24490             }
24491         }
24492         
24493         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24494         
24495         var syncValue = toolbar.editorcore.syncValue;
24496         
24497         var fields = {};
24498         
24499         return [
24500             {
24501                 xtype : 'Button',
24502                 text : 'Edit Table',
24503                 listeners : {
24504                     click : function() {
24505                         var t = toolbar.tb.selectedNode.closest('table');
24506                         toolbar.editorcore.selectNode(t);
24507                         toolbar.editorcore.onEditorEvent();                        
24508                     }
24509                 }
24510                 
24511             },
24512               
24513            
24514              
24515             {
24516                 xtype : 'TextItem',
24517                 text : "Column Width: ",
24518                  xns : rooui.Toolbar 
24519                
24520             },
24521             {
24522                 xtype : 'Button',
24523                 text: '-',
24524                 listeners : {
24525                     click : function (_self, e)
24526                     {
24527                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24528                         cell().shrinkColumn();
24529                         syncValue();
24530                          toolbar.editorcore.onEditorEvent();
24531                     }
24532                 },
24533                 xns : rooui.Toolbar
24534             },
24535             {
24536                 xtype : 'Button',
24537                 text: '+',
24538                 listeners : {
24539                     click : function (_self, e)
24540                     {
24541                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24542                         cell().growColumn();
24543                         syncValue();
24544                         toolbar.editorcore.onEditorEvent();
24545                     }
24546                 },
24547                 xns : rooui.Toolbar
24548             },
24549             
24550             {
24551                 xtype : 'TextItem',
24552                 text : "Vertical Align: ",
24553                 xns : rooui.Toolbar  //Boostrap?
24554             },
24555             {
24556                 xtype : 'ComboBox',
24557                 allowBlank : false,
24558                 displayField : 'val',
24559                 editable : true,
24560                 listWidth : 100,
24561                 triggerAction : 'all',
24562                 typeAhead : true,
24563                 valueField : 'val',
24564                 width : 100,
24565                 name : 'valign',
24566                 listeners : {
24567                     select : function (combo, r, index)
24568                     {
24569                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24570                         var b = cell();
24571                         b.valign = r.get('val');
24572                         b.updateElement();
24573                         syncValue();
24574                         toolbar.editorcore.onEditorEvent();
24575                     }
24576                 },
24577                 xns : rooui.form,
24578                 store : {
24579                     xtype : 'SimpleStore',
24580                     data : [
24581                         ['top'],
24582                         ['middle'],
24583                         ['bottom'] // there are afew more... 
24584                     ],
24585                     fields : [ 'val'],
24586                     xns : Roo.data
24587                 }
24588             },
24589             
24590             {
24591                 xtype : 'TextItem',
24592                 text : "Merge Cells: ",
24593                  xns : rooui.Toolbar 
24594                
24595             },
24596             
24597             
24598             {
24599                 xtype : 'Button',
24600                 text: 'Right',
24601                 listeners : {
24602                     click : function (_self, e)
24603                     {
24604                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24605                         cell().mergeRight();
24606                         //block().growColumn();
24607                         syncValue();
24608                         toolbar.editorcore.onEditorEvent();
24609                     }
24610                 },
24611                 xns : rooui.Toolbar
24612             },
24613              
24614             {
24615                 xtype : 'Button',
24616                 text: 'Below',
24617                 listeners : {
24618                     click : function (_self, e)
24619                     {
24620                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24621                         cell().mergeBelow();
24622                         //block().growColumn();
24623                         syncValue();
24624                         toolbar.editorcore.onEditorEvent();
24625                     }
24626                 },
24627                 xns : rooui.Toolbar
24628             },
24629             {
24630                 xtype : 'TextItem',
24631                 text : "| ",
24632                  xns : rooui.Toolbar 
24633                
24634             },
24635             
24636             {
24637                 xtype : 'Button',
24638                 text: 'Split',
24639                 listeners : {
24640                     click : function (_self, e)
24641                     {
24642                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24643                         cell().split();
24644                         syncValue();
24645                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24646                         toolbar.editorcore.onEditorEvent();
24647                                              
24648                     }
24649                 },
24650                 xns : rooui.Toolbar
24651             },
24652             {
24653                 xtype : 'Fill',
24654                 xns : rooui.Toolbar 
24655                
24656             },
24657         
24658           
24659             {
24660                 xtype : 'Button',
24661                 text: 'Delete',
24662                  
24663                 xns : rooui.Toolbar,
24664                 menu : {
24665                     xtype : 'Menu',
24666                     xns : rooui.menu,
24667                     items : [
24668                         {
24669                             xtype : 'Item',
24670                             html: 'Column',
24671                             listeners : {
24672                                 click : function (_self, e)
24673                                 {
24674                                     var t = table();
24675                                     
24676                                     cell().deleteColumn();
24677                                     syncValue();
24678                                     toolbar.editorcore.selectNode(t.node);
24679                                     toolbar.editorcore.onEditorEvent();   
24680                                 }
24681                             },
24682                             xns : rooui.menu
24683                         },
24684                         {
24685                             xtype : 'Item',
24686                             html: 'Row',
24687                             listeners : {
24688                                 click : function (_self, e)
24689                                 {
24690                                     var t = table();
24691                                     cell().deleteRow();
24692                                     syncValue();
24693                                     
24694                                     toolbar.editorcore.selectNode(t.node);
24695                                     toolbar.editorcore.onEditorEvent();   
24696                                                          
24697                                 }
24698                             },
24699                             xns : rooui.menu
24700                         },
24701                        {
24702                             xtype : 'Separator',
24703                             xns : rooui.menu
24704                         },
24705                         {
24706                             xtype : 'Item',
24707                             html: 'Table',
24708                             listeners : {
24709                                 click : function (_self, e)
24710                                 {
24711                                     var t = table();
24712                                     var nn = t.node.nextSibling || t.node.previousSibling;
24713                                     t.node.parentNode.removeChild(t.node);
24714                                     if (nn) { 
24715                                         toolbar.editorcore.selectNode(nn, true);
24716                                     }
24717                                     toolbar.editorcore.onEditorEvent();   
24718                                                          
24719                                 }
24720                             },
24721                             xns : rooui.menu
24722                         }
24723                     ]
24724                 }
24725             }
24726             
24727             // align... << fixme
24728             
24729         ];
24730         
24731     },
24732     
24733     
24734   /**
24735      * create a DomHelper friendly object - for use with
24736      * Roo.DomHelper.markup / overwrite / etc..
24737      * ?? should it be called with option to hide all editing features?
24738      */
24739  /**
24740      * create a DomHelper friendly object - for use with
24741      * Roo.DomHelper.markup / overwrite / etc..
24742      * ?? should it be called with option to hide all editing features?
24743      */
24744     toObject : function()
24745     {
24746         
24747         var ret = {
24748             tag : 'td',
24749             contenteditable : 'true', // this stops cell selection from picking the table.
24750             'data-block' : 'Td',
24751             valign : this.valign,
24752             style : {  
24753                 'text-align' :  this.textAlign,
24754                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24755                 'border-collapse' : 'collapse',
24756                 padding : '6px', // 8 for desktop / 4 for mobile
24757                 'vertical-align': this.valign
24758             },
24759             html : this.html
24760         };
24761         if (this.width != '') {
24762             ret.width = this.width;
24763             ret.style.width = this.width;
24764         }
24765         
24766         
24767         if (this.colspan > 1) {
24768             ret.colspan = this.colspan ;
24769         } 
24770         if (this.rowspan > 1) {
24771             ret.rowspan = this.rowspan ;
24772         }
24773         
24774            
24775         
24776         return ret;
24777          
24778     },
24779     
24780     readElement : function(node)
24781     {
24782         node  = node ? node : this.node ;
24783         this.width = node.style.width;
24784         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24785         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24786         this.html = node.innerHTML;
24787         
24788         
24789     },
24790      
24791     // the default cell object... at present...
24792     emptyCell : function() {
24793         return {
24794             colspan :  1,
24795             rowspan :  1,
24796             textAlign : 'left',
24797             html : "&nbsp;" // is this going to be editable now?
24798         };
24799      
24800     },
24801     
24802     removeNode : function()
24803     {
24804         return this.node.closest('table');
24805          
24806     },
24807     
24808     cellData : false,
24809     
24810     colWidths : false,
24811     
24812     toTableArray  : function()
24813     {
24814         var ret = [];
24815         var tab = this.node.closest('tr').closest('table');
24816         Array.from(tab.rows).forEach(function(r, ri){
24817             ret[ri] = [];
24818         });
24819         var rn = 0;
24820         this.colWidths = [];
24821         var all_auto = true;
24822         Array.from(tab.rows).forEach(function(r, ri){
24823             
24824             var cn = 0;
24825             Array.from(r.cells).forEach(function(ce, ci){
24826                 var c =  {
24827                     cell : ce,
24828                     row : rn,
24829                     col: cn,
24830                     colspan : ce.colSpan,
24831                     rowspan : ce.rowSpan
24832                 };
24833                 if (ce.isEqualNode(this.node)) {
24834                     this.cellData = c;
24835                 }
24836                 // if we have been filled up by a row?
24837                 if (typeof(ret[rn][cn]) != 'undefined') {
24838                     while(typeof(ret[rn][cn]) != 'undefined') {
24839                         cn++;
24840                     }
24841                     c.col = cn;
24842                 }
24843                 
24844                 if (typeof(this.colWidths[cn]) == 'undefined') {
24845                     this.colWidths[cn] =   ce.style.width;
24846                     if (this.colWidths[cn] != '') {
24847                         all_auto = false;
24848                     }
24849                 }
24850                 
24851                 
24852                 if (c.colspan < 2 && c.rowspan < 2 ) {
24853                     ret[rn][cn] = c;
24854                     cn++;
24855                     return;
24856                 }
24857                 for(var j = 0; j < c.rowspan; j++) {
24858                     if (typeof(ret[rn+j]) == 'undefined') {
24859                         continue; // we have a problem..
24860                     }
24861                     ret[rn+j][cn] = c;
24862                     for(var i = 0; i < c.colspan; i++) {
24863                         ret[rn+j][cn+i] = c;
24864                     }
24865                 }
24866                 
24867                 cn += c.colspan;
24868             }, this);
24869             rn++;
24870         }, this);
24871         
24872         // initalize widths.?
24873         // either all widths or no widths..
24874         if (all_auto) {
24875             this.colWidths[0] = false; // no widths flag.
24876         }
24877         
24878         
24879         return ret;
24880         
24881     },
24882     
24883     
24884     
24885     
24886     mergeRight: function()
24887     {
24888          
24889         // get the contents of the next cell along..
24890         var tr = this.node.closest('tr');
24891         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24892         if (i >= tr.childNodes.length - 1) {
24893             return; // no cells on right to merge with.
24894         }
24895         var table = this.toTableArray();
24896         
24897         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24898             return; // nothing right?
24899         }
24900         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24901         // right cell - must be same rowspan and on the same row.
24902         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24903             return; // right hand side is not same rowspan.
24904         }
24905         
24906         
24907         
24908         this.node.innerHTML += ' ' + rc.cell.innerHTML;
24909         tr.removeChild(rc.cell);
24910         this.colspan += rc.colspan;
24911         this.node.setAttribute('colspan', this.colspan);
24912
24913     },
24914     
24915     
24916     mergeBelow : function()
24917     {
24918         var table = this.toTableArray();
24919         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24920             return; // no row below
24921         }
24922         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24923             return; // nothing right?
24924         }
24925         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24926         
24927         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24928             return; // right hand side is not same rowspan.
24929         }
24930         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
24931         rc.cell.parentNode.removeChild(rc.cell);
24932         this.rowspan += rc.rowspan;
24933         this.node.setAttribute('rowspan', this.rowspan);
24934     },
24935     
24936     split: function()
24937     {
24938         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24939             return;
24940         }
24941         var table = this.toTableArray();
24942         var cd = this.cellData;
24943         this.rowspan = 1;
24944         this.colspan = 1;
24945         
24946         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24947             
24948             
24949             
24950             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24951                 if (r == cd.row && c == cd.col) {
24952                     this.node.removeAttribute('rowspan');
24953                     this.node.removeAttribute('colspan');
24954                     continue;
24955                 }
24956                  
24957                 var ntd = this.node.cloneNode(); // which col/row should be 0..
24958                 ntd.removeAttribute('id'); //
24959                 //ntd.style.width  = '';
24960                 ntd.innerHTML = '';
24961                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
24962             }
24963             
24964         }
24965         this.redrawAllCells(table);
24966         
24967          
24968         
24969     },
24970     
24971     
24972     
24973     redrawAllCells: function(table)
24974     {
24975         
24976          
24977         var tab = this.node.closest('tr').closest('table');
24978         var ctr = tab.rows[0].parentNode;
24979         Array.from(tab.rows).forEach(function(r, ri){
24980             
24981             Array.from(r.cells).forEach(function(ce, ci){
24982                 ce.parentNode.removeChild(ce);
24983             });
24984             r.parentNode.removeChild(r);
24985         });
24986         for(var r = 0 ; r < table.length; r++) {
24987             var re = tab.rows[r];
24988             
24989             var re = tab.ownerDocument.createElement('tr');
24990             ctr.appendChild(re);
24991             for(var c = 0 ; c < table[r].length; c++) {
24992                 if (table[r][c].cell === false) {
24993                     continue;
24994                 }
24995                 
24996                 re.appendChild(table[r][c].cell);
24997                  
24998                 table[r][c].cell = false;
24999             }
25000         }
25001         
25002     },
25003     updateWidths : function(table)
25004     {
25005         for(var r = 0 ; r < table.length; r++) {
25006            
25007             for(var c = 0 ; c < table[r].length; c++) {
25008                 if (table[r][c].cell === false) {
25009                     continue;
25010                 }
25011                 
25012                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
25013                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
25014                     el.width = Math.floor(this.colWidths[c])  +'%';
25015                     el.updateElement(el.node);
25016                 }
25017                 table[r][c].cell = false; // done
25018             }
25019         }
25020     },
25021     normalizeWidths : function(table)
25022     {
25023     
25024         if (this.colWidths[0] === false) {
25025             var nw = 100.0 / this.colWidths.length;
25026             this.colWidths.forEach(function(w,i) {
25027                 this.colWidths[i] = nw;
25028             },this);
25029             return;
25030         }
25031     
25032         var t = 0, missing = [];
25033         
25034         this.colWidths.forEach(function(w,i) {
25035             //if you mix % and
25036             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
25037             var add =  this.colWidths[i];
25038             if (add > 0) {
25039                 t+=add;
25040                 return;
25041             }
25042             missing.push(i);
25043             
25044             
25045         },this);
25046         var nc = this.colWidths.length;
25047         if (missing.length) {
25048             var mult = (nc - missing.length) / (1.0 * nc);
25049             var t = mult * t;
25050             var ew = (100 -t) / (1.0 * missing.length);
25051             this.colWidths.forEach(function(w,i) {
25052                 if (w > 0) {
25053                     this.colWidths[i] = w * mult;
25054                     return;
25055                 }
25056                 
25057                 this.colWidths[i] = ew;
25058             }, this);
25059             // have to make up numbers..
25060              
25061         }
25062         // now we should have all the widths..
25063         
25064     
25065     },
25066     
25067     shrinkColumn : function()
25068     {
25069         var table = this.toTableArray();
25070         this.normalizeWidths(table);
25071         var col = this.cellData.col;
25072         var nw = this.colWidths[col] * 0.8;
25073         if (nw < 5) {
25074             return;
25075         }
25076         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
25077         this.colWidths.forEach(function(w,i) {
25078             if (i == col) {
25079                  this.colWidths[i] = nw;
25080                 return;
25081             }
25082             this.colWidths[i] += otherAdd
25083         }, this);
25084         this.updateWidths(table);
25085          
25086     },
25087     growColumn : function()
25088     {
25089         var table = this.toTableArray();
25090         this.normalizeWidths(table);
25091         var col = this.cellData.col;
25092         var nw = this.colWidths[col] * 1.2;
25093         if (nw > 90) {
25094             return;
25095         }
25096         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
25097         this.colWidths.forEach(function(w,i) {
25098             if (i == col) {
25099                 this.colWidths[i] = nw;
25100                 return;
25101             }
25102             this.colWidths[i] -= otherSub
25103         }, this);
25104         this.updateWidths(table);
25105          
25106     },
25107     deleteRow : function()
25108     {
25109         // delete this rows 'tr'
25110         // if any of the cells in this row have a rowspan > 1 && row!= this row..
25111         // then reduce the rowspan.
25112         var table = this.toTableArray();
25113         // this.cellData.row;
25114         for (var i =0;i< table[this.cellData.row].length ; i++) {
25115             var c = table[this.cellData.row][i];
25116             if (c.row != this.cellData.row) {
25117                 
25118                 c.rowspan--;
25119                 c.cell.setAttribute('rowspan', c.rowspan);
25120                 continue;
25121             }
25122             if (c.rowspan > 1) {
25123                 c.rowspan--;
25124                 c.cell.setAttribute('rowspan', c.rowspan);
25125             }
25126         }
25127         table.splice(this.cellData.row,1);
25128         this.redrawAllCells(table);
25129         
25130     },
25131     deleteColumn : function()
25132     {
25133         var table = this.toTableArray();
25134         
25135         for (var i =0;i< table.length ; i++) {
25136             var c = table[i][this.cellData.col];
25137             if (c.col != this.cellData.col) {
25138                 table[i][this.cellData.col].colspan--;
25139             } else if (c.colspan > 1) {
25140                 c.colspan--;
25141                 c.cell.setAttribute('colspan', c.colspan);
25142             }
25143             table[i].splice(this.cellData.col,1);
25144         }
25145         
25146         this.redrawAllCells(table);
25147     }
25148     
25149     
25150     
25151     
25152 })
25153
25154 //<script type="text/javascript">
25155
25156 /*
25157  * Based  Ext JS Library 1.1.1
25158  * Copyright(c) 2006-2007, Ext JS, LLC.
25159  * LGPL
25160  *
25161  */
25162  
25163 /**
25164  * @class Roo.HtmlEditorCore
25165  * @extends Roo.Component
25166  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25167  *
25168  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25169  */
25170
25171 Roo.HtmlEditorCore = function(config){
25172     
25173     
25174     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25175     
25176     
25177     this.addEvents({
25178         /**
25179          * @event initialize
25180          * Fires when the editor is fully initialized (including the iframe)
25181          * @param {Roo.HtmlEditorCore} this
25182          */
25183         initialize: true,
25184         /**
25185          * @event activate
25186          * Fires when the editor is first receives the focus. Any insertion must wait
25187          * until after this event.
25188          * @param {Roo.HtmlEditorCore} this
25189          */
25190         activate: true,
25191          /**
25192          * @event beforesync
25193          * Fires before the textarea is updated with content from the editor iframe. Return false
25194          * to cancel the sync.
25195          * @param {Roo.HtmlEditorCore} this
25196          * @param {String} html
25197          */
25198         beforesync: true,
25199          /**
25200          * @event beforepush
25201          * Fires before the iframe editor is updated with content from the textarea. Return false
25202          * to cancel the push.
25203          * @param {Roo.HtmlEditorCore} this
25204          * @param {String} html
25205          */
25206         beforepush: true,
25207          /**
25208          * @event sync
25209          * Fires when the textarea is updated with content from the editor iframe.
25210          * @param {Roo.HtmlEditorCore} this
25211          * @param {String} html
25212          */
25213         sync: true,
25214          /**
25215          * @event push
25216          * Fires when the iframe editor is updated with content from the textarea.
25217          * @param {Roo.HtmlEditorCore} this
25218          * @param {String} html
25219          */
25220         push: true,
25221         
25222         /**
25223          * @event editorevent
25224          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25225          * @param {Roo.HtmlEditorCore} this
25226          */
25227         editorevent: true 
25228          
25229         
25230     });
25231     
25232     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25233     
25234     // defaults : white / black...
25235     this.applyBlacklists();
25236     
25237     
25238     
25239 };
25240
25241
25242 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25243
25244
25245      /**
25246      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25247      */
25248     
25249     owner : false,
25250     
25251      /**
25252      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25253      *                        Roo.resizable.
25254      */
25255     resizable : false,
25256      /**
25257      * @cfg {Number} height (in pixels)
25258      */   
25259     height: 300,
25260    /**
25261      * @cfg {Number} width (in pixels)
25262      */   
25263     width: 500,
25264      /**
25265      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25266      *         if you are doing an email editor, this probably needs disabling, it's designed
25267      */
25268     autoClean: true,
25269     
25270     /**
25271      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25272      */
25273     enableBlocks : true,
25274     /**
25275      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25276      * 
25277      */
25278     stylesheets: false,
25279      /**
25280      * @cfg {String} language default en - language of text (usefull for rtl languages)
25281      * 
25282      */
25283     language: 'en',
25284     
25285     /**
25286      * @cfg {boolean} allowComments - default false - allow comments in HTML source
25287      *          - by default they are stripped - if you are editing email you may need this.
25288      */
25289     allowComments: false,
25290     // id of frame..
25291     frameId: false,
25292     
25293     // private properties
25294     validationEvent : false,
25295     deferHeight: true,
25296     initialized : false,
25297     activated : false,
25298     sourceEditMode : false,
25299     onFocus : Roo.emptyFn,
25300     iframePad:3,
25301     hideMode:'offsets',
25302     
25303     clearUp: true,
25304     
25305     // blacklist + whitelisted elements..
25306     black: false,
25307     white: false,
25308      
25309     bodyCls : '',
25310
25311     
25312     undoManager : false,
25313     /**
25314      * Protected method that will not generally be called directly. It
25315      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25316      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25317      */
25318     getDocMarkup : function(){
25319         // body styles..
25320         var st = '';
25321         
25322         // inherit styels from page...?? 
25323         if (this.stylesheets === false) {
25324             
25325             Roo.get(document.head).select('style').each(function(node) {
25326                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25327             });
25328             
25329             Roo.get(document.head).select('link').each(function(node) { 
25330                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25331             });
25332             
25333         } else if (!this.stylesheets.length) {
25334                 // simple..
25335                 st = '<style type="text/css">' +
25336                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25337                    '</style>';
25338         } else {
25339             for (var i in this.stylesheets) {
25340                 if (typeof(this.stylesheets[i]) != 'string') {
25341                     continue;
25342                 }
25343                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25344             }
25345             
25346         }
25347         
25348         st +=  '<style type="text/css">' +
25349             'IMG { cursor: pointer } ' +
25350         '</style>';
25351         
25352         st += '<meta name="google" content="notranslate">';
25353         
25354         var cls = 'notranslate roo-htmleditor-body';
25355         
25356         if(this.bodyCls.length){
25357             cls += ' ' + this.bodyCls;
25358         }
25359         
25360         return '<html  class="notranslate" translate="no"><head>' + st  +
25361             //<style type="text/css">' +
25362             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25363             //'</style>' +
25364             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25365     },
25366
25367     // private
25368     onRender : function(ct, position)
25369     {
25370         var _t = this;
25371         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25372         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25373         
25374         
25375         this.el.dom.style.border = '0 none';
25376         this.el.dom.setAttribute('tabIndex', -1);
25377         this.el.addClass('x-hidden hide');
25378         
25379         
25380         
25381         if(Roo.isIE){ // fix IE 1px bogus margin
25382             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25383         }
25384        
25385         
25386         this.frameId = Roo.id();
25387         
25388          
25389         
25390         var iframe = this.owner.wrap.createChild({
25391             tag: 'iframe',
25392             cls: 'form-control', // bootstrap..
25393             id: this.frameId,
25394             name: this.frameId,
25395             frameBorder : 'no',
25396             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25397         }, this.el
25398         );
25399         
25400         
25401         this.iframe = iframe.dom;
25402
25403         this.assignDocWin();
25404         
25405         this.doc.designMode = 'on';
25406        
25407         this.doc.open();
25408         this.doc.write(this.getDocMarkup());
25409         this.doc.close();
25410
25411         
25412         var task = { // must defer to wait for browser to be ready
25413             run : function(){
25414                 //console.log("run task?" + this.doc.readyState);
25415                 this.assignDocWin();
25416                 if(this.doc.body || this.doc.readyState == 'complete'){
25417                     try {
25418                         this.doc.designMode="on";
25419                         
25420                     } catch (e) {
25421                         return;
25422                     }
25423                     Roo.TaskMgr.stop(task);
25424                     this.initEditor.defer(10, this);
25425                 }
25426             },
25427             interval : 10,
25428             duration: 10000,
25429             scope: this
25430         };
25431         Roo.TaskMgr.start(task);
25432
25433     },
25434
25435     // private
25436     onResize : function(w, h)
25437     {
25438          Roo.log('resize: ' +w + ',' + h );
25439         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25440         if(!this.iframe){
25441             return;
25442         }
25443         if(typeof w == 'number'){
25444             
25445             this.iframe.style.width = w + 'px';
25446         }
25447         if(typeof h == 'number'){
25448             
25449             this.iframe.style.height = h + 'px';
25450             if(this.doc){
25451                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25452             }
25453         }
25454         
25455     },
25456
25457     /**
25458      * Toggles the editor between standard and source edit mode.
25459      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25460      */
25461     toggleSourceEdit : function(sourceEditMode){
25462         
25463         this.sourceEditMode = sourceEditMode === true;
25464         
25465         if(this.sourceEditMode){
25466  
25467             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
25468             
25469         }else{
25470             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25471             //this.iframe.className = '';
25472             this.deferFocus();
25473         }
25474         //this.setSize(this.owner.wrap.getSize());
25475         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25476     },
25477
25478     
25479   
25480
25481     /**
25482      * Protected method that will not generally be called directly. If you need/want
25483      * custom HTML cleanup, this is the method you should override.
25484      * @param {String} html The HTML to be cleaned
25485      * return {String} The cleaned HTML
25486      */
25487     cleanHtml : function(html)
25488     {
25489         html = String(html);
25490         if(html.length > 5){
25491             if(Roo.isSafari){ // strip safari nonsense
25492                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25493             }
25494         }
25495         if(html == '&nbsp;'){
25496             html = '';
25497         }
25498         return html;
25499     },
25500
25501     /**
25502      * HTML Editor -> Textarea
25503      * Protected method that will not generally be called directly. Syncs the contents
25504      * of the editor iframe with the textarea.
25505      */
25506     syncValue : function()
25507     {
25508         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25509         if(this.initialized){
25510             
25511             if (this.undoManager) {
25512                 this.undoManager.addEvent();
25513             }
25514
25515             
25516             var bd = (this.doc.body || this.doc.documentElement);
25517            
25518             
25519             var sel = this.win.getSelection();
25520             
25521             var div = document.createElement('div');
25522             div.innerHTML = bd.innerHTML;
25523             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25524             if (gtx.length > 0) {
25525                 var rm = gtx.item(0).parentNode;
25526                 rm.parentNode.removeChild(rm);
25527             }
25528             
25529            
25530             if (this.enableBlocks) {
25531                 new Roo.htmleditor.FilterBlock({ node : div });
25532             }
25533             //?? tidy?
25534             var tidy = new Roo.htmleditor.TidySerializer({
25535                 inner:  true
25536             });
25537             var html  = tidy.serialize(div);
25538             
25539             
25540             if(Roo.isSafari){
25541                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25542                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25543                 if(m && m[1]){
25544                     html = '<div style="'+m[0]+'">' + html + '</div>';
25545                 }
25546             }
25547             html = this.cleanHtml(html);
25548             // fix up the special chars.. normaly like back quotes in word...
25549             // however we do not want to do this with chinese..
25550             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25551                 
25552                 var cc = match.charCodeAt();
25553
25554                 // Get the character value, handling surrogate pairs
25555                 if (match.length == 2) {
25556                     // It's a surrogate pair, calculate the Unicode code point
25557                     var high = match.charCodeAt(0) - 0xD800;
25558                     var low  = match.charCodeAt(1) - 0xDC00;
25559                     cc = (high * 0x400) + low + 0x10000;
25560                 }  else if (
25561                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25562                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25563                     (cc >= 0xf900 && cc < 0xfb00 )
25564                 ) {
25565                         return match;
25566                 }  
25567          
25568                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25569                 return "&#" + cc + ";";
25570                 
25571                 
25572             });
25573             
25574             
25575              
25576             if(this.owner.fireEvent('beforesync', this, html) !== false){
25577                 this.el.dom.value = html;
25578                 this.owner.fireEvent('sync', this, html);
25579             }
25580         }
25581     },
25582
25583     /**
25584      * TEXTAREA -> EDITABLE
25585      * Protected method that will not generally be called directly. Pushes the value of the textarea
25586      * into the iframe editor.
25587      */
25588     pushValue : function()
25589     {
25590         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25591         if(this.initialized){
25592             var v = this.el.dom.value.trim();
25593             
25594             
25595             if(this.owner.fireEvent('beforepush', this, v) !== false){
25596                 var d = (this.doc.body || this.doc.documentElement);
25597                 d.innerHTML = v;
25598                  
25599                 this.el.dom.value = d.innerHTML;
25600                 this.owner.fireEvent('push', this, v);
25601             }
25602             if (this.autoClean) {
25603                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25604                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25605             }
25606             if (this.enableBlocks) {
25607                 Roo.htmleditor.Block.initAll(this.doc.body);
25608             }
25609             
25610             this.updateLanguage();
25611             
25612             var lc = this.doc.body.lastChild;
25613             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25614                 // add an extra line at the end.
25615                 this.doc.body.appendChild(this.doc.createElement('br'));
25616             }
25617             
25618             
25619         }
25620     },
25621
25622     // private
25623     deferFocus : function(){
25624         this.focus.defer(10, this);
25625     },
25626
25627     // doc'ed in Field
25628     focus : function(){
25629         if(this.win && !this.sourceEditMode){
25630             this.win.focus();
25631         }else{
25632             this.el.focus();
25633         }
25634     },
25635     
25636     assignDocWin: function()
25637     {
25638         var iframe = this.iframe;
25639         
25640          if(Roo.isIE){
25641             this.doc = iframe.contentWindow.document;
25642             this.win = iframe.contentWindow;
25643         } else {
25644 //            if (!Roo.get(this.frameId)) {
25645 //                return;
25646 //            }
25647 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25648 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25649             
25650             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25651                 return;
25652             }
25653             
25654             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25655             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25656         }
25657     },
25658     
25659     // private
25660     initEditor : function(){
25661         //console.log("INIT EDITOR");
25662         this.assignDocWin();
25663         
25664         
25665         
25666         this.doc.designMode="on";
25667         this.doc.open();
25668         this.doc.write(this.getDocMarkup());
25669         this.doc.close();
25670         
25671         var dbody = (this.doc.body || this.doc.documentElement);
25672         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25673         // this copies styles from the containing element into thsi one..
25674         // not sure why we need all of this..
25675         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25676         
25677         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25678         //ss['background-attachment'] = 'fixed'; // w3c
25679         dbody.bgProperties = 'fixed'; // ie
25680         dbody.setAttribute("translate", "no");
25681         
25682         //Roo.DomHelper.applyStyles(dbody, ss);
25683         Roo.EventManager.on(this.doc, {
25684              
25685             'mouseup': this.onEditorEvent,
25686             'dblclick': this.onEditorEvent,
25687             'click': this.onEditorEvent,
25688             'keyup': this.onEditorEvent,
25689             
25690             buffer:100,
25691             scope: this
25692         });
25693         Roo.EventManager.on(this.doc, {
25694             'paste': this.onPasteEvent,
25695             scope : this
25696         });
25697         if(Roo.isGecko){
25698             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25699         }
25700         //??? needed???
25701         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25702             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25703         }
25704         this.initialized = true;
25705
25706         
25707         // initialize special key events - enter
25708         new Roo.htmleditor.KeyEnter({core : this});
25709         
25710          
25711         
25712         this.owner.fireEvent('initialize', this);
25713         this.pushValue();
25714     },
25715     // this is to prevent a href clicks resulting in a redirect?
25716    
25717     onPasteEvent : function(e,v)
25718     {
25719         // I think we better assume paste is going to be a dirty load of rubish from word..
25720         
25721         // even pasting into a 'email version' of this widget will have to clean up that mess.
25722         var cd = (e.browserEvent.clipboardData || window.clipboardData);
25723         
25724         // check what type of paste - if it's an image, then handle it differently.
25725         if (cd.files && cd.files.length > 0) {
25726             // pasting images?
25727             var urlAPI = (window.createObjectURL && window) || 
25728                 (window.URL && URL.revokeObjectURL && URL) || 
25729                 (window.webkitURL && webkitURL);
25730     
25731             var url = urlAPI.createObjectURL( cd.files[0]);
25732             this.insertAtCursor('<img src=" + url + ">');
25733             return false;
25734         }
25735         if (cd.types.indexOf('text/html') < 0 ) {
25736             return false;
25737         }
25738         var images = [];
25739         var html = cd.getData('text/html'); // clipboard event
25740         if (cd.types.indexOf('text/rtf') > -1) {
25741             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25742             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25743         }
25744         //Roo.log(images);
25745         //Roo.log(imgs);
25746         // fixme..
25747         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
25748                        .map(function(g) { return g.toDataURL(); })
25749                        .filter(function(g) { return g != 'about:blank'; });
25750         
25751         
25752         html = this.cleanWordChars(html);
25753         
25754         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25755         
25756         
25757         var sn = this.getParentElement();
25758         // check if d contains a table, and prevent nesting??
25759         //Roo.log(d.getElementsByTagName('table'));
25760         //Roo.log(sn);
25761         //Roo.log(sn.closest('table'));
25762         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25763             e.preventDefault();
25764             this.insertAtCursor("You can not nest tables");
25765             //Roo.log("prevent?"); // fixme - 
25766             return false;
25767         }
25768         
25769         if (images.length > 0) {
25770             Roo.each(d.getElementsByTagName('img'), function(img, i) {
25771                 img.setAttribute('src', images[i]);
25772             });
25773         }
25774         if (this.autoClean) {
25775             new Roo.htmleditor.FilterWord({ node : d });
25776             
25777             new Roo.htmleditor.FilterStyleToTag({ node : d });
25778             new Roo.htmleditor.FilterAttributes({
25779                 node : d,
25780                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
25781                 attrib_clean : ['href', 'src' ] 
25782             });
25783             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25784             // should be fonts..
25785             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25786             new Roo.htmleditor.FilterParagraph({ node : d });
25787             new Roo.htmleditor.FilterSpan({ node : d });
25788             new Roo.htmleditor.FilterLongBr({ node : d });
25789             new Roo.htmleditor.FilterComment({ node : d });
25790             
25791             
25792         }
25793         if (this.enableBlocks) {
25794                 
25795             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25796                 if (img.closest('figure')) { // assume!! that it's aready
25797                     return;
25798                 }
25799                 var fig  = new Roo.htmleditor.BlockFigure({
25800                     image_src  : img.src
25801                 });
25802                 fig.updateElement(img); // replace it..
25803                 
25804             });
25805         }
25806         
25807         
25808         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
25809         if (this.enableBlocks) {
25810             Roo.htmleditor.Block.initAll(this.doc.body);
25811         }
25812          
25813         
25814         e.preventDefault();
25815         return false;
25816         // default behaveiour should be our local cleanup paste? (optional?)
25817         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25818         //this.owner.fireEvent('paste', e, v);
25819     },
25820     // private
25821     onDestroy : function(){
25822         
25823         
25824         
25825         if(this.rendered){
25826             
25827             //for (var i =0; i < this.toolbars.length;i++) {
25828             //    // fixme - ask toolbars for heights?
25829             //    this.toolbars[i].onDestroy();
25830            // }
25831             
25832             //this.wrap.dom.innerHTML = '';
25833             //this.wrap.remove();
25834         }
25835     },
25836
25837     // private
25838     onFirstFocus : function(){
25839         
25840         this.assignDocWin();
25841         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25842         
25843         this.activated = true;
25844          
25845     
25846         if(Roo.isGecko){ // prevent silly gecko errors
25847             this.win.focus();
25848             var s = this.win.getSelection();
25849             if(!s.focusNode || s.focusNode.nodeType != 3){
25850                 var r = s.getRangeAt(0);
25851                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25852                 r.collapse(true);
25853                 this.deferFocus();
25854             }
25855             try{
25856                 this.execCmd('useCSS', true);
25857                 this.execCmd('styleWithCSS', false);
25858             }catch(e){}
25859         }
25860         this.owner.fireEvent('activate', this);
25861     },
25862
25863     // private
25864     adjustFont: function(btn){
25865         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25866         //if(Roo.isSafari){ // safari
25867         //    adjust *= 2;
25868        // }
25869         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25870         if(Roo.isSafari){ // safari
25871             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25872             v =  (v < 10) ? 10 : v;
25873             v =  (v > 48) ? 48 : v;
25874             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25875             
25876         }
25877         
25878         
25879         v = Math.max(1, v+adjust);
25880         
25881         this.execCmd('FontSize', v  );
25882     },
25883
25884     onEditorEvent : function(e)
25885     {
25886          
25887         
25888         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25889             return; // we do not handle this.. (undo manager does..)
25890         }
25891         // in theory this detects if the last element is not a br, then we try and do that.
25892         // its so clicking in space at bottom triggers adding a br and moving the cursor.
25893         if (e &&
25894             e.target.nodeName == 'BODY' &&
25895             e.type == "mouseup" &&
25896             this.doc.body.lastChild
25897            ) {
25898             var lc = this.doc.body.lastChild;
25899             // gtx-trans is google translate plugin adding crap.
25900             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25901                 lc = lc.previousSibling;
25902             }
25903             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25904             // if last element is <BR> - then dont do anything.
25905             
25906                 var ns = this.doc.createElement('br');
25907                 this.doc.body.appendChild(ns);
25908                 range = this.doc.createRange();
25909                 range.setStartAfter(ns);
25910                 range.collapse(true);
25911                 var sel = this.win.getSelection();
25912                 sel.removeAllRanges();
25913                 sel.addRange(range);
25914             }
25915         }
25916         
25917         
25918         
25919         this.fireEditorEvent(e);
25920       //  this.updateToolbar();
25921         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25922     },
25923     
25924     fireEditorEvent: function(e)
25925     {
25926         this.owner.fireEvent('editorevent', this, e);
25927     },
25928
25929     insertTag : function(tg)
25930     {
25931         // could be a bit smarter... -> wrap the current selected tRoo..
25932         if (tg.toLowerCase() == 'span' ||
25933             tg.toLowerCase() == 'code' ||
25934             tg.toLowerCase() == 'sup' ||
25935             tg.toLowerCase() == 'sub' 
25936             ) {
25937             
25938             range = this.createRange(this.getSelection());
25939             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25940             wrappingNode.appendChild(range.extractContents());
25941             range.insertNode(wrappingNode);
25942
25943             return;
25944             
25945             
25946             
25947         }
25948         this.execCmd("formatblock",   tg);
25949         this.undoManager.addEvent(); 
25950     },
25951     
25952     insertText : function(txt)
25953     {
25954         
25955         
25956         var range = this.createRange();
25957         range.deleteContents();
25958                //alert(Sender.getAttribute('label'));
25959                
25960         range.insertNode(this.doc.createTextNode(txt));
25961         this.undoManager.addEvent();
25962     } ,
25963     
25964      
25965
25966     /**
25967      * Executes a Midas editor command on the editor document and performs necessary focus and
25968      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25969      * @param {String} cmd The Midas command
25970      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25971      */
25972     relayCmd : function(cmd, value)
25973     {
25974         
25975         switch (cmd) {
25976             case 'justifyleft':
25977             case 'justifyright':
25978             case 'justifycenter':
25979                 // if we are in a cell, then we will adjust the
25980                 var n = this.getParentElement();
25981                 var td = n.closest('td');
25982                 if (td) {
25983                     var bl = Roo.htmleditor.Block.factory(td);
25984                     bl.textAlign = cmd.replace('justify','');
25985                     bl.updateElement();
25986                     this.owner.fireEvent('editorevent', this);
25987                     return;
25988                 }
25989                 this.execCmd('styleWithCSS', true); // 
25990                 break;
25991             case 'bold':
25992             case 'italic':
25993                 // if there is no selection, then we insert, and set the curson inside it..
25994                 this.execCmd('styleWithCSS', false); 
25995                 break;
25996                 
25997         
25998             default:
25999                 break;
26000         }
26001         
26002         
26003         this.win.focus();
26004         this.execCmd(cmd, value);
26005         this.owner.fireEvent('editorevent', this);
26006         //this.updateToolbar();
26007         this.owner.deferFocus();
26008     },
26009
26010     /**
26011      * Executes a Midas editor command directly on the editor document.
26012      * For visual commands, you should use {@link #relayCmd} instead.
26013      * <b>This should only be called after the editor is initialized.</b>
26014      * @param {String} cmd The Midas command
26015      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26016      */
26017     execCmd : function(cmd, value){
26018         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26019         this.syncValue();
26020     },
26021  
26022  
26023    
26024     /**
26025      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26026      * to insert tRoo.
26027      * @param {String} text | dom node.. 
26028      */
26029     insertAtCursor : function(text)
26030     {
26031         
26032         if(!this.activated){
26033             return;
26034         }
26035          
26036         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26037             this.win.focus();
26038             
26039             
26040             // from jquery ui (MIT licenced)
26041             var range, node;
26042             var win = this.win;
26043             
26044             if (win.getSelection && win.getSelection().getRangeAt) {
26045                 
26046                 // delete the existing?
26047                 
26048                 this.createRange(this.getSelection()).deleteContents();
26049                 range = win.getSelection().getRangeAt(0);
26050                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26051                 range.insertNode(node);
26052                 range = range.cloneRange();
26053                 range.collapse(false);
26054                  
26055                 win.getSelection().removeAllRanges();
26056                 win.getSelection().addRange(range);
26057                 
26058                 
26059                 
26060             } else if (win.document.selection && win.document.selection.createRange) {
26061                 // no firefox support
26062                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26063                 win.document.selection.createRange().pasteHTML(txt);
26064             
26065             } else {
26066                 // no firefox support
26067                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26068                 this.execCmd('InsertHTML', txt);
26069             } 
26070             this.syncValue();
26071             
26072             this.deferFocus();
26073         }
26074     },
26075  // private
26076     mozKeyPress : function(e){
26077         if(e.ctrlKey){
26078             var c = e.getCharCode(), cmd;
26079           
26080             if(c > 0){
26081                 c = String.fromCharCode(c).toLowerCase();
26082                 switch(c){
26083                     case 'b':
26084                         cmd = 'bold';
26085                         break;
26086                     case 'i':
26087                         cmd = 'italic';
26088                         break;
26089                     
26090                     case 'u':
26091                         cmd = 'underline';
26092                         break;
26093                     
26094                     //case 'v':
26095                       //  this.cleanUpPaste.defer(100, this);
26096                       //  return;
26097                         
26098                 }
26099                 if(cmd){
26100                     
26101                     this.relayCmd(cmd);
26102                     //this.win.focus();
26103                     //this.execCmd(cmd);
26104                     //this.deferFocus();
26105                     e.preventDefault();
26106                 }
26107                 
26108             }
26109         }
26110     },
26111
26112     // private
26113     fixKeys : function(){ // load time branching for fastest keydown performance
26114         
26115         
26116         if(Roo.isIE){
26117             return function(e){
26118                 var k = e.getKey(), r;
26119                 if(k == e.TAB){
26120                     e.stopEvent();
26121                     r = this.doc.selection.createRange();
26122                     if(r){
26123                         r.collapse(true);
26124                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26125                         this.deferFocus();
26126                     }
26127                     return;
26128                 }
26129                 /// this is handled by Roo.htmleditor.KeyEnter
26130                  /*
26131                 if(k == e.ENTER){
26132                     r = this.doc.selection.createRange();
26133                     if(r){
26134                         var target = r.parentElement();
26135                         if(!target || target.tagName.toLowerCase() != 'li'){
26136                             e.stopEvent();
26137                             r.pasteHTML('<br/>');
26138                             r.collapse(false);
26139                             r.select();
26140                         }
26141                     }
26142                 }
26143                 */
26144                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26145                 //    this.cleanUpPaste.defer(100, this);
26146                 //    return;
26147                 //}
26148                 
26149                 
26150             };
26151         }else if(Roo.isOpera){
26152             return function(e){
26153                 var k = e.getKey();
26154                 if(k == e.TAB){
26155                     e.stopEvent();
26156                     this.win.focus();
26157                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26158                     this.deferFocus();
26159                 }
26160                
26161                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26162                 //    this.cleanUpPaste.defer(100, this);
26163                  //   return;
26164                 //}
26165                 
26166             };
26167         }else if(Roo.isSafari){
26168             return function(e){
26169                 var k = e.getKey();
26170                 
26171                 if(k == e.TAB){
26172                     e.stopEvent();
26173                     this.execCmd('InsertText','\t');
26174                     this.deferFocus();
26175                     return;
26176                 }
26177                  this.mozKeyPress(e);
26178                 
26179                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26180                  //   this.cleanUpPaste.defer(100, this);
26181                  //   return;
26182                // }
26183                 
26184              };
26185         }
26186     }(),
26187     
26188     getAllAncestors: function()
26189     {
26190         var p = this.getSelectedNode();
26191         var a = [];
26192         if (!p) {
26193             a.push(p); // push blank onto stack..
26194             p = this.getParentElement();
26195         }
26196         
26197         
26198         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26199             a.push(p);
26200             p = p.parentNode;
26201         }
26202         a.push(this.doc.body);
26203         return a;
26204     },
26205     lastSel : false,
26206     lastSelNode : false,
26207     
26208     
26209     getSelection : function() 
26210     {
26211         this.assignDocWin();
26212         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26213     },
26214     /**
26215      * Select a dom node
26216      * @param {DomElement} node the node to select
26217      */
26218     selectNode : function(node, collapse)
26219     {
26220         var nodeRange = node.ownerDocument.createRange();
26221         try {
26222             nodeRange.selectNode(node);
26223         } catch (e) {
26224             nodeRange.selectNodeContents(node);
26225         }
26226         if (collapse === true) {
26227             nodeRange.collapse(true);
26228         }
26229         //
26230         var s = this.win.getSelection();
26231         s.removeAllRanges();
26232         s.addRange(nodeRange);
26233     },
26234     
26235     getSelectedNode: function() 
26236     {
26237         // this may only work on Gecko!!!
26238         
26239         // should we cache this!!!!
26240         
26241          
26242          
26243         var range = this.createRange(this.getSelection()).cloneRange();
26244         
26245         if (Roo.isIE) {
26246             var parent = range.parentElement();
26247             while (true) {
26248                 var testRange = range.duplicate();
26249                 testRange.moveToElementText(parent);
26250                 if (testRange.inRange(range)) {
26251                     break;
26252                 }
26253                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26254                     break;
26255                 }
26256                 parent = parent.parentElement;
26257             }
26258             return parent;
26259         }
26260         
26261         // is ancestor a text element.
26262         var ac =  range.commonAncestorContainer;
26263         if (ac.nodeType == 3) {
26264             ac = ac.parentNode;
26265         }
26266         
26267         var ar = ac.childNodes;
26268          
26269         var nodes = [];
26270         var other_nodes = [];
26271         var has_other_nodes = false;
26272         for (var i=0;i<ar.length;i++) {
26273             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26274                 continue;
26275             }
26276             // fullly contained node.
26277             
26278             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26279                 nodes.push(ar[i]);
26280                 continue;
26281             }
26282             
26283             // probably selected..
26284             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26285                 other_nodes.push(ar[i]);
26286                 continue;
26287             }
26288             // outer..
26289             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26290                 continue;
26291             }
26292             
26293             
26294             has_other_nodes = true;
26295         }
26296         if (!nodes.length && other_nodes.length) {
26297             nodes= other_nodes;
26298         }
26299         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26300             return false;
26301         }
26302         
26303         return nodes[0];
26304     },
26305     
26306     
26307     createRange: function(sel)
26308     {
26309         // this has strange effects when using with 
26310         // top toolbar - not sure if it's a great idea.
26311         //this.editor.contentWindow.focus();
26312         if (typeof sel != "undefined") {
26313             try {
26314                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26315             } catch(e) {
26316                 return this.doc.createRange();
26317             }
26318         } else {
26319             return this.doc.createRange();
26320         }
26321     },
26322     getParentElement: function()
26323     {
26324         
26325         this.assignDocWin();
26326         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26327         
26328         var range = this.createRange(sel);
26329          
26330         try {
26331             var p = range.commonAncestorContainer;
26332             while (p.nodeType == 3) { // text node
26333                 p = p.parentNode;
26334             }
26335             return p;
26336         } catch (e) {
26337             return null;
26338         }
26339     
26340     },
26341     /***
26342      *
26343      * Range intersection.. the hard stuff...
26344      *  '-1' = before
26345      *  '0' = hits..
26346      *  '1' = after.
26347      *         [ -- selected range --- ]
26348      *   [fail]                        [fail]
26349      *
26350      *    basically..
26351      *      if end is before start or  hits it. fail.
26352      *      if start is after end or hits it fail.
26353      *
26354      *   if either hits (but other is outside. - then it's not 
26355      *   
26356      *    
26357      **/
26358     
26359     
26360     // @see http://www.thismuchiknow.co.uk/?p=64.
26361     rangeIntersectsNode : function(range, node)
26362     {
26363         var nodeRange = node.ownerDocument.createRange();
26364         try {
26365             nodeRange.selectNode(node);
26366         } catch (e) {
26367             nodeRange.selectNodeContents(node);
26368         }
26369     
26370         var rangeStartRange = range.cloneRange();
26371         rangeStartRange.collapse(true);
26372     
26373         var rangeEndRange = range.cloneRange();
26374         rangeEndRange.collapse(false);
26375     
26376         var nodeStartRange = nodeRange.cloneRange();
26377         nodeStartRange.collapse(true);
26378     
26379         var nodeEndRange = nodeRange.cloneRange();
26380         nodeEndRange.collapse(false);
26381     
26382         return rangeStartRange.compareBoundaryPoints(
26383                  Range.START_TO_START, nodeEndRange) == -1 &&
26384                rangeEndRange.compareBoundaryPoints(
26385                  Range.START_TO_START, nodeStartRange) == 1;
26386         
26387          
26388     },
26389     rangeCompareNode : function(range, node)
26390     {
26391         var nodeRange = node.ownerDocument.createRange();
26392         try {
26393             nodeRange.selectNode(node);
26394         } catch (e) {
26395             nodeRange.selectNodeContents(node);
26396         }
26397         
26398         
26399         range.collapse(true);
26400     
26401         nodeRange.collapse(true);
26402      
26403         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26404         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26405          
26406         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26407         
26408         var nodeIsBefore   =  ss == 1;
26409         var nodeIsAfter    = ee == -1;
26410         
26411         if (nodeIsBefore && nodeIsAfter) {
26412             return 0; // outer
26413         }
26414         if (!nodeIsBefore && nodeIsAfter) {
26415             return 1; //right trailed.
26416         }
26417         
26418         if (nodeIsBefore && !nodeIsAfter) {
26419             return 2;  // left trailed.
26420         }
26421         // fully contined.
26422         return 3;
26423     },
26424  
26425     cleanWordChars : function(input) {// change the chars to hex code
26426         
26427        var swapCodes  = [ 
26428             [    8211, "&#8211;" ], 
26429             [    8212, "&#8212;" ], 
26430             [    8216,  "'" ],  
26431             [    8217, "'" ],  
26432             [    8220, '"' ],  
26433             [    8221, '"' ],  
26434             [    8226, "*" ],  
26435             [    8230, "..." ]
26436         ]; 
26437         var output = input;
26438         Roo.each(swapCodes, function(sw) { 
26439             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26440             
26441             output = output.replace(swapper, sw[1]);
26442         });
26443         
26444         return output;
26445     },
26446     
26447      
26448     
26449         
26450     
26451     cleanUpChild : function (node)
26452     {
26453         
26454         new Roo.htmleditor.FilterComment({node : node});
26455         new Roo.htmleditor.FilterAttributes({
26456                 node : node,
26457                 attrib_black : this.ablack,
26458                 attrib_clean : this.aclean,
26459                 style_white : this.cwhite,
26460                 style_black : this.cblack
26461         });
26462         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26463         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26464          
26465         
26466     },
26467     
26468     /**
26469      * Clean up MS wordisms...
26470      * @deprecated - use filter directly
26471      */
26472     cleanWord : function(node)
26473     {
26474         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26475         
26476     },
26477    
26478     
26479     /**
26480
26481      * @deprecated - use filters
26482      */
26483     cleanTableWidths : function(node)
26484     {
26485         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26486         
26487  
26488     },
26489     
26490      
26491         
26492     applyBlacklists : function()
26493     {
26494         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26495         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26496         
26497         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
26498         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
26499         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
26500         
26501         this.white = [];
26502         this.black = [];
26503         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26504             if (b.indexOf(tag) > -1) {
26505                 return;
26506             }
26507             this.white.push(tag);
26508             
26509         }, this);
26510         
26511         Roo.each(w, function(tag) {
26512             if (b.indexOf(tag) > -1) {
26513                 return;
26514             }
26515             if (this.white.indexOf(tag) > -1) {
26516                 return;
26517             }
26518             this.white.push(tag);
26519             
26520         }, this);
26521         
26522         
26523         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26524             if (w.indexOf(tag) > -1) {
26525                 return;
26526             }
26527             this.black.push(tag);
26528             
26529         }, this);
26530         
26531         Roo.each(b, function(tag) {
26532             if (w.indexOf(tag) > -1) {
26533                 return;
26534             }
26535             if (this.black.indexOf(tag) > -1) {
26536                 return;
26537             }
26538             this.black.push(tag);
26539             
26540         }, this);
26541         
26542         
26543         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26544         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26545         
26546         this.cwhite = [];
26547         this.cblack = [];
26548         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26549             if (b.indexOf(tag) > -1) {
26550                 return;
26551             }
26552             this.cwhite.push(tag);
26553             
26554         }, this);
26555         
26556         Roo.each(w, function(tag) {
26557             if (b.indexOf(tag) > -1) {
26558                 return;
26559             }
26560             if (this.cwhite.indexOf(tag) > -1) {
26561                 return;
26562             }
26563             this.cwhite.push(tag);
26564             
26565         }, this);
26566         
26567         
26568         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26569             if (w.indexOf(tag) > -1) {
26570                 return;
26571             }
26572             this.cblack.push(tag);
26573             
26574         }, this);
26575         
26576         Roo.each(b, function(tag) {
26577             if (w.indexOf(tag) > -1) {
26578                 return;
26579             }
26580             if (this.cblack.indexOf(tag) > -1) {
26581                 return;
26582             }
26583             this.cblack.push(tag);
26584             
26585         }, this);
26586     },
26587     
26588     setStylesheets : function(stylesheets)
26589     {
26590         if(typeof(stylesheets) == 'string'){
26591             Roo.get(this.iframe.contentDocument.head).createChild({
26592                 tag : 'link',
26593                 rel : 'stylesheet',
26594                 type : 'text/css',
26595                 href : stylesheets
26596             });
26597             
26598             return;
26599         }
26600         var _this = this;
26601      
26602         Roo.each(stylesheets, function(s) {
26603             if(!s.length){
26604                 return;
26605             }
26606             
26607             Roo.get(_this.iframe.contentDocument.head).createChild({
26608                 tag : 'link',
26609                 rel : 'stylesheet',
26610                 type : 'text/css',
26611                 href : s
26612             });
26613         });
26614
26615         
26616     },
26617     
26618     
26619     updateLanguage : function()
26620     {
26621         if (!this.iframe || !this.iframe.contentDocument) {
26622             return;
26623         }
26624         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26625     },
26626     
26627     
26628     removeStylesheets : function()
26629     {
26630         var _this = this;
26631         
26632         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26633             s.remove();
26634         });
26635     },
26636     
26637     setStyle : function(style)
26638     {
26639         Roo.get(this.iframe.contentDocument.head).createChild({
26640             tag : 'style',
26641             type : 'text/css',
26642             html : style
26643         });
26644
26645         return;
26646     }
26647     
26648     // hide stuff that is not compatible
26649     /**
26650      * @event blur
26651      * @hide
26652      */
26653     /**
26654      * @event change
26655      * @hide
26656      */
26657     /**
26658      * @event focus
26659      * @hide
26660      */
26661     /**
26662      * @event specialkey
26663      * @hide
26664      */
26665     /**
26666      * @cfg {String} fieldClass @hide
26667      */
26668     /**
26669      * @cfg {String} focusClass @hide
26670      */
26671     /**
26672      * @cfg {String} autoCreate @hide
26673      */
26674     /**
26675      * @cfg {String} inputType @hide
26676      */
26677     /**
26678      * @cfg {String} invalidClass @hide
26679      */
26680     /**
26681      * @cfg {String} invalidText @hide
26682      */
26683     /**
26684      * @cfg {String} msgFx @hide
26685      */
26686     /**
26687      * @cfg {String} validateOnBlur @hide
26688      */
26689 });
26690
26691 Roo.HtmlEditorCore.white = [
26692         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26693         
26694        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
26695        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
26696        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
26697        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
26698        'TABLE',   'UL',         'XMP', 
26699        
26700        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
26701       'THEAD',   'TR', 
26702      
26703       'DIR', 'MENU', 'OL', 'UL', 'DL',
26704        
26705       'EMBED',  'OBJECT'
26706 ];
26707
26708
26709 Roo.HtmlEditorCore.black = [
26710     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26711         'APPLET', // 
26712         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
26713         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
26714         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
26715         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
26716         //'FONT' // CLEAN LATER..
26717         'COLGROUP', 'COL'   // messy tables.
26718         
26719         
26720 ];
26721 Roo.HtmlEditorCore.clean = [ // ?? needed???
26722      'SCRIPT', 'STYLE', 'TITLE', 'XML'
26723 ];
26724 Roo.HtmlEditorCore.tag_remove = [
26725     'FONT', 'TBODY'  
26726 ];
26727 // attributes..
26728
26729 Roo.HtmlEditorCore.ablack = [
26730     'on'
26731 ];
26732     
26733 Roo.HtmlEditorCore.aclean = [ 
26734     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26735 ];
26736
26737 // protocols..
26738 Roo.HtmlEditorCore.pwhite= [
26739         'http',  'https',  'mailto'
26740 ];
26741
26742 // white listed style attributes.
26743 Roo.HtmlEditorCore.cwhite= [
26744       //  'text-align', /// default is to allow most things..
26745       
26746          
26747 //        'font-size'//??
26748 ];
26749
26750 // black listed style attributes.
26751 Roo.HtmlEditorCore.cblack= [
26752       //  'font-size' -- this can be set by the project 
26753 ];
26754
26755
26756
26757
26758     //<script type="text/javascript">
26759
26760 /*
26761  * Ext JS Library 1.1.1
26762  * Copyright(c) 2006-2007, Ext JS, LLC.
26763  * Licence LGPL
26764  * 
26765  */
26766  
26767  
26768 Roo.form.HtmlEditor = function(config){
26769     
26770     
26771     
26772     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26773     
26774     if (!this.toolbars) {
26775         this.toolbars = [];
26776     }
26777     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26778     
26779     
26780 };
26781
26782 /**
26783  * @class Roo.form.HtmlEditor
26784  * @extends Roo.form.Field
26785  * Provides a lightweight HTML Editor component.
26786  *
26787  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26788  * 
26789  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26790  * supported by this editor.</b><br/><br/>
26791  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26792  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26793  */
26794 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26795     /**
26796      * @cfg {Boolean} clearUp
26797      */
26798     clearUp : true,
26799       /**
26800      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26801      */
26802     toolbars : false,
26803    
26804      /**
26805      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26806      *                        Roo.resizable.
26807      */
26808     resizable : false,
26809      /**
26810      * @cfg {Number} height (in pixels)
26811      */   
26812     height: 300,
26813    /**
26814      * @cfg {Number} width (in pixels)
26815      */   
26816     width: 500,
26817     
26818     /**
26819      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
26820      * 
26821      */
26822     stylesheets: false,
26823     
26824     
26825      /**
26826      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26827      * 
26828      */
26829     cblack: false,
26830     /**
26831      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26832      * 
26833      */
26834     cwhite: false,
26835     
26836      /**
26837      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26838      * 
26839      */
26840     black: false,
26841     /**
26842      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26843      * 
26844      */
26845     white: false,
26846     /**
26847      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26848      */
26849     allowComments: false,
26850     /**
26851      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26852      */
26853     enableBlocks : true,
26854     
26855     /**
26856      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26857      *         if you are doing an email editor, this probably needs disabling, it's designed
26858      */
26859     autoClean: true,
26860     /**
26861      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
26862      */
26863     bodyCls : '',
26864     /**
26865      * @cfg {String} language default en - language of text (usefull for rtl languages)
26866      * 
26867      */
26868     language: 'en',
26869     
26870      
26871     // id of frame..
26872     frameId: false,
26873     
26874     // private properties
26875     validationEvent : false,
26876     deferHeight: true,
26877     initialized : false,
26878     activated : false,
26879     
26880     onFocus : Roo.emptyFn,
26881     iframePad:3,
26882     hideMode:'offsets',
26883     
26884     actionMode : 'container', // defaults to hiding it...
26885     
26886     defaultAutoCreate : { // modified by initCompnoent..
26887         tag: "textarea",
26888         style:"width:500px;height:300px;",
26889         autocomplete: "new-password"
26890     },
26891
26892     // private
26893     initComponent : function(){
26894         this.addEvents({
26895             /**
26896              * @event initialize
26897              * Fires when the editor is fully initialized (including the iframe)
26898              * @param {HtmlEditor} this
26899              */
26900             initialize: true,
26901             /**
26902              * @event activate
26903              * Fires when the editor is first receives the focus. Any insertion must wait
26904              * until after this event.
26905              * @param {HtmlEditor} this
26906              */
26907             activate: true,
26908              /**
26909              * @event beforesync
26910              * Fires before the textarea is updated with content from the editor iframe. Return false
26911              * to cancel the sync.
26912              * @param {HtmlEditor} this
26913              * @param {String} html
26914              */
26915             beforesync: true,
26916              /**
26917              * @event beforepush
26918              * Fires before the iframe editor is updated with content from the textarea. Return false
26919              * to cancel the push.
26920              * @param {HtmlEditor} this
26921              * @param {String} html
26922              */
26923             beforepush: true,
26924              /**
26925              * @event sync
26926              * Fires when the textarea is updated with content from the editor iframe.
26927              * @param {HtmlEditor} this
26928              * @param {String} html
26929              */
26930             sync: true,
26931              /**
26932              * @event push
26933              * Fires when the iframe editor is updated with content from the textarea.
26934              * @param {HtmlEditor} this
26935              * @param {String} html
26936              */
26937             push: true,
26938              /**
26939              * @event editmodechange
26940              * Fires when the editor switches edit modes
26941              * @param {HtmlEditor} this
26942              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26943              */
26944             editmodechange: true,
26945             /**
26946              * @event editorevent
26947              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26948              * @param {HtmlEditor} this
26949              */
26950             editorevent: true,
26951             /**
26952              * @event firstfocus
26953              * Fires when on first focus - needed by toolbars..
26954              * @param {HtmlEditor} this
26955              */
26956             firstfocus: true,
26957             /**
26958              * @event autosave
26959              * Auto save the htmlEditor value as a file into Events
26960              * @param {HtmlEditor} this
26961              */
26962             autosave: true,
26963             /**
26964              * @event savedpreview
26965              * preview the saved version of htmlEditor
26966              * @param {HtmlEditor} this
26967              */
26968             savedpreview: true,
26969             
26970             /**
26971             * @event stylesheetsclick
26972             * Fires when press the Sytlesheets button
26973             * @param {Roo.HtmlEditorCore} this
26974             */
26975             stylesheetsclick: true,
26976             /**
26977             * @event paste
26978             * Fires when press user pastes into the editor
26979             * @param {Roo.HtmlEditorCore} this
26980             */
26981             paste: true 
26982         });
26983         this.defaultAutoCreate =  {
26984             tag: "textarea",
26985             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26986             autocomplete: "new-password"
26987         };
26988     },
26989
26990     /**
26991      * Protected method that will not generally be called directly. It
26992      * is called when the editor creates its toolbar. Override this method if you need to
26993      * add custom toolbar buttons.
26994      * @param {HtmlEditor} editor
26995      */
26996     createToolbar : function(editor){
26997         Roo.log("create toolbars");
26998         if (!editor.toolbars || !editor.toolbars.length) {
26999             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
27000         }
27001         
27002         for (var i =0 ; i < editor.toolbars.length;i++) {
27003             editor.toolbars[i] = Roo.factory(
27004                     typeof(editor.toolbars[i]) == 'string' ?
27005                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
27006                 Roo.form.HtmlEditor);
27007             editor.toolbars[i].init(editor);
27008         }
27009          
27010         
27011     },
27012     /**
27013      * get the Context selected node
27014      * @returns {DomElement|boolean} selected node if active or false if none
27015      * 
27016      */
27017     getSelectedNode : function()
27018     {
27019         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
27020             return false;
27021         }
27022         return this.toolbars[1].tb.selectedNode;
27023     
27024     },
27025     // private
27026     onRender : function(ct, position)
27027     {
27028         var _t = this;
27029         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27030         
27031         this.wrap = this.el.wrap({
27032             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27033         });
27034         
27035         this.editorcore.onRender(ct, position);
27036          
27037         if (this.resizable) {
27038             this.resizeEl = new Roo.Resizable(this.wrap, {
27039                 pinned : true,
27040                 wrap: true,
27041                 dynamic : true,
27042                 minHeight : this.height,
27043                 height: this.height,
27044                 handles : this.resizable,
27045                 width: this.width,
27046                 listeners : {
27047                     resize : function(r, w, h) {
27048                         _t.onResize(w,h); // -something
27049                     }
27050                 }
27051             });
27052             
27053         }
27054         this.createToolbar(this);
27055        
27056         
27057         if(!this.width){
27058             this.setSize(this.wrap.getSize());
27059         }
27060         if (this.resizeEl) {
27061             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27062             // should trigger onReize..
27063         }
27064         
27065         this.keyNav = new Roo.KeyNav(this.el, {
27066             
27067             "tab" : function(e){
27068                 e.preventDefault();
27069                 
27070                 var value = this.getValue();
27071                 
27072                 var start = this.el.dom.selectionStart;
27073                 var end = this.el.dom.selectionEnd;
27074                 
27075                 if(!e.shiftKey){
27076                     
27077                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
27078                     this.el.dom.setSelectionRange(end + 1, end + 1);
27079                     return;
27080                 }
27081                 
27082                 var f = value.substring(0, start).split("\t");
27083                 
27084                 if(f.pop().length != 0){
27085                     return;
27086                 }
27087                 
27088                 this.setValue(f.join("\t") + value.substring(end));
27089                 this.el.dom.setSelectionRange(start - 1, start - 1);
27090                 
27091             },
27092             
27093             "home" : function(e){
27094                 e.preventDefault();
27095                 
27096                 var curr = this.el.dom.selectionStart;
27097                 var lines = this.getValue().split("\n");
27098                 
27099                 if(!lines.length){
27100                     return;
27101                 }
27102                 
27103                 if(e.ctrlKey){
27104                     this.el.dom.setSelectionRange(0, 0);
27105                     return;
27106                 }
27107                 
27108                 var pos = 0;
27109                 
27110                 for (var i = 0; i < lines.length;i++) {
27111                     pos += lines[i].length;
27112                     
27113                     if(i != 0){
27114                         pos += 1;
27115                     }
27116                     
27117                     if(pos < curr){
27118                         continue;
27119                     }
27120                     
27121                     pos -= lines[i].length;
27122                     
27123                     break;
27124                 }
27125                 
27126                 if(!e.shiftKey){
27127                     this.el.dom.setSelectionRange(pos, pos);
27128                     return;
27129                 }
27130                 
27131                 this.el.dom.selectionStart = pos;
27132                 this.el.dom.selectionEnd = curr;
27133             },
27134             
27135             "end" : function(e){
27136                 e.preventDefault();
27137                 
27138                 var curr = this.el.dom.selectionStart;
27139                 var lines = this.getValue().split("\n");
27140                 
27141                 if(!lines.length){
27142                     return;
27143                 }
27144                 
27145                 if(e.ctrlKey){
27146                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
27147                     return;
27148                 }
27149                 
27150                 var pos = 0;
27151                 
27152                 for (var i = 0; i < lines.length;i++) {
27153                     
27154                     pos += lines[i].length;
27155                     
27156                     if(i != 0){
27157                         pos += 1;
27158                     }
27159                     
27160                     if(pos < curr){
27161                         continue;
27162                     }
27163                     
27164                     break;
27165                 }
27166                 
27167                 if(!e.shiftKey){
27168                     this.el.dom.setSelectionRange(pos, pos);
27169                     return;
27170                 }
27171                 
27172                 this.el.dom.selectionStart = curr;
27173                 this.el.dom.selectionEnd = pos;
27174             },
27175
27176             scope : this,
27177
27178             doRelay : function(foo, bar, hname){
27179                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
27180             },
27181
27182             forceKeyDown: true
27183         });
27184         
27185 //        if(this.autosave && this.w){
27186 //            this.autoSaveFn = setInterval(this.autosave, 1000);
27187 //        }
27188     },
27189
27190     // private
27191     onResize : function(w, h)
27192     {
27193         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27194         var ew = false;
27195         var eh = false;
27196         
27197         if(this.el ){
27198             if(typeof w == 'number'){
27199                 var aw = w - this.wrap.getFrameWidth('lr');
27200                 this.el.setWidth(this.adjustWidth('textarea', aw));
27201                 ew = aw;
27202             }
27203             if(typeof h == 'number'){
27204                 var tbh = 0;
27205                 for (var i =0; i < this.toolbars.length;i++) {
27206                     // fixme - ask toolbars for heights?
27207                     tbh += this.toolbars[i].tb.el.getHeight();
27208                     if (this.toolbars[i].footer) {
27209                         tbh += this.toolbars[i].footer.el.getHeight();
27210                     }
27211                 }
27212                 
27213                 
27214                 
27215                 
27216                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27217                 ah -= 5; // knock a few pixes off for look..
27218 //                Roo.log(ah);
27219                 this.el.setHeight(this.adjustWidth('textarea', ah));
27220                 var eh = ah;
27221             }
27222         }
27223         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27224         this.editorcore.onResize(ew,eh);
27225         
27226     },
27227
27228     /**
27229      * Toggles the editor between standard and source edit mode.
27230      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27231      */
27232     toggleSourceEdit : function(sourceEditMode)
27233     {
27234         this.editorcore.toggleSourceEdit(sourceEditMode);
27235         
27236         if(this.editorcore.sourceEditMode){
27237             Roo.log('editor - showing textarea');
27238             
27239 //            Roo.log('in');
27240 //            Roo.log(this.syncValue());
27241             this.editorcore.syncValue();
27242             this.el.removeClass('x-hidden');
27243             this.el.dom.removeAttribute('tabIndex');
27244             this.el.focus();
27245             this.el.dom.scrollTop = 0;
27246             
27247             
27248             for (var i = 0; i < this.toolbars.length; i++) {
27249                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27250                     this.toolbars[i].tb.hide();
27251                     this.toolbars[i].footer.hide();
27252                 }
27253             }
27254             
27255         }else{
27256             Roo.log('editor - hiding textarea');
27257 //            Roo.log('out')
27258 //            Roo.log(this.pushValue()); 
27259             this.editorcore.pushValue();
27260             
27261             this.el.addClass('x-hidden');
27262             this.el.dom.setAttribute('tabIndex', -1);
27263             
27264             for (var i = 0; i < this.toolbars.length; i++) {
27265                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27266                     this.toolbars[i].tb.show();
27267                     this.toolbars[i].footer.show();
27268                 }
27269             }
27270             
27271             //this.deferFocus();
27272         }
27273         
27274         this.setSize(this.wrap.getSize());
27275         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27276         
27277         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27278     },
27279  
27280     // private (for BoxComponent)
27281     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27282
27283     // private (for BoxComponent)
27284     getResizeEl : function(){
27285         return this.wrap;
27286     },
27287
27288     // private (for BoxComponent)
27289     getPositionEl : function(){
27290         return this.wrap;
27291     },
27292
27293     // private
27294     initEvents : function(){
27295         this.originalValue = this.getValue();
27296     },
27297
27298     /**
27299      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27300      * @method
27301      */
27302     markInvalid : Roo.emptyFn,
27303     /**
27304      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27305      * @method
27306      */
27307     clearInvalid : Roo.emptyFn,
27308
27309     setValue : function(v){
27310         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27311         this.editorcore.pushValue();
27312     },
27313
27314     /**
27315      * update the language in the body - really done by core
27316      * @param {String} language - eg. en / ar / zh-CN etc..
27317      */
27318     updateLanguage : function(lang)
27319     {
27320         this.language = lang;
27321         this.editorcore.language = lang;
27322         this.editorcore.updateLanguage();
27323      
27324     },
27325     // private
27326     deferFocus : function(){
27327         this.focus.defer(10, this);
27328     },
27329
27330     // doc'ed in Field
27331     focus : function(){
27332         this.editorcore.focus();
27333         
27334     },
27335       
27336
27337     // private
27338     onDestroy : function(){
27339         
27340         
27341         
27342         if(this.rendered){
27343             
27344             for (var i =0; i < this.toolbars.length;i++) {
27345                 // fixme - ask toolbars for heights?
27346                 this.toolbars[i].onDestroy();
27347             }
27348             
27349             this.wrap.dom.innerHTML = '';
27350             this.wrap.remove();
27351         }
27352     },
27353
27354     // private
27355     onFirstFocus : function(){
27356         //Roo.log("onFirstFocus");
27357         this.editorcore.onFirstFocus();
27358          for (var i =0; i < this.toolbars.length;i++) {
27359             this.toolbars[i].onFirstFocus();
27360         }
27361         
27362     },
27363     
27364     // private
27365     syncValue : function()
27366     {
27367         this.editorcore.syncValue();
27368     },
27369     
27370     pushValue : function()
27371     {
27372         this.editorcore.pushValue();
27373     },
27374     
27375     setStylesheets : function(stylesheets)
27376     {
27377         this.editorcore.setStylesheets(stylesheets);
27378     },
27379     
27380     removeStylesheets : function()
27381     {
27382         this.editorcore.removeStylesheets();
27383     }
27384      
27385     
27386     // hide stuff that is not compatible
27387     /**
27388      * @event blur
27389      * @hide
27390      */
27391     /**
27392      * @event change
27393      * @hide
27394      */
27395     /**
27396      * @event focus
27397      * @hide
27398      */
27399     /**
27400      * @event specialkey
27401      * @hide
27402      */
27403     /**
27404      * @cfg {String} fieldClass @hide
27405      */
27406     /**
27407      * @cfg {String} focusClass @hide
27408      */
27409     /**
27410      * @cfg {String} autoCreate @hide
27411      */
27412     /**
27413      * @cfg {String} inputType @hide
27414      */
27415     /**
27416      * @cfg {String} invalidClass @hide
27417      */
27418     /**
27419      * @cfg {String} invalidText @hide
27420      */
27421     /**
27422      * @cfg {String} msgFx @hide
27423      */
27424     /**
27425      * @cfg {String} validateOnBlur @hide
27426      */
27427 });
27428  
27429     /*
27430  * Based on
27431  * Ext JS Library 1.1.1
27432  * Copyright(c) 2006-2007, Ext JS, LLC.
27433  *  
27434  
27435  */
27436
27437 /**
27438  * @class Roo.form.HtmlEditor.ToolbarStandard
27439  * Basic Toolbar
27440
27441  * Usage:
27442  *
27443  new Roo.form.HtmlEditor({
27444     ....
27445     toolbars : [
27446         new Roo.form.HtmlEditorToolbar1({
27447             disable : { fonts: 1 , format: 1, ..., ... , ...],
27448             btns : [ .... ]
27449         })
27450     }
27451      
27452  * 
27453  * @cfg {Object} disable List of elements to disable..
27454  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27455  * 
27456  * 
27457  * NEEDS Extra CSS? 
27458  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27459  */
27460  
27461 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27462 {
27463     
27464     Roo.apply(this, config);
27465     
27466     // default disabled, based on 'good practice'..
27467     this.disable = this.disable || {};
27468     Roo.applyIf(this.disable, {
27469         fontSize : true,
27470         colors : true,
27471         specialElements : true
27472     });
27473     
27474     
27475     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27476     // dont call parent... till later.
27477 }
27478
27479 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27480     
27481     tb: false,
27482     
27483     rendered: false,
27484     
27485     editor : false,
27486     editorcore : false,
27487     /**
27488      * @cfg {Object} disable  List of toolbar elements to disable
27489          
27490      */
27491     disable : false,
27492     
27493     
27494      /**
27495      * @cfg {String} createLinkText The default text for the create link prompt
27496      */
27497     createLinkText : 'Please enter the URL for the link:',
27498     /**
27499      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27500      */
27501     defaultLinkValue : 'http:/'+'/',
27502    
27503     
27504       /**
27505      * @cfg {Array} fontFamilies An array of available font families
27506      */
27507     fontFamilies : [
27508         'Arial',
27509         'Courier New',
27510         'Tahoma',
27511         'Times New Roman',
27512         'Verdana'
27513     ],
27514     
27515     specialChars : [
27516            "&#169;",
27517           "&#174;",     
27518           "&#8482;",    
27519           "&#163;" ,    
27520          // "&#8212;",    
27521           "&#8230;",    
27522           "&#247;" ,    
27523         //  "&#225;" ,     ?? a acute?
27524            "&#8364;"    , //Euro
27525        //   "&#8220;"    ,
27526         //  "&#8221;"    ,
27527         //  "&#8226;"    ,
27528           "&#176;"  //   , // degrees
27529
27530          // "&#233;"     , // e ecute
27531          // "&#250;"     , // u ecute?
27532     ],
27533     
27534     specialElements : [
27535         {
27536             text: "Insert Table",
27537             xtype: 'MenuItem',
27538             xns : Roo.Menu,
27539             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27540                 
27541         },
27542         {    
27543             text: "Insert Image",
27544             xtype: 'MenuItem',
27545             xns : Roo.Menu,
27546             ihtml : '<img src="about:blank"/>'
27547             
27548         }
27549         
27550          
27551     ],
27552     
27553     
27554     inputElements : [ 
27555             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27556             "input:submit", "input:button", "select", "textarea", "label" ],
27557     formats : [
27558         ["p"] ,  
27559         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27560         ["pre"],[ "code"], 
27561         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27562         ['div'],['span'],
27563         ['sup'],['sub']
27564     ],
27565     
27566     cleanStyles : [
27567         "font-size"
27568     ],
27569      /**
27570      * @cfg {String} defaultFont default font to use.
27571      */
27572     defaultFont: 'tahoma',
27573    
27574     fontSelect : false,
27575     
27576     
27577     formatCombo : false,
27578     
27579     init : function(editor)
27580     {
27581         this.editor = editor;
27582         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27583         var editorcore = this.editorcore;
27584         
27585         var _t = this;
27586         
27587         var fid = editorcore.frameId;
27588         var etb = this;
27589         function btn(id, toggle, handler){
27590             var xid = fid + '-'+ id ;
27591             return {
27592                 id : xid,
27593                 cmd : id,
27594                 cls : 'x-btn-icon x-edit-'+id,
27595                 enableToggle:toggle !== false,
27596                 scope: _t, // was editor...
27597                 handler:handler||_t.relayBtnCmd,
27598                 clickEvent:'mousedown',
27599                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27600                 tabIndex:-1
27601             };
27602         }
27603         
27604         
27605         
27606         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27607         this.tb = tb;
27608          // stop form submits
27609         tb.el.on('click', function(e){
27610             e.preventDefault(); // what does this do?
27611         });
27612
27613         if(!this.disable.font) { // && !Roo.isSafari){
27614             /* why no safari for fonts 
27615             editor.fontSelect = tb.el.createChild({
27616                 tag:'select',
27617                 tabIndex: -1,
27618                 cls:'x-font-select',
27619                 html: this.createFontOptions()
27620             });
27621             
27622             editor.fontSelect.on('change', function(){
27623                 var font = editor.fontSelect.dom.value;
27624                 editor.relayCmd('fontname', font);
27625                 editor.deferFocus();
27626             }, editor);
27627             
27628             tb.add(
27629                 editor.fontSelect.dom,
27630                 '-'
27631             );
27632             */
27633             
27634         };
27635         if(!this.disable.formats){
27636             this.formatCombo = new Roo.form.ComboBox({
27637                 store: new Roo.data.SimpleStore({
27638                     id : 'tag',
27639                     fields: ['tag'],
27640                     data : this.formats // from states.js
27641                 }),
27642                 blockFocus : true,
27643                 name : '',
27644                 //autoCreate : {tag: "div",  size: "20"},
27645                 displayField:'tag',
27646                 typeAhead: false,
27647                 mode: 'local',
27648                 editable : false,
27649                 triggerAction: 'all',
27650                 emptyText:'Add tag',
27651                 selectOnFocus:true,
27652                 width:135,
27653                 listeners : {
27654                     'select': function(c, r, i) {
27655                         editorcore.insertTag(r.get('tag'));
27656                         editor.focus();
27657                     }
27658                 }
27659
27660             });
27661             tb.addField(this.formatCombo);
27662             
27663         }
27664         
27665         if(!this.disable.format){
27666             tb.add(
27667                 btn('bold'),
27668                 btn('italic'),
27669                 btn('underline'),
27670                 btn('strikethrough')
27671             );
27672         };
27673         if(!this.disable.fontSize){
27674             tb.add(
27675                 '-',
27676                 
27677                 
27678                 btn('increasefontsize', false, editorcore.adjustFont),
27679                 btn('decreasefontsize', false, editorcore.adjustFont)
27680             );
27681         };
27682         
27683         
27684         if(!this.disable.colors){
27685             tb.add(
27686                 '-', {
27687                     id:editorcore.frameId +'-forecolor',
27688                     cls:'x-btn-icon x-edit-forecolor',
27689                     clickEvent:'mousedown',
27690                     tooltip: this.buttonTips['forecolor'] || undefined,
27691                     tabIndex:-1,
27692                     menu : new Roo.menu.ColorMenu({
27693                         allowReselect: true,
27694                         focus: Roo.emptyFn,
27695                         value:'000000',
27696                         plain:true,
27697                         selectHandler: function(cp, color){
27698                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27699                             editor.deferFocus();
27700                         },
27701                         scope: editorcore,
27702                         clickEvent:'mousedown'
27703                     })
27704                 }, {
27705                     id:editorcore.frameId +'backcolor',
27706                     cls:'x-btn-icon x-edit-backcolor',
27707                     clickEvent:'mousedown',
27708                     tooltip: this.buttonTips['backcolor'] || undefined,
27709                     tabIndex:-1,
27710                     menu : new Roo.menu.ColorMenu({
27711                         focus: Roo.emptyFn,
27712                         value:'FFFFFF',
27713                         plain:true,
27714                         allowReselect: true,
27715                         selectHandler: function(cp, color){
27716                             if(Roo.isGecko){
27717                                 editorcore.execCmd('useCSS', false);
27718                                 editorcore.execCmd('hilitecolor', color);
27719                                 editorcore.execCmd('useCSS', true);
27720                                 editor.deferFocus();
27721                             }else{
27722                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27723                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27724                                 editor.deferFocus();
27725                             }
27726                         },
27727                         scope:editorcore,
27728                         clickEvent:'mousedown'
27729                     })
27730                 }
27731             );
27732         };
27733         // now add all the items...
27734         
27735
27736         if(!this.disable.alignments){
27737             tb.add(
27738                 '-',
27739                 btn('justifyleft'),
27740                 btn('justifycenter'),
27741                 btn('justifyright')
27742             );
27743         };
27744
27745         //if(!Roo.isSafari){
27746             if(!this.disable.links){
27747                 tb.add(
27748                     '-',
27749                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27750                 );
27751             };
27752
27753             if(!this.disable.lists){
27754                 tb.add(
27755                     '-',
27756                     btn('insertorderedlist'),
27757                     btn('insertunorderedlist')
27758                 );
27759             }
27760             if(!this.disable.sourceEdit){
27761                 tb.add(
27762                     '-',
27763                     btn('sourceedit', true, function(btn){
27764                         this.toggleSourceEdit(btn.pressed);
27765                     })
27766                 );
27767             }
27768         //}
27769         
27770         var smenu = { };
27771         // special menu.. - needs to be tidied up..
27772         if (!this.disable.special) {
27773             smenu = {
27774                 text: "&#169;",
27775                 cls: 'x-edit-none',
27776                 
27777                 menu : {
27778                     items : []
27779                 }
27780             };
27781             for (var i =0; i < this.specialChars.length; i++) {
27782                 smenu.menu.items.push({
27783                     
27784                     html: this.specialChars[i],
27785                     handler: function(a,b) {
27786                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27787                         //editor.insertAtCursor(a.html);
27788                         
27789                     },
27790                     tabIndex:-1
27791                 });
27792             }
27793             
27794             
27795             tb.add(smenu);
27796             
27797             
27798         }
27799         
27800         var cmenu = { };
27801         if (!this.disable.cleanStyles) {
27802             cmenu = {
27803                 cls: 'x-btn-icon x-btn-clear',
27804                 
27805                 menu : {
27806                     items : []
27807                 }
27808             };
27809             for (var i =0; i < this.cleanStyles.length; i++) {
27810                 cmenu.menu.items.push({
27811                     actiontype : this.cleanStyles[i],
27812                     html: 'Remove ' + this.cleanStyles[i],
27813                     handler: function(a,b) {
27814 //                        Roo.log(a);
27815 //                        Roo.log(b);
27816                         var c = Roo.get(editorcore.doc.body);
27817                         c.select('[style]').each(function(s) {
27818                             s.dom.style.removeProperty(a.actiontype);
27819                         });
27820                         editorcore.syncValue();
27821                     },
27822                     tabIndex:-1
27823                 });
27824             }
27825             cmenu.menu.items.push({
27826                 actiontype : 'tablewidths',
27827                 html: 'Remove Table Widths',
27828                 handler: function(a,b) {
27829                     editorcore.cleanTableWidths();
27830                     editorcore.syncValue();
27831                 },
27832                 tabIndex:-1
27833             });
27834             cmenu.menu.items.push({
27835                 actiontype : 'word',
27836                 html: 'Remove MS Word Formating',
27837                 handler: function(a,b) {
27838                     editorcore.cleanWord();
27839                     editorcore.syncValue();
27840                 },
27841                 tabIndex:-1
27842             });
27843             
27844             cmenu.menu.items.push({
27845                 actiontype : 'all',
27846                 html: 'Remove All Styles',
27847                 handler: function(a,b) {
27848                     
27849                     var c = Roo.get(editorcore.doc.body);
27850                     c.select('[style]').each(function(s) {
27851                         s.dom.removeAttribute('style');
27852                     });
27853                     editorcore.syncValue();
27854                 },
27855                 tabIndex:-1
27856             });
27857             
27858             cmenu.menu.items.push({
27859                 actiontype : 'all',
27860                 html: 'Remove All CSS Classes',
27861                 handler: function(a,b) {
27862                     
27863                     var c = Roo.get(editorcore.doc.body);
27864                     c.select('[class]').each(function(s) {
27865                         s.dom.removeAttribute('class');
27866                     });
27867                     editorcore.cleanWord();
27868                     editorcore.syncValue();
27869                 },
27870                 tabIndex:-1
27871             });
27872             
27873              cmenu.menu.items.push({
27874                 actiontype : 'tidy',
27875                 html: 'Tidy HTML Source',
27876                 handler: function(a,b) {
27877                     new Roo.htmleditor.Tidy(editorcore.doc.body);
27878                     editorcore.syncValue();
27879                 },
27880                 tabIndex:-1
27881             });
27882             
27883             
27884             tb.add(cmenu);
27885         }
27886          
27887         if (!this.disable.specialElements) {
27888             var semenu = {
27889                 text: "Other;",
27890                 cls: 'x-edit-none',
27891                 menu : {
27892                     items : []
27893                 }
27894             };
27895             for (var i =0; i < this.specialElements.length; i++) {
27896                 semenu.menu.items.push(
27897                     Roo.apply({ 
27898                         handler: function(a,b) {
27899                             editor.insertAtCursor(this.ihtml);
27900                         }
27901                     }, this.specialElements[i])
27902                 );
27903                     
27904             }
27905             
27906             tb.add(semenu);
27907             
27908             
27909         }
27910          
27911         
27912         if (this.btns) {
27913             for(var i =0; i< this.btns.length;i++) {
27914                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27915                 b.cls =  'x-edit-none';
27916                 
27917                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27918                     b.cls += ' x-init-enable';
27919                 }
27920                 
27921                 b.scope = editorcore;
27922                 tb.add(b);
27923             }
27924         
27925         }
27926         
27927         
27928         
27929         // disable everything...
27930         
27931         this.tb.items.each(function(item){
27932             
27933            if(
27934                 item.id != editorcore.frameId+ '-sourceedit' && 
27935                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27936             ){
27937                 
27938                 item.disable();
27939             }
27940         });
27941         this.rendered = true;
27942         
27943         // the all the btns;
27944         editor.on('editorevent', this.updateToolbar, this);
27945         // other toolbars need to implement this..
27946         //editor.on('editmodechange', this.updateToolbar, this);
27947     },
27948     
27949     
27950     relayBtnCmd : function(btn) {
27951         this.editorcore.relayCmd(btn.cmd);
27952     },
27953     // private used internally
27954     createLink : function(){
27955         //Roo.log("create link?");
27956         var ec = this.editorcore;
27957         var ar = ec.getAllAncestors();
27958         var n = false;
27959         for(var i = 0;i< ar.length;i++) {
27960             if (ar[i] && ar[i].nodeName == 'A') {
27961                 n = ar[i];
27962                 break;
27963             }
27964         }
27965         
27966         (function() {
27967             
27968             Roo.MessageBox.show({
27969                 title : "Add / Edit Link URL",
27970                 msg : "Enter the url for the link",
27971                 buttons: Roo.MessageBox.OKCANCEL,
27972                 fn: function(btn, url){
27973                     if (btn != 'ok') {
27974                         return;
27975                     }
27976                     if(url && url != 'http:/'+'/'){
27977                         if (n) {
27978                             n.setAttribute('href', url);
27979                         } else {
27980                             ec.relayCmd('createlink', url);
27981                         }
27982                     }
27983                 },
27984                 minWidth:250,
27985                 prompt:true,
27986                 //multiline: multiline,
27987                 modal : true,
27988                 value :  n  ? n.getAttribute('href') : '' 
27989             });
27990             
27991              
27992         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27993         
27994     },
27995
27996     
27997     /**
27998      * Protected method that will not generally be called directly. It triggers
27999      * a toolbar update by reading the markup state of the current selection in the editor.
28000      */
28001     updateToolbar: function(){
28002
28003         if(!this.editorcore.activated){
28004             this.editor.onFirstFocus();
28005             return;
28006         }
28007
28008         var btns = this.tb.items.map, 
28009             doc = this.editorcore.doc,
28010             frameId = this.editorcore.frameId;
28011
28012         if(!this.disable.font && !Roo.isSafari){
28013             /*
28014             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
28015             if(name != this.fontSelect.dom.value){
28016                 this.fontSelect.dom.value = name;
28017             }
28018             */
28019         }
28020         if(!this.disable.format){
28021             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
28022             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
28023             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
28024             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
28025         }
28026         if(!this.disable.alignments){
28027             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
28028             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
28029             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
28030         }
28031         if(!Roo.isSafari && !this.disable.lists){
28032             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
28033             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
28034         }
28035         
28036         var ans = this.editorcore.getAllAncestors();
28037         if (this.formatCombo) {
28038             
28039             
28040             var store = this.formatCombo.store;
28041             this.formatCombo.setValue("");
28042             for (var i =0; i < ans.length;i++) {
28043                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28044                     // select it..
28045                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28046                     break;
28047                 }
28048             }
28049         }
28050         
28051         
28052         
28053         // hides menus... - so this cant be on a menu...
28054         Roo.menu.MenuMgr.hideAll();
28055
28056         //this.editorsyncValue();
28057     },
28058    
28059     
28060     createFontOptions : function(){
28061         var buf = [], fs = this.fontFamilies, ff, lc;
28062         
28063         
28064         
28065         for(var i = 0, len = fs.length; i< len; i++){
28066             ff = fs[i];
28067             lc = ff.toLowerCase();
28068             buf.push(
28069                 '<option value="',lc,'" style="font-family:',ff,';"',
28070                     (this.defaultFont == lc ? ' selected="true">' : '>'),
28071                     ff,
28072                 '</option>'
28073             );
28074         }
28075         return buf.join('');
28076     },
28077     
28078     toggleSourceEdit : function(sourceEditMode){
28079         
28080         Roo.log("toolbar toogle");
28081         if(sourceEditMode === undefined){
28082             sourceEditMode = !this.sourceEditMode;
28083         }
28084         this.sourceEditMode = sourceEditMode === true;
28085         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
28086         // just toggle the button?
28087         if(btn.pressed !== this.sourceEditMode){
28088             btn.toggle(this.sourceEditMode);
28089             return;
28090         }
28091         
28092         if(sourceEditMode){
28093             Roo.log("disabling buttons");
28094             this.tb.items.each(function(item){
28095                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
28096                     item.disable();
28097                 }
28098             });
28099           
28100         }else{
28101             Roo.log("enabling buttons");
28102             if(this.editorcore.initialized){
28103                 this.tb.items.each(function(item){
28104                     item.enable();
28105                 });
28106                 // initialize 'blocks'
28107                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
28108                     Roo.htmleditor.Block.factory(e).updateElement(e);
28109                 },this);
28110             
28111             }
28112             
28113         }
28114         Roo.log("calling toggole on editor");
28115         // tell the editor that it's been pressed..
28116         this.editor.toggleSourceEdit(sourceEditMode);
28117        
28118     },
28119      /**
28120      * Object collection of toolbar tooltips for the buttons in the editor. The key
28121      * is the command id associated with that button and the value is a valid QuickTips object.
28122      * For example:
28123 <pre><code>
28124 {
28125     bold : {
28126         title: 'Bold (Ctrl+B)',
28127         text: 'Make the selected text bold.',
28128         cls: 'x-html-editor-tip'
28129     },
28130     italic : {
28131         title: 'Italic (Ctrl+I)',
28132         text: 'Make the selected text italic.',
28133         cls: 'x-html-editor-tip'
28134     },
28135     ...
28136 </code></pre>
28137     * @type Object
28138      */
28139     buttonTips : {
28140         bold : {
28141             title: 'Bold (Ctrl+B)',
28142             text: 'Make the selected text bold.',
28143             cls: 'x-html-editor-tip'
28144         },
28145         italic : {
28146             title: 'Italic (Ctrl+I)',
28147             text: 'Make the selected text italic.',
28148             cls: 'x-html-editor-tip'
28149         },
28150         underline : {
28151             title: 'Underline (Ctrl+U)',
28152             text: 'Underline the selected text.',
28153             cls: 'x-html-editor-tip'
28154         },
28155         strikethrough : {
28156             title: 'Strikethrough',
28157             text: 'Strikethrough the selected text.',
28158             cls: 'x-html-editor-tip'
28159         },
28160         increasefontsize : {
28161             title: 'Grow Text',
28162             text: 'Increase the font size.',
28163             cls: 'x-html-editor-tip'
28164         },
28165         decreasefontsize : {
28166             title: 'Shrink Text',
28167             text: 'Decrease the font size.',
28168             cls: 'x-html-editor-tip'
28169         },
28170         backcolor : {
28171             title: 'Text Highlight Color',
28172             text: 'Change the background color of the selected text.',
28173             cls: 'x-html-editor-tip'
28174         },
28175         forecolor : {
28176             title: 'Font Color',
28177             text: 'Change the color of the selected text.',
28178             cls: 'x-html-editor-tip'
28179         },
28180         justifyleft : {
28181             title: 'Align Text Left',
28182             text: 'Align text to the left.',
28183             cls: 'x-html-editor-tip'
28184         },
28185         justifycenter : {
28186             title: 'Center Text',
28187             text: 'Center text in the editor.',
28188             cls: 'x-html-editor-tip'
28189         },
28190         justifyright : {
28191             title: 'Align Text Right',
28192             text: 'Align text to the right.',
28193             cls: 'x-html-editor-tip'
28194         },
28195         insertunorderedlist : {
28196             title: 'Bullet List',
28197             text: 'Start a bulleted list.',
28198             cls: 'x-html-editor-tip'
28199         },
28200         insertorderedlist : {
28201             title: 'Numbered List',
28202             text: 'Start a numbered list.',
28203             cls: 'x-html-editor-tip'
28204         },
28205         createlink : {
28206             title: 'Hyperlink',
28207             text: 'Make the selected text a hyperlink.',
28208             cls: 'x-html-editor-tip'
28209         },
28210         sourceedit : {
28211             title: 'Source Edit',
28212             text: 'Switch to source editing mode.',
28213             cls: 'x-html-editor-tip'
28214         }
28215     },
28216     // private
28217     onDestroy : function(){
28218         if(this.rendered){
28219             
28220             this.tb.items.each(function(item){
28221                 if(item.menu){
28222                     item.menu.removeAll();
28223                     if(item.menu.el){
28224                         item.menu.el.destroy();
28225                     }
28226                 }
28227                 item.destroy();
28228             });
28229              
28230         }
28231     },
28232     onFirstFocus: function() {
28233         this.tb.items.each(function(item){
28234            item.enable();
28235         });
28236     }
28237 };
28238
28239
28240
28241
28242 // <script type="text/javascript">
28243 /*
28244  * Based on
28245  * Ext JS Library 1.1.1
28246  * Copyright(c) 2006-2007, Ext JS, LLC.
28247  *  
28248  
28249  */
28250
28251  
28252 /**
28253  * @class Roo.form.HtmlEditor.ToolbarContext
28254  * Context Toolbar
28255  * 
28256  * Usage:
28257  *
28258  new Roo.form.HtmlEditor({
28259     ....
28260     toolbars : [
28261         { xtype: 'ToolbarStandard', styles : {} }
28262         { xtype: 'ToolbarContext', disable : {} }
28263     ]
28264 })
28265
28266      
28267  * 
28268  * @config : {Object} disable List of elements to disable.. (not done yet.)
28269  * @config : {Object} styles  Map of styles available.
28270  * 
28271  */
28272
28273 Roo.form.HtmlEditor.ToolbarContext = function(config)
28274 {
28275     
28276     Roo.apply(this, config);
28277     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28278     // dont call parent... till later.
28279     this.styles = this.styles || {};
28280 }
28281
28282  
28283
28284 Roo.form.HtmlEditor.ToolbarContext.types = {
28285     'IMG' : [
28286         {
28287             name : 'width',
28288             title: "Width",
28289             width: 40
28290         },
28291         {
28292             name : 'height',
28293             title: "Height",
28294             width: 40
28295         },
28296         {
28297             name : 'align',
28298             title: "Align",
28299             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28300             width : 80
28301             
28302         },
28303         {
28304             name : 'border',
28305             title: "Border",
28306             width: 40
28307         },
28308         {
28309             name : 'alt',
28310             title: "Alt",
28311             width: 120
28312         },
28313         {
28314             name : 'src',
28315             title: "Src",
28316             width: 220
28317         }
28318         
28319     ],
28320     
28321     'FIGURE' : [
28322         {
28323             name : 'align',
28324             title: "Align",
28325             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28326             width : 80  
28327         }
28328     ],
28329     'A' : [
28330         {
28331             name : 'name',
28332             title: "Name",
28333             width: 50
28334         },
28335         {
28336             name : 'target',
28337             title: "Target",
28338             width: 120
28339         },
28340         {
28341             name : 'href',
28342             title: "Href",
28343             width: 220
28344         } // border?
28345         
28346     ],
28347     
28348     'INPUT' : [
28349         {
28350             name : 'name',
28351             title: "name",
28352             width: 120
28353         },
28354         {
28355             name : 'value',
28356             title: "Value",
28357             width: 120
28358         },
28359         {
28360             name : 'width',
28361             title: "Width",
28362             width: 40
28363         }
28364     ],
28365     'LABEL' : [
28366          {
28367             name : 'for',
28368             title: "For",
28369             width: 120
28370         }
28371     ],
28372     'TEXTAREA' : [
28373         {
28374             name : 'name',
28375             title: "name",
28376             width: 120
28377         },
28378         {
28379             name : 'rows',
28380             title: "Rows",
28381             width: 20
28382         },
28383         {
28384             name : 'cols',
28385             title: "Cols",
28386             width: 20
28387         }
28388     ],
28389     'SELECT' : [
28390         {
28391             name : 'name',
28392             title: "name",
28393             width: 120
28394         },
28395         {
28396             name : 'selectoptions',
28397             title: "Options",
28398             width: 200
28399         }
28400     ],
28401     
28402     // should we really allow this??
28403     // should this just be 
28404     'BODY' : [
28405         
28406         {
28407             name : 'title',
28408             title: "Title",
28409             width: 200,
28410             disabled : true
28411         }
28412     ],
28413  
28414     '*' : [
28415         // empty.
28416     ]
28417
28418 };
28419
28420 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28421 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28422
28423 Roo.form.HtmlEditor.ToolbarContext.options = {
28424         'font-family'  : [ 
28425                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28426                 [ 'Courier New', 'Courier New'],
28427                 [ 'Tahoma', 'Tahoma'],
28428                 [ 'Times New Roman,serif', 'Times'],
28429                 [ 'Verdana','Verdana' ]
28430         ]
28431 };
28432
28433 // fixme - these need to be configurable..
28434  
28435
28436 //Roo.form.HtmlEditor.ToolbarContext.types
28437
28438
28439 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28440     
28441     tb: false,
28442     
28443     rendered: false,
28444     
28445     editor : false,
28446     editorcore : false,
28447     /**
28448      * @cfg {Object} disable  List of toolbar elements to disable
28449          
28450      */
28451     disable : false,
28452     /**
28453      * @cfg {Object} styles List of styles 
28454      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28455      *
28456      * These must be defined in the page, so they get rendered correctly..
28457      * .headline { }
28458      * TD.underline { }
28459      * 
28460      */
28461     styles : false,
28462     
28463     options: false,
28464     
28465     toolbars : false,
28466     
28467     init : function(editor)
28468     {
28469         this.editor = editor;
28470         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28471         var editorcore = this.editorcore;
28472         
28473         var fid = editorcore.frameId;
28474         var etb = this;
28475         function btn(id, toggle, handler){
28476             var xid = fid + '-'+ id ;
28477             return {
28478                 id : xid,
28479                 cmd : id,
28480                 cls : 'x-btn-icon x-edit-'+id,
28481                 enableToggle:toggle !== false,
28482                 scope: editorcore, // was editor...
28483                 handler:handler||editorcore.relayBtnCmd,
28484                 clickEvent:'mousedown',
28485                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28486                 tabIndex:-1
28487             };
28488         }
28489         // create a new element.
28490         var wdiv = editor.wrap.createChild({
28491                 tag: 'div'
28492             }, editor.wrap.dom.firstChild.nextSibling, true);
28493         
28494         // can we do this more than once??
28495         
28496          // stop form submits
28497       
28498  
28499         // disable everything...
28500         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28501         this.toolbars = {};
28502         // block toolbars are built in updateToolbar when needed.
28503         for (var i in  ty) {
28504             
28505             this.toolbars[i] = this.buildToolbar(ty[i],i);
28506         }
28507         this.tb = this.toolbars.BODY;
28508         this.tb.el.show();
28509         this.buildFooter();
28510         this.footer.show();
28511         editor.on('hide', function( ) { this.footer.hide() }, this);
28512         editor.on('show', function( ) { this.footer.show() }, this);
28513         
28514          
28515         this.rendered = true;
28516         
28517         // the all the btns;
28518         editor.on('editorevent', this.updateToolbar, this);
28519         // other toolbars need to implement this..
28520         //editor.on('editmodechange', this.updateToolbar, this);
28521     },
28522     
28523     
28524     
28525     /**
28526      * Protected method that will not generally be called directly. It triggers
28527      * a toolbar update by reading the markup state of the current selection in the editor.
28528      *
28529      * Note you can force an update by calling on('editorevent', scope, false)
28530      */
28531     updateToolbar: function(editor ,ev, sel)
28532     {
28533         
28534         if (ev) {
28535             ev.stopEvent(); // se if we can stop this looping with mutiple events.
28536         }
28537         
28538         //Roo.log(ev);
28539         // capture mouse up - this is handy for selecting images..
28540         // perhaps should go somewhere else...
28541         if(!this.editorcore.activated){
28542              this.editor.onFirstFocus();
28543             return;
28544         }
28545         //Roo.log(ev ? ev.target : 'NOTARGET');
28546         
28547         
28548         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28549         // selectNode - might want to handle IE?
28550         
28551         
28552         
28553         if (ev &&
28554             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28555             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28556             // they have click on an image...
28557             // let's see if we can change the selection...
28558             sel = ev.target;
28559             
28560             // this triggers looping?
28561             //this.editorcore.selectNode(sel);
28562              
28563         }
28564         
28565         // this forces an id..
28566         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28567              e.classList.remove('roo-ed-selection');
28568         });
28569         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28570         //Roo.get(node).addClass('roo-ed-selection');
28571       
28572         //var updateFooter = sel ? false : true; 
28573         
28574         
28575         var ans = this.editorcore.getAllAncestors();
28576         
28577         // pick
28578         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28579         
28580         if (!sel) { 
28581             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28582             sel = sel ? sel : this.editorcore.doc.body;
28583             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28584             
28585         }
28586         
28587         var tn = sel.tagName.toUpperCase();
28588         var lastSel = this.tb.selectedNode;
28589         this.tb.selectedNode = sel;
28590         var left_label = tn;
28591         
28592         // ok see if we are editing a block?
28593         
28594         var db = false;
28595         // you are not actually selecting the block.
28596         if (sel && sel.hasAttribute('data-block')) {
28597             db = sel;
28598         } else if (sel && sel.closest('[data-block]')) {
28599             
28600             db = sel.closest('[data-block]');
28601             //var cepar = sel.closest('[contenteditable=true]');
28602             //if (db && cepar && cepar.tagName != 'BODY') {
28603             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28604             //}   
28605         }
28606         
28607         
28608         var block = false;
28609         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28610         if (db && this.editorcore.enableBlocks) {
28611             block = Roo.htmleditor.Block.factory(db);
28612             
28613             
28614             if (block) {
28615                  db.className = (
28616                         db.classList.length > 0  ? db.className + ' ' : ''
28617                     )  + 'roo-ed-selection';
28618                  
28619                  // since we removed it earlier... its not there..
28620                 tn = 'BLOCK.' + db.getAttribute('data-block');
28621                 
28622                 //this.editorcore.selectNode(db);
28623                 if (typeof(this.toolbars[tn]) == 'undefined') {
28624                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
28625                 }
28626                 this.toolbars[tn].selectedNode = db;
28627                 left_label = block.friendly_name;
28628                 ans = this.editorcore.getAllAncestors();
28629             }
28630             
28631                 
28632             
28633         }
28634         
28635         
28636         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28637             return; // no change?
28638         }
28639         
28640         
28641           
28642         this.tb.el.hide();
28643         ///console.log("show: " + tn);
28644         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28645         
28646         this.tb.el.show();
28647         // update name
28648         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
28649         
28650         
28651         // update attributes
28652         if (block && this.tb.fields) {
28653              
28654             this.tb.fields.each(function(e) {
28655                 e.setValue(block[e.name]);
28656             });
28657             
28658             
28659         } else  if (this.tb.fields && this.tb.selectedNode) {
28660             this.tb.fields.each( function(e) {
28661                 if (e.stylename) {
28662                     e.setValue(this.tb.selectedNode.style[e.stylename]);
28663                     return;
28664                 } 
28665                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28666             }, this);
28667             this.updateToolbarStyles(this.tb.selectedNode);  
28668         }
28669         
28670         
28671        
28672         Roo.menu.MenuMgr.hideAll();
28673
28674         
28675         
28676     
28677         // update the footer
28678         //
28679         this.updateFooter(ans);
28680              
28681     },
28682     
28683     updateToolbarStyles : function(sel)
28684     {
28685         var hasStyles = false;
28686         for(var i in this.styles) {
28687             hasStyles = true;
28688             break;
28689         }
28690         
28691         // update styles
28692         if (hasStyles && this.tb.hasStyles) { 
28693             var st = this.tb.fields.item(0);
28694             
28695             st.store.removeAll();
28696             var cn = sel.className.split(/\s+/);
28697             
28698             var avs = [];
28699             if (this.styles['*']) {
28700                 
28701                 Roo.each(this.styles['*'], function(v) {
28702                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28703                 });
28704             }
28705             if (this.styles[tn]) { 
28706                 Roo.each(this.styles[tn], function(v) {
28707                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28708                 });
28709             }
28710             
28711             st.store.loadData(avs);
28712             st.collapse();
28713             st.setValue(cn);
28714         }
28715     },
28716     
28717      
28718     updateFooter : function(ans)
28719     {
28720         var html = '';
28721         if (ans === false) {
28722             this.footDisp.dom.innerHTML = '';
28723             return;
28724         }
28725         
28726         this.footerEls = ans.reverse();
28727         Roo.each(this.footerEls, function(a,i) {
28728             if (!a) { return; }
28729             html += html.length ? ' &gt; '  :  '';
28730             
28731             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28732             
28733         });
28734        
28735         // 
28736         var sz = this.footDisp.up('td').getSize();
28737         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28738         this.footDisp.dom.style.marginLeft = '5px';
28739         
28740         this.footDisp.dom.style.overflow = 'hidden';
28741         
28742         this.footDisp.dom.innerHTML = html;
28743             
28744         
28745     },
28746    
28747        
28748     // private
28749     onDestroy : function(){
28750         if(this.rendered){
28751             
28752             this.tb.items.each(function(item){
28753                 if(item.menu){
28754                     item.menu.removeAll();
28755                     if(item.menu.el){
28756                         item.menu.el.destroy();
28757                     }
28758                 }
28759                 item.destroy();
28760             });
28761              
28762         }
28763     },
28764     onFirstFocus: function() {
28765         // need to do this for all the toolbars..
28766         this.tb.items.each(function(item){
28767            item.enable();
28768         });
28769     },
28770     buildToolbar: function(tlist, nm, friendly_name, block)
28771     {
28772         var editor = this.editor;
28773         var editorcore = this.editorcore;
28774          // create a new element.
28775         var wdiv = editor.wrap.createChild({
28776                 tag: 'div'
28777             }, editor.wrap.dom.firstChild.nextSibling, true);
28778         
28779        
28780         var tb = new Roo.Toolbar(wdiv);
28781         ///this.tb = tb; // << this sets the active toolbar..
28782         if (tlist === false && block) {
28783             tlist = block.contextMenu(this);
28784         }
28785         
28786         tb.hasStyles = false;
28787         tb.name = nm;
28788         
28789         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
28790         
28791         var styles = Array.from(this.styles);
28792         
28793         
28794         // styles...
28795         if (styles && styles.length) {
28796             tb.hasStyles = true;
28797             // this needs a multi-select checkbox...
28798             tb.addField( new Roo.form.ComboBox({
28799                 store: new Roo.data.SimpleStore({
28800                     id : 'val',
28801                     fields: ['val', 'selected'],
28802                     data : [] 
28803                 }),
28804                 name : '-roo-edit-className',
28805                 attrname : 'className',
28806                 displayField: 'val',
28807                 typeAhead: false,
28808                 mode: 'local',
28809                 editable : false,
28810                 triggerAction: 'all',
28811                 emptyText:'Select Style',
28812                 selectOnFocus:true,
28813                 width: 130,
28814                 listeners : {
28815                     'select': function(c, r, i) {
28816                         // initial support only for on class per el..
28817                         tb.selectedNode.className =  r ? r.get('val') : '';
28818                         editorcore.syncValue();
28819                     }
28820                 }
28821     
28822             }));
28823         }
28824         
28825         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28826         
28827         
28828         for (var i = 0; i < tlist.length; i++) {
28829             
28830             // newer versions will use xtype cfg to create menus.
28831             if (typeof(tlist[i].xtype) != 'undefined') {
28832                 
28833                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28834                 
28835                 
28836                 continue;
28837             }
28838             
28839             var item = tlist[i];
28840             tb.add(item.title + ":&nbsp;");
28841             
28842             
28843             //optname == used so you can configure the options available..
28844             var opts = item.opts ? item.opts : false;
28845             if (item.optname) { // use the b
28846                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28847            
28848             }
28849             
28850             if (opts) {
28851                 // opts == pulldown..
28852                 tb.addField( new Roo.form.ComboBox({
28853                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28854                         id : 'val',
28855                         fields: ['val', 'display'],
28856                         data : opts  
28857                     }),
28858                     name : '-roo-edit-' + tlist[i].name,
28859                     
28860                     attrname : tlist[i].name,
28861                     stylename : item.style ? item.style : false,
28862                     
28863                     displayField: item.displayField ? item.displayField : 'val',
28864                     valueField :  'val',
28865                     typeAhead: false,
28866                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
28867                     editable : false,
28868                     triggerAction: 'all',
28869                     emptyText:'Select',
28870                     selectOnFocus:true,
28871                     width: item.width ? item.width  : 130,
28872                     listeners : {
28873                         'select': function(c, r, i) {
28874                              
28875                             
28876                             if (c.stylename) {
28877                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28878                                 editorcore.syncValue();
28879                                 return;
28880                             }
28881                             if (r === false) {
28882                                 tb.selectedNode.removeAttribute(c.attrname);
28883                                 editorcore.syncValue();
28884                                 return;
28885                             }
28886                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28887                             editorcore.syncValue();
28888                         }
28889                     }
28890
28891                 }));
28892                 continue;
28893                     
28894                  
28895                 /*
28896                 tb.addField( new Roo.form.TextField({
28897                     name: i,
28898                     width: 100,
28899                     //allowBlank:false,
28900                     value: ''
28901                 }));
28902                 continue;
28903                 */
28904             }
28905             tb.addField( new Roo.form.TextField({
28906                 name: '-roo-edit-' + tlist[i].name,
28907                 attrname : tlist[i].name,
28908                 
28909                 width: item.width,
28910                 //allowBlank:true,
28911                 value: '',
28912                 listeners: {
28913                     'change' : function(f, nv, ov) {
28914                         
28915                          
28916                         tb.selectedNode.setAttribute(f.attrname, nv);
28917                         editorcore.syncValue();
28918                     }
28919                 }
28920             }));
28921              
28922         }
28923         
28924         var _this = this;
28925         var show_delete = !block || block.deleteTitle !== false;
28926         if(nm == 'BODY'){
28927             show_delete = false;
28928             tb.addSeparator();
28929         
28930             tb.addButton( {
28931                 text: 'Stylesheets',
28932
28933                 listeners : {
28934                     click : function ()
28935                     {
28936                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28937                     }
28938                 }
28939             });
28940         }
28941         
28942         tb.addFill();
28943         if (show_delete) {
28944             tb.addButton({
28945                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28946         
28947                 listeners : {
28948                     click : function ()
28949                     {
28950                         var sn = tb.selectedNode;
28951                         if (block) {
28952                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28953                             
28954                         }
28955                         if (!sn) {
28956                             return;
28957                         }
28958                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28959                         if (sn.hasAttribute('data-block')) {
28960                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
28961                             sn.parentNode.removeChild(sn);
28962                             
28963                         } else if (sn && sn.tagName != 'BODY') {
28964                             // remove and keep parents.
28965                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28966                             a.replaceTag(sn);
28967                         }
28968                         
28969                         
28970                         var range = editorcore.createRange();
28971             
28972                         range.setStart(stn,0);
28973                         range.setEnd(stn,0); 
28974                         var selection = editorcore.getSelection();
28975                         selection.removeAllRanges();
28976                         selection.addRange(range);
28977                         
28978                         
28979                         //_this.updateToolbar(null, null, pn);
28980                         _this.updateToolbar(null, null, null);
28981                         _this.updateFooter(false);
28982                         
28983                     }
28984                 }
28985                 
28986                         
28987                     
28988                 
28989             });
28990         }    
28991         
28992         tb.el.on('click', function(e){
28993             e.preventDefault(); // what does this do?
28994         });
28995         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28996         tb.el.hide();
28997         
28998         // dont need to disable them... as they will get hidden
28999         return tb;
29000          
29001         
29002     },
29003     buildFooter : function()
29004     {
29005         
29006         var fel = this.editor.wrap.createChild();
29007         this.footer = new Roo.Toolbar(fel);
29008         // toolbar has scrolly on left / right?
29009         var footDisp= new Roo.Toolbar.Fill();
29010         var _t = this;
29011         this.footer.add(
29012             {
29013                 text : '&lt;',
29014                 xtype: 'Button',
29015                 handler : function() {
29016                     _t.footDisp.scrollTo('left',0,true)
29017                 }
29018             }
29019         );
29020         this.footer.add( footDisp );
29021         this.footer.add( 
29022             {
29023                 text : '&gt;',
29024                 xtype: 'Button',
29025                 handler : function() {
29026                     // no animation..
29027                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
29028                 }
29029             }
29030         );
29031         var fel = Roo.get(footDisp.el);
29032         fel.addClass('x-editor-context');
29033         this.footDispWrap = fel; 
29034         this.footDispWrap.overflow  = 'hidden';
29035         
29036         this.footDisp = fel.createChild();
29037         this.footDispWrap.on('click', this.onContextClick, this)
29038         
29039         
29040     },
29041     // when the footer contect changes
29042     onContextClick : function (ev,dom)
29043     {
29044         ev.preventDefault();
29045         var  cn = dom.className;
29046         //Roo.log(cn);
29047         if (!cn.match(/x-ed-loc-/)) {
29048             return;
29049         }
29050         var n = cn.split('-').pop();
29051         var ans = this.footerEls;
29052         var sel = ans[n];
29053         
29054         this.editorcore.selectNode(sel);
29055         
29056         
29057         this.updateToolbar(null, null, sel);
29058         
29059         
29060     }
29061     
29062     
29063     
29064     
29065     
29066 });
29067
29068
29069
29070
29071
29072 /*
29073  * Based on:
29074  * Ext JS Library 1.1.1
29075  * Copyright(c) 2006-2007, Ext JS, LLC.
29076  *
29077  * Originally Released Under LGPL - original licence link has changed is not relivant.
29078  *
29079  * Fork - LGPL
29080  * <script type="text/javascript">
29081  */
29082  
29083 /**
29084  * @class Roo.form.BasicForm
29085  * @extends Roo.util.Observable
29086  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
29087  * @constructor
29088  * @param {String/HTMLElement/Roo.Element} el The form element or its id
29089  * @param {Object} config Configuration options
29090  */
29091 Roo.form.BasicForm = function(el, config){
29092     this.allItems = [];
29093     this.childForms = [];
29094     Roo.apply(this, config);
29095     /*
29096      * The Roo.form.Field items in this form.
29097      * @type MixedCollection
29098      */
29099      
29100      
29101     this.items = new Roo.util.MixedCollection(false, function(o){
29102         return o.id || (o.id = Roo.id());
29103     });
29104     this.addEvents({
29105         /**
29106          * @event beforeaction
29107          * Fires before any action is performed. Return false to cancel the action.
29108          * @param {Form} this
29109          * @param {Action} action The action to be performed
29110          */
29111         beforeaction: true,
29112         /**
29113          * @event actionfailed
29114          * Fires when an action fails.
29115          * @param {Form} this
29116          * @param {Action} action The action that failed
29117          */
29118         actionfailed : true,
29119         /**
29120          * @event actioncomplete
29121          * Fires when an action is completed.
29122          * @param {Form} this
29123          * @param {Action} action The action that completed
29124          */
29125         actioncomplete : true
29126     });
29127     if(el){
29128         this.initEl(el);
29129     }
29130     Roo.form.BasicForm.superclass.constructor.call(this);
29131     
29132     Roo.form.BasicForm.popover.apply();
29133 };
29134
29135 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
29136     /**
29137      * @cfg {String} method
29138      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
29139      */
29140     /**
29141      * @cfg {DataReader} reader
29142      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
29143      * This is optional as there is built-in support for processing JSON.
29144      */
29145     /**
29146      * @cfg {DataReader} errorReader
29147      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
29148      * This is completely optional as there is built-in support for processing JSON.
29149      */
29150     /**
29151      * @cfg {String} url
29152      * The URL to use for form actions if one isn't supplied in the action options.
29153      */
29154     /**
29155      * @cfg {Boolean} fileUpload
29156      * Set to true if this form is a file upload.
29157      */
29158      
29159     /**
29160      * @cfg {Object} baseParams
29161      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
29162      */
29163      /**
29164      
29165     /**
29166      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
29167      */
29168     timeout: 30,
29169
29170     // private
29171     activeAction : null,
29172
29173     /**
29174      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
29175      * or setValues() data instead of when the form was first created.
29176      */
29177     trackResetOnLoad : false,
29178     
29179     
29180     /**
29181      * childForms - used for multi-tab forms
29182      * @type {Array}
29183      */
29184     childForms : false,
29185     
29186     /**
29187      * allItems - full list of fields.
29188      * @type {Array}
29189      */
29190     allItems : false,
29191     
29192     /**
29193      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
29194      * element by passing it or its id or mask the form itself by passing in true.
29195      * @type Mixed
29196      */
29197     waitMsgTarget : false,
29198     
29199     /**
29200      * @type Boolean
29201      */
29202     disableMask : false,
29203     
29204     /**
29205      * @cfg {Boolean} errorMask (true|false) default false
29206      */
29207     errorMask : false,
29208     
29209     /**
29210      * @cfg {Number} maskOffset Default 100
29211      */
29212     maskOffset : 100,
29213
29214     // private
29215     initEl : function(el){
29216         this.el = Roo.get(el);
29217         this.id = this.el.id || Roo.id();
29218         this.el.on('submit', this.onSubmit, this);
29219         this.el.addClass('x-form');
29220     },
29221
29222     // private
29223     onSubmit : function(e){
29224         e.stopEvent();
29225     },
29226
29227     /**
29228      * Returns true if client-side validation on the form is successful.
29229      * @return Boolean
29230      */
29231     isValid : function(){
29232         var valid = true;
29233         var target = false;
29234         this.items.each(function(f){
29235             if(f.validate()){
29236                 return;
29237             }
29238             
29239             valid = false;
29240                 
29241             if(!target && f.el.isVisible(true)){
29242                 target = f;
29243             }
29244         });
29245         
29246         if(this.errorMask && !valid){
29247             Roo.form.BasicForm.popover.mask(this, target);
29248         }
29249         
29250         return valid;
29251     },
29252     /**
29253      * Returns array of invalid form fields.
29254      * @return Array
29255      */
29256     
29257     invalidFields : function()
29258     {
29259         var ret = [];
29260         this.items.each(function(f){
29261             if(f.validate()){
29262                 return;
29263             }
29264             ret.push(f);
29265             
29266         });
29267         
29268         return ret;
29269     },
29270     
29271     
29272     /**
29273      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
29274      * @return Boolean
29275      */
29276     isDirty : function(){
29277         var dirty = false;
29278         this.items.each(function(f){
29279            if(f.isDirty()){
29280                dirty = true;
29281                return false;
29282            }
29283         });
29284         return dirty;
29285     },
29286     
29287     /**
29288      * Returns true if any fields in this form have changed since their original load. (New version)
29289      * @return Boolean
29290      */
29291     
29292     hasChanged : function()
29293     {
29294         var dirty = false;
29295         this.items.each(function(f){
29296            if(f.hasChanged()){
29297                dirty = true;
29298                return false;
29299            }
29300         });
29301         return dirty;
29302         
29303     },
29304     /**
29305      * Resets all hasChanged to 'false' -
29306      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29307      * So hasChanged storage is only to be used for this purpose
29308      * @return Boolean
29309      */
29310     resetHasChanged : function()
29311     {
29312         this.items.each(function(f){
29313            f.resetHasChanged();
29314         });
29315         
29316     },
29317     
29318     
29319     /**
29320      * Performs a predefined action (submit or load) or custom actions you define on this form.
29321      * @param {String} actionName The name of the action type
29322      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
29323      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29324      * accept other config options):
29325      * <pre>
29326 Property          Type             Description
29327 ----------------  ---------------  ----------------------------------------------------------------------------------
29328 url               String           The url for the action (defaults to the form's url)
29329 method            String           The form method to use (defaults to the form's method, or POST if not defined)
29330 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
29331 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
29332                                    validate the form on the client (defaults to false)
29333      * </pre>
29334      * @return {BasicForm} this
29335      */
29336     doAction : function(action, options){
29337         if(typeof action == 'string'){
29338             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29339         }
29340         if(this.fireEvent('beforeaction', this, action) !== false){
29341             this.beforeAction(action);
29342             action.run.defer(100, action);
29343         }
29344         return this;
29345     },
29346
29347     /**
29348      * Shortcut to do a submit action.
29349      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29350      * @return {BasicForm} this
29351      */
29352     submit : function(options){
29353         this.doAction('submit', options);
29354         return this;
29355     },
29356
29357     /**
29358      * Shortcut to do a load action.
29359      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29360      * @return {BasicForm} this
29361      */
29362     load : function(options){
29363         this.doAction('load', options);
29364         return this;
29365     },
29366
29367     /**
29368      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29369      * @param {Record} record The record to edit
29370      * @return {BasicForm} this
29371      */
29372     updateRecord : function(record){
29373         record.beginEdit();
29374         var fs = record.fields;
29375         fs.each(function(f){
29376             var field = this.findField(f.name);
29377             if(field){
29378                 record.set(f.name, field.getValue());
29379             }
29380         }, this);
29381         record.endEdit();
29382         return this;
29383     },
29384
29385     /**
29386      * Loads an Roo.data.Record into this form.
29387      * @param {Record} record The record to load
29388      * @return {BasicForm} this
29389      */
29390     loadRecord : function(record){
29391         this.setValues(record.data);
29392         return this;
29393     },
29394
29395     // private
29396     beforeAction : function(action){
29397         var o = action.options;
29398         
29399         if(!this.disableMask) {
29400             if(this.waitMsgTarget === true){
29401                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29402             }else if(this.waitMsgTarget){
29403                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29404                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29405             }else {
29406                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29407             }
29408         }
29409         
29410          
29411     },
29412
29413     // private
29414     afterAction : function(action, success){
29415         this.activeAction = null;
29416         var o = action.options;
29417         
29418         if(!this.disableMask) {
29419             if(this.waitMsgTarget === true){
29420                 this.el.unmask();
29421             }else if(this.waitMsgTarget){
29422                 this.waitMsgTarget.unmask();
29423             }else{
29424                 Roo.MessageBox.updateProgress(1);
29425                 Roo.MessageBox.hide();
29426             }
29427         }
29428         
29429         if(success){
29430             if(o.reset){
29431                 this.reset();
29432             }
29433             Roo.callback(o.success, o.scope, [this, action]);
29434             this.fireEvent('actioncomplete', this, action);
29435             
29436         }else{
29437             
29438             // failure condition..
29439             // we have a scenario where updates need confirming.
29440             // eg. if a locking scenario exists..
29441             // we look for { errors : { needs_confirm : true }} in the response.
29442             if (
29443                 (typeof(action.result) != 'undefined')  &&
29444                 (typeof(action.result.errors) != 'undefined')  &&
29445                 (typeof(action.result.errors.needs_confirm) != 'undefined')
29446            ){
29447                 var _t = this;
29448                 Roo.MessageBox.confirm(
29449                     "Change requires confirmation",
29450                     action.result.errorMsg,
29451                     function(r) {
29452                         if (r != 'yes') {
29453                             return;
29454                         }
29455                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
29456                     }
29457                     
29458                 );
29459                 
29460                 
29461                 
29462                 return;
29463             }
29464             
29465             Roo.callback(o.failure, o.scope, [this, action]);
29466             // show an error message if no failed handler is set..
29467             if (!this.hasListener('actionfailed')) {
29468                 Roo.MessageBox.alert("Error",
29469                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29470                         action.result.errorMsg :
29471                         "Saving Failed, please check your entries or try again"
29472                 );
29473             }
29474             
29475             this.fireEvent('actionfailed', this, action);
29476         }
29477         
29478     },
29479
29480     /**
29481      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29482      * @param {String} id The value to search for
29483      * @return Field
29484      */
29485     findField : function(id){
29486         var field = this.items.get(id);
29487         if(!field){
29488             this.items.each(function(f){
29489                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29490                     field = f;
29491                     return false;
29492                 }
29493             });
29494         }
29495         return field || null;
29496     },
29497
29498     /**
29499      * Add a secondary form to this one, 
29500      * Used to provide tabbed forms. One form is primary, with hidden values 
29501      * which mirror the elements from the other forms.
29502      * 
29503      * @param {Roo.form.Form} form to add.
29504      * 
29505      */
29506     addForm : function(form)
29507     {
29508        
29509         if (this.childForms.indexOf(form) > -1) {
29510             // already added..
29511             return;
29512         }
29513         this.childForms.push(form);
29514         var n = '';
29515         Roo.each(form.allItems, function (fe) {
29516             
29517             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29518             if (this.findField(n)) { // already added..
29519                 return;
29520             }
29521             var add = new Roo.form.Hidden({
29522                 name : n
29523             });
29524             add.render(this.el);
29525             
29526             this.add( add );
29527         }, this);
29528         
29529     },
29530     /**
29531      * Mark fields in this form invalid in bulk.
29532      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29533      * @return {BasicForm} this
29534      */
29535     markInvalid : function(errors){
29536         if(errors instanceof Array){
29537             for(var i = 0, len = errors.length; i < len; i++){
29538                 var fieldError = errors[i];
29539                 var f = this.findField(fieldError.id);
29540                 if(f){
29541                     f.markInvalid(fieldError.msg);
29542                 }
29543             }
29544         }else{
29545             var field, id;
29546             for(id in errors){
29547                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29548                     field.markInvalid(errors[id]);
29549                 }
29550             }
29551         }
29552         Roo.each(this.childForms || [], function (f) {
29553             f.markInvalid(errors);
29554         });
29555         
29556         return this;
29557     },
29558
29559     /**
29560      * Set values for fields in this form in bulk.
29561      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29562      * @return {BasicForm} this
29563      */
29564     setValues : function(values){
29565         if(values instanceof Array){ // array of objects
29566             for(var i = 0, len = values.length; i < len; i++){
29567                 var v = values[i];
29568                 var f = this.findField(v.id);
29569                 if(f){
29570                     f.setValue(v.value);
29571                     if(this.trackResetOnLoad){
29572                         f.originalValue = f.getValue();
29573                     }
29574                 }
29575             }
29576         }else{ // object hash
29577             var field, id;
29578             for(id in values){
29579                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29580                     
29581                     if (field.setFromData && 
29582                         field.valueField && 
29583                         field.displayField &&
29584                         // combos' with local stores can 
29585                         // be queried via setValue()
29586                         // to set their value..
29587                         (field.store && !field.store.isLocal)
29588                         ) {
29589                         // it's a combo
29590                         var sd = { };
29591                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29592                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29593                         field.setFromData(sd);
29594                         
29595                     } else {
29596                         field.setValue(values[id]);
29597                     }
29598                     
29599                     
29600                     if(this.trackResetOnLoad){
29601                         field.originalValue = field.getValue();
29602                     }
29603                 }
29604             }
29605         }
29606         this.resetHasChanged();
29607         
29608         
29609         Roo.each(this.childForms || [], function (f) {
29610             f.setValues(values);
29611             f.resetHasChanged();
29612         });
29613                 
29614         return this;
29615     },
29616  
29617     /**
29618      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29619      * they are returned as an array.
29620      * @param {Boolean} asString
29621      * @return {Object}
29622      */
29623     getValues : function(asString)
29624     {
29625         if (this.childForms) {
29626             // copy values from the child forms
29627             Roo.each(this.childForms, function (f) {
29628                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29629             }, this);
29630         }
29631         
29632         // use formdata
29633         if (typeof(FormData) != 'undefined' && asString !== true) {
29634             // this relies on a 'recent' version of chrome apparently...
29635             try {
29636                 var fd = (new FormData(this.el.dom)).entries();
29637                 var ret = {};
29638                 var ent = fd.next();
29639                 while (!ent.done) {
29640                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29641                     ent = fd.next();
29642                 };
29643                 return ret;
29644             } catch(e) {
29645                 
29646             }
29647             
29648         }
29649         
29650         
29651         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29652         if(asString === true){
29653             return fs;
29654         }
29655         return Roo.urlDecode(fs);
29656     },
29657     
29658     /**
29659      * Returns the fields in this form as an object with key/value pairs. 
29660      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29661      * Normally this will not return readOnly data 
29662      * @param {Boolean} with_readonly return readonly field data.
29663      * @return {Object}
29664      */
29665     getFieldValues : function(with_readonly)
29666     {
29667         if (this.childForms) {
29668             // copy values from the child forms
29669             // should this call getFieldValues - probably not as we do not currently copy
29670             // hidden fields when we generate..
29671             Roo.each(this.childForms, function (f) {
29672                 this.setValues(f.getFieldValues());
29673             }, this);
29674         }
29675         
29676         var ret = {};
29677         this.items.each(function(f){
29678             
29679             if (f.readOnly && with_readonly !== true) {
29680                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29681                         // if a subform contains a copy of them.
29682                         // if you have subforms with the same editable data, you will need to copy the data back
29683                         // and forth.
29684             }
29685             
29686             if (!f.getName()) {
29687                 return;
29688             }
29689             var v = f.getValue();
29690             if (f.inputType =='radio') {
29691                 if (typeof(ret[f.getName()]) == 'undefined') {
29692                     ret[f.getName()] = ''; // empty..
29693                 }
29694                 
29695                 if (!f.el.dom.checked) {
29696                     return;
29697                     
29698                 }
29699                 v = f.el.dom.value;
29700                 
29701             }
29702             
29703             // not sure if this supported any more..
29704             if ((typeof(v) == 'object') && f.getRawValue) {
29705                 v = f.getRawValue() ; // dates..
29706             }
29707             // combo boxes where name != hiddenName...
29708             if (f.name != f.getName()) {
29709                 ret[f.name] = f.getRawValue();
29710             }
29711             ret[f.getName()] = v;
29712         });
29713         
29714         return ret;
29715     },
29716
29717     /**
29718      * Clears all invalid messages in this form.
29719      * @return {BasicForm} this
29720      */
29721     clearInvalid : function(){
29722         this.items.each(function(f){
29723            f.clearInvalid();
29724         });
29725         
29726         Roo.each(this.childForms || [], function (f) {
29727             f.clearInvalid();
29728         });
29729         
29730         
29731         return this;
29732     },
29733
29734     /**
29735      * Resets this form.
29736      * @return {BasicForm} this
29737      */
29738     reset : function(){
29739         this.items.each(function(f){
29740             f.reset();
29741         });
29742         
29743         Roo.each(this.childForms || [], function (f) {
29744             f.reset();
29745         });
29746         this.resetHasChanged();
29747         
29748         return this;
29749     },
29750
29751     /**
29752      * Add Roo.form components to this form.
29753      * @param {Field} field1
29754      * @param {Field} field2 (optional)
29755      * @param {Field} etc (optional)
29756      * @return {BasicForm} this
29757      */
29758     add : function(){
29759         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29760         return this;
29761     },
29762
29763
29764     /**
29765      * Removes a field from the items collection (does NOT remove its markup).
29766      * @param {Field} field
29767      * @return {BasicForm} this
29768      */
29769     remove : function(field){
29770         this.items.remove(field);
29771         return this;
29772     },
29773
29774     /**
29775      * Looks at the fields in this form, checks them for an id attribute,
29776      * and calls applyTo on the existing dom element with that id.
29777      * @return {BasicForm} this
29778      */
29779     render : function(){
29780         this.items.each(function(f){
29781             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29782                 f.applyTo(f.id);
29783             }
29784         });
29785         return this;
29786     },
29787
29788     /**
29789      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29790      * @param {Object} values
29791      * @return {BasicForm} this
29792      */
29793     applyToFields : function(o){
29794         this.items.each(function(f){
29795            Roo.apply(f, o);
29796         });
29797         return this;
29798     },
29799
29800     /**
29801      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29802      * @param {Object} values
29803      * @return {BasicForm} this
29804      */
29805     applyIfToFields : function(o){
29806         this.items.each(function(f){
29807            Roo.applyIf(f, o);
29808         });
29809         return this;
29810     }
29811 });
29812
29813 // back compat
29814 Roo.BasicForm = Roo.form.BasicForm;
29815
29816 Roo.apply(Roo.form.BasicForm, {
29817     
29818     popover : {
29819         
29820         padding : 5,
29821         
29822         isApplied : false,
29823         
29824         isMasked : false,
29825         
29826         form : false,
29827         
29828         target : false,
29829         
29830         intervalID : false,
29831         
29832         maskEl : false,
29833         
29834         apply : function()
29835         {
29836             if(this.isApplied){
29837                 return;
29838             }
29839             
29840             this.maskEl = {
29841                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29842                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29843                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29844                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29845             };
29846             
29847             this.maskEl.top.enableDisplayMode("block");
29848             this.maskEl.left.enableDisplayMode("block");
29849             this.maskEl.bottom.enableDisplayMode("block");
29850             this.maskEl.right.enableDisplayMode("block");
29851             
29852             Roo.get(document.body).on('click', function(){
29853                 this.unmask();
29854             }, this);
29855             
29856             Roo.get(document.body).on('touchstart', function(){
29857                 this.unmask();
29858             }, this);
29859             
29860             this.isApplied = true
29861         },
29862         
29863         mask : function(form, target)
29864         {
29865             this.form = form;
29866             
29867             this.target = target;
29868             
29869             if(!this.form.errorMask || !target.el){
29870                 return;
29871             }
29872             
29873             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29874             
29875             var ot = this.target.el.calcOffsetsTo(scrollable);
29876             
29877             var scrollTo = ot[1] - this.form.maskOffset;
29878             
29879             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29880             
29881             scrollable.scrollTo('top', scrollTo);
29882             
29883             var el = this.target.wrap || this.target.el;
29884             
29885             var box = el.getBox();
29886             
29887             this.maskEl.top.setStyle('position', 'absolute');
29888             this.maskEl.top.setStyle('z-index', 10000);
29889             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29890             this.maskEl.top.setLeft(0);
29891             this.maskEl.top.setTop(0);
29892             this.maskEl.top.show();
29893             
29894             this.maskEl.left.setStyle('position', 'absolute');
29895             this.maskEl.left.setStyle('z-index', 10000);
29896             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29897             this.maskEl.left.setLeft(0);
29898             this.maskEl.left.setTop(box.y - this.padding);
29899             this.maskEl.left.show();
29900
29901             this.maskEl.bottom.setStyle('position', 'absolute');
29902             this.maskEl.bottom.setStyle('z-index', 10000);
29903             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29904             this.maskEl.bottom.setLeft(0);
29905             this.maskEl.bottom.setTop(box.bottom + this.padding);
29906             this.maskEl.bottom.show();
29907
29908             this.maskEl.right.setStyle('position', 'absolute');
29909             this.maskEl.right.setStyle('z-index', 10000);
29910             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29911             this.maskEl.right.setLeft(box.right + this.padding);
29912             this.maskEl.right.setTop(box.y - this.padding);
29913             this.maskEl.right.show();
29914
29915             this.intervalID = window.setInterval(function() {
29916                 Roo.form.BasicForm.popover.unmask();
29917             }, 10000);
29918
29919             window.onwheel = function(){ return false;};
29920             
29921             (function(){ this.isMasked = true; }).defer(500, this);
29922             
29923         },
29924         
29925         unmask : function()
29926         {
29927             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29928                 return;
29929             }
29930             
29931             this.maskEl.top.setStyle('position', 'absolute');
29932             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29933             this.maskEl.top.hide();
29934
29935             this.maskEl.left.setStyle('position', 'absolute');
29936             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29937             this.maskEl.left.hide();
29938
29939             this.maskEl.bottom.setStyle('position', 'absolute');
29940             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29941             this.maskEl.bottom.hide();
29942
29943             this.maskEl.right.setStyle('position', 'absolute');
29944             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29945             this.maskEl.right.hide();
29946             
29947             window.onwheel = function(){ return true;};
29948             
29949             if(this.intervalID){
29950                 window.clearInterval(this.intervalID);
29951                 this.intervalID = false;
29952             }
29953             
29954             this.isMasked = false;
29955             
29956         }
29957         
29958     }
29959     
29960 });/*
29961  * Based on:
29962  * Ext JS Library 1.1.1
29963  * Copyright(c) 2006-2007, Ext JS, LLC.
29964  *
29965  * Originally Released Under LGPL - original licence link has changed is not relivant.
29966  *
29967  * Fork - LGPL
29968  * <script type="text/javascript">
29969  */
29970
29971 /**
29972  * @class Roo.form.Form
29973  * @extends Roo.form.BasicForm
29974  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29975  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29976  * @constructor
29977  * @param {Object} config Configuration options
29978  */
29979 Roo.form.Form = function(config){
29980     var xitems =  [];
29981     if (config.items) {
29982         xitems = config.items;
29983         delete config.items;
29984     }
29985    
29986     
29987     Roo.form.Form.superclass.constructor.call(this, null, config);
29988     this.url = this.url || this.action;
29989     if(!this.root){
29990         this.root = new Roo.form.Layout(Roo.applyIf({
29991             id: Roo.id()
29992         }, config));
29993     }
29994     this.active = this.root;
29995     /**
29996      * Array of all the buttons that have been added to this form via {@link addButton}
29997      * @type Array
29998      */
29999     this.buttons = [];
30000     this.allItems = [];
30001     this.addEvents({
30002         /**
30003          * @event clientvalidation
30004          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
30005          * @param {Form} this
30006          * @param {Boolean} valid true if the form has passed client-side validation
30007          */
30008         clientvalidation: true,
30009         /**
30010          * @event rendered
30011          * Fires when the form is rendered
30012          * @param {Roo.form.Form} form
30013          */
30014         rendered : true
30015     });
30016     
30017     if (this.progressUrl) {
30018             // push a hidden field onto the list of fields..
30019             this.addxtype( {
30020                     xns: Roo.form, 
30021                     xtype : 'Hidden', 
30022                     name : 'UPLOAD_IDENTIFIER' 
30023             });
30024         }
30025         
30026     
30027     Roo.each(xitems, this.addxtype, this);
30028     
30029 };
30030
30031 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
30032      /**
30033      * @cfg {Roo.Button} buttons[] buttons at bottom of form
30034      */
30035     
30036     /**
30037      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
30038      */
30039     /**
30040      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
30041      */
30042     /**
30043      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
30044      */
30045     buttonAlign:'center',
30046
30047     /**
30048      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
30049      */
30050     minButtonWidth:75,
30051
30052     /**
30053      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
30054      * This property cascades to child containers if not set.
30055      */
30056     labelAlign:'left',
30057
30058     /**
30059      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
30060      * fires a looping event with that state. This is required to bind buttons to the valid
30061      * state using the config value formBind:true on the button.
30062      */
30063     monitorValid : false,
30064
30065     /**
30066      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
30067      */
30068     monitorPoll : 200,
30069     
30070     /**
30071      * @cfg {String} progressUrl - Url to return progress data 
30072      */
30073     
30074     progressUrl : false,
30075     /**
30076      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
30077      * sending a formdata with extra parameters - eg uploaded elements.
30078      */
30079     
30080     formData : false,
30081     
30082     /**
30083      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
30084      * fields are added and the column is closed. If no fields are passed the column remains open
30085      * until end() is called.
30086      * @param {Object} config The config to pass to the column
30087      * @param {Field} field1 (optional)
30088      * @param {Field} field2 (optional)
30089      * @param {Field} etc (optional)
30090      * @return Column The column container object
30091      */
30092     column : function(c){
30093         var col = new Roo.form.Column(c);
30094         this.start(col);
30095         if(arguments.length > 1){ // duplicate code required because of Opera
30096             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30097             this.end();
30098         }
30099         return col;
30100     },
30101
30102     /**
30103      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
30104      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
30105      * until end() is called.
30106      * @param {Object} config The config to pass to the fieldset
30107      * @param {Field} field1 (optional)
30108      * @param {Field} field2 (optional)
30109      * @param {Field} etc (optional)
30110      * @return FieldSet The fieldset container object
30111      */
30112     fieldset : function(c){
30113         var fs = new Roo.form.FieldSet(c);
30114         this.start(fs);
30115         if(arguments.length > 1){ // duplicate code required because of Opera
30116             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30117             this.end();
30118         }
30119         return fs;
30120     },
30121
30122     /**
30123      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
30124      * fields are added and the container is closed. If no fields are passed the container remains open
30125      * until end() is called.
30126      * @param {Object} config The config to pass to the Layout
30127      * @param {Field} field1 (optional)
30128      * @param {Field} field2 (optional)
30129      * @param {Field} etc (optional)
30130      * @return Layout The container object
30131      */
30132     container : function(c){
30133         var l = new Roo.form.Layout(c);
30134         this.start(l);
30135         if(arguments.length > 1){ // duplicate code required because of Opera
30136             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30137             this.end();
30138         }
30139         return l;
30140     },
30141
30142     /**
30143      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
30144      * @param {Object} container A Roo.form.Layout or subclass of Layout
30145      * @return {Form} this
30146      */
30147     start : function(c){
30148         // cascade label info
30149         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
30150         this.active.stack.push(c);
30151         c.ownerCt = this.active;
30152         this.active = c;
30153         return this;
30154     },
30155
30156     /**
30157      * Closes the current open container
30158      * @return {Form} this
30159      */
30160     end : function(){
30161         if(this.active == this.root){
30162             return this;
30163         }
30164         this.active = this.active.ownerCt;
30165         return this;
30166     },
30167
30168     /**
30169      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
30170      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
30171      * as the label of the field.
30172      * @param {Field} field1
30173      * @param {Field} field2 (optional)
30174      * @param {Field} etc. (optional)
30175      * @return {Form} this
30176      */
30177     add : function(){
30178         this.active.stack.push.apply(this.active.stack, arguments);
30179         this.allItems.push.apply(this.allItems,arguments);
30180         var r = [];
30181         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
30182             if(a[i].isFormField){
30183                 r.push(a[i]);
30184             }
30185         }
30186         if(r.length > 0){
30187             Roo.form.Form.superclass.add.apply(this, r);
30188         }
30189         return this;
30190     },
30191     
30192
30193     
30194     
30195     
30196      /**
30197      * Find any element that has been added to a form, using it's ID or name
30198      * This can include framesets, columns etc. along with regular fields..
30199      * @param {String} id - id or name to find.
30200      
30201      * @return {Element} e - or false if nothing found.
30202      */
30203     findbyId : function(id)
30204     {
30205         var ret = false;
30206         if (!id) {
30207             return ret;
30208         }
30209         Roo.each(this.allItems, function(f){
30210             if (f.id == id || f.name == id ){
30211                 ret = f;
30212                 return false;
30213             }
30214         });
30215         return ret;
30216     },
30217
30218     
30219     
30220     /**
30221      * Render this form into the passed container. This should only be called once!
30222      * @param {String/HTMLElement/Element} container The element this component should be rendered into
30223      * @return {Form} this
30224      */
30225     render : function(ct)
30226     {
30227         
30228         
30229         
30230         ct = Roo.get(ct);
30231         var o = this.autoCreate || {
30232             tag: 'form',
30233             method : this.method || 'POST',
30234             id : this.id || Roo.id()
30235         };
30236         this.initEl(ct.createChild(o));
30237
30238         this.root.render(this.el);
30239         
30240        
30241              
30242         this.items.each(function(f){
30243             f.render('x-form-el-'+f.id);
30244         });
30245
30246         if(this.buttons.length > 0){
30247             // tables are required to maintain order and for correct IE layout
30248             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30249                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30250                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30251             }}, null, true);
30252             var tr = tb.getElementsByTagName('tr')[0];
30253             for(var i = 0, len = this.buttons.length; i < len; i++) {
30254                 var b = this.buttons[i];
30255                 var td = document.createElement('td');
30256                 td.className = 'x-form-btn-td';
30257                 b.render(tr.appendChild(td));
30258             }
30259         }
30260         if(this.monitorValid){ // initialize after render
30261             this.startMonitoring();
30262         }
30263         this.fireEvent('rendered', this);
30264         return this;
30265     },
30266
30267     /**
30268      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30269      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30270      * object or a valid Roo.DomHelper element config
30271      * @param {Function} handler The function called when the button is clicked
30272      * @param {Object} scope (optional) The scope of the handler function
30273      * @return {Roo.Button}
30274      */
30275     addButton : function(config, handler, scope){
30276         var bc = {
30277             handler: handler,
30278             scope: scope,
30279             minWidth: this.minButtonWidth,
30280             hideParent:true
30281         };
30282         if(typeof config == "string"){
30283             bc.text = config;
30284         }else{
30285             Roo.apply(bc, config);
30286         }
30287         var btn = new Roo.Button(null, bc);
30288         this.buttons.push(btn);
30289         return btn;
30290     },
30291
30292      /**
30293      * Adds a series of form elements (using the xtype property as the factory method.
30294      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30295      * @param {Object} config 
30296      */
30297     
30298     addxtype : function()
30299     {
30300         var ar = Array.prototype.slice.call(arguments, 0);
30301         var ret = false;
30302         for(var i = 0; i < ar.length; i++) {
30303             if (!ar[i]) {
30304                 continue; // skip -- if this happends something invalid got sent, we 
30305                 // should ignore it, as basically that interface element will not show up
30306                 // and that should be pretty obvious!!
30307             }
30308             
30309             if (Roo.form[ar[i].xtype]) {
30310                 ar[i].form = this;
30311                 var fe = Roo.factory(ar[i], Roo.form);
30312                 if (!ret) {
30313                     ret = fe;
30314                 }
30315                 fe.form = this;
30316                 if (fe.store) {
30317                     fe.store.form = this;
30318                 }
30319                 if (fe.isLayout) {  
30320                          
30321                     this.start(fe);
30322                     this.allItems.push(fe);
30323                     if (fe.items && fe.addxtype) {
30324                         fe.addxtype.apply(fe, fe.items);
30325                         delete fe.items;
30326                     }
30327                      this.end();
30328                     continue;
30329                 }
30330                 
30331                 
30332                  
30333                 this.add(fe);
30334               //  console.log('adding ' + ar[i].xtype);
30335             }
30336             if (ar[i].xtype == 'Button') {  
30337                 //console.log('adding button');
30338                 //console.log(ar[i]);
30339                 this.addButton(ar[i]);
30340                 this.allItems.push(fe);
30341                 continue;
30342             }
30343             
30344             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30345                 alert('end is not supported on xtype any more, use items');
30346             //    this.end();
30347             //    //console.log('adding end');
30348             }
30349             
30350         }
30351         return ret;
30352     },
30353     
30354     /**
30355      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30356      * option "monitorValid"
30357      */
30358     startMonitoring : function(){
30359         if(!this.bound){
30360             this.bound = true;
30361             Roo.TaskMgr.start({
30362                 run : this.bindHandler,
30363                 interval : this.monitorPoll || 200,
30364                 scope: this
30365             });
30366         }
30367     },
30368
30369     /**
30370      * Stops monitoring of the valid state of this form
30371      */
30372     stopMonitoring : function(){
30373         this.bound = false;
30374     },
30375
30376     // private
30377     bindHandler : function(){
30378         if(!this.bound){
30379             return false; // stops binding
30380         }
30381         var valid = true;
30382         this.items.each(function(f){
30383             if(!f.isValid(true)){
30384                 valid = false;
30385                 return false;
30386             }
30387         });
30388         for(var i = 0, len = this.buttons.length; i < len; i++){
30389             var btn = this.buttons[i];
30390             if(btn.formBind === true && btn.disabled === valid){
30391                 btn.setDisabled(!valid);
30392             }
30393         }
30394         this.fireEvent('clientvalidation', this, valid);
30395     }
30396     
30397     
30398     
30399     
30400     
30401     
30402     
30403     
30404 });
30405
30406
30407 // back compat
30408 Roo.Form = Roo.form.Form;
30409 /*
30410  * Based on:
30411  * Ext JS Library 1.1.1
30412  * Copyright(c) 2006-2007, Ext JS, LLC.
30413  *
30414  * Originally Released Under LGPL - original licence link has changed is not relivant.
30415  *
30416  * Fork - LGPL
30417  * <script type="text/javascript">
30418  */
30419
30420 // as we use this in bootstrap.
30421 Roo.namespace('Roo.form');
30422  /**
30423  * @class Roo.form.Action
30424  * Internal Class used to handle form actions
30425  * @constructor
30426  * @param {Roo.form.BasicForm} el The form element or its id
30427  * @param {Object} config Configuration options
30428  */
30429
30430  
30431  
30432 // define the action interface
30433 Roo.form.Action = function(form, options){
30434     this.form = form;
30435     this.options = options || {};
30436 };
30437 /**
30438  * Client Validation Failed
30439  * @const 
30440  */
30441 Roo.form.Action.CLIENT_INVALID = 'client';
30442 /**
30443  * Server Validation Failed
30444  * @const 
30445  */
30446 Roo.form.Action.SERVER_INVALID = 'server';
30447  /**
30448  * Connect to Server Failed
30449  * @const 
30450  */
30451 Roo.form.Action.CONNECT_FAILURE = 'connect';
30452 /**
30453  * Reading Data from Server Failed
30454  * @const 
30455  */
30456 Roo.form.Action.LOAD_FAILURE = 'load';
30457
30458 Roo.form.Action.prototype = {
30459     type : 'default',
30460     failureType : undefined,
30461     response : undefined,
30462     result : undefined,
30463
30464     // interface method
30465     run : function(options){
30466
30467     },
30468
30469     // interface method
30470     success : function(response){
30471
30472     },
30473
30474     // interface method
30475     handleResponse : function(response){
30476
30477     },
30478
30479     // default connection failure
30480     failure : function(response){
30481         
30482         this.response = response;
30483         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30484         this.form.afterAction(this, false);
30485     },
30486
30487     processResponse : function(response){
30488         this.response = response;
30489         if(!response.responseText){
30490             return true;
30491         }
30492         this.result = this.handleResponse(response);
30493         return this.result;
30494     },
30495
30496     // utility functions used internally
30497     getUrl : function(appendParams){
30498         var url = this.options.url || this.form.url || this.form.el.dom.action;
30499         if(appendParams){
30500             var p = this.getParams();
30501             if(p){
30502                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30503             }
30504         }
30505         return url;
30506     },
30507
30508     getMethod : function(){
30509         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30510     },
30511
30512     getParams : function(){
30513         var bp = this.form.baseParams;
30514         var p = this.options.params;
30515         if(p){
30516             if(typeof p == "object"){
30517                 p = Roo.urlEncode(Roo.applyIf(p, bp));
30518             }else if(typeof p == 'string' && bp){
30519                 p += '&' + Roo.urlEncode(bp);
30520             }
30521         }else if(bp){
30522             p = Roo.urlEncode(bp);
30523         }
30524         return p;
30525     },
30526
30527     createCallback : function(){
30528         return {
30529             success: this.success,
30530             failure: this.failure,
30531             scope: this,
30532             timeout: (this.form.timeout*1000),
30533             upload: this.form.fileUpload ? this.success : undefined
30534         };
30535     }
30536 };
30537
30538 Roo.form.Action.Submit = function(form, options){
30539     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30540 };
30541
30542 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30543     type : 'submit',
30544
30545     haveProgress : false,
30546     uploadComplete : false,
30547     
30548     // uploadProgress indicator.
30549     uploadProgress : function()
30550     {
30551         if (!this.form.progressUrl) {
30552             return;
30553         }
30554         
30555         if (!this.haveProgress) {
30556             Roo.MessageBox.progress("Uploading", "Uploading");
30557         }
30558         if (this.uploadComplete) {
30559            Roo.MessageBox.hide();
30560            return;
30561         }
30562         
30563         this.haveProgress = true;
30564    
30565         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30566         
30567         var c = new Roo.data.Connection();
30568         c.request({
30569             url : this.form.progressUrl,
30570             params: {
30571                 id : uid
30572             },
30573             method: 'GET',
30574             success : function(req){
30575                //console.log(data);
30576                 var rdata = false;
30577                 var edata;
30578                 try  {
30579                    rdata = Roo.decode(req.responseText)
30580                 } catch (e) {
30581                     Roo.log("Invalid data from server..");
30582                     Roo.log(edata);
30583                     return;
30584                 }
30585                 if (!rdata || !rdata.success) {
30586                     Roo.log(rdata);
30587                     Roo.MessageBox.alert(Roo.encode(rdata));
30588                     return;
30589                 }
30590                 var data = rdata.data;
30591                 
30592                 if (this.uploadComplete) {
30593                    Roo.MessageBox.hide();
30594                    return;
30595                 }
30596                    
30597                 if (data){
30598                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30599                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30600                     );
30601                 }
30602                 this.uploadProgress.defer(2000,this);
30603             },
30604        
30605             failure: function(data) {
30606                 Roo.log('progress url failed ');
30607                 Roo.log(data);
30608             },
30609             scope : this
30610         });
30611            
30612     },
30613     
30614     
30615     run : function()
30616     {
30617         // run get Values on the form, so it syncs any secondary forms.
30618         this.form.getValues();
30619         
30620         var o = this.options;
30621         var method = this.getMethod();
30622         var isPost = method == 'POST';
30623         if(o.clientValidation === false || this.form.isValid()){
30624             
30625             if (this.form.progressUrl) {
30626                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30627                     (new Date() * 1) + '' + Math.random());
30628                     
30629             } 
30630             
30631             
30632             Roo.Ajax.request(Roo.apply(this.createCallback(), {
30633                 form:this.form.el.dom,
30634                 url:this.getUrl(!isPost),
30635                 method: method,
30636                 params:isPost ? this.getParams() : null,
30637                 isUpload: this.form.fileUpload,
30638                 formData : this.form.formData
30639             }));
30640             
30641             this.uploadProgress();
30642
30643         }else if (o.clientValidation !== false){ // client validation failed
30644             this.failureType = Roo.form.Action.CLIENT_INVALID;
30645             this.form.afterAction(this, false);
30646         }
30647     },
30648
30649     success : function(response)
30650     {
30651         this.uploadComplete= true;
30652         if (this.haveProgress) {
30653             Roo.MessageBox.hide();
30654         }
30655         
30656         
30657         var result = this.processResponse(response);
30658         if(result === true || result.success){
30659             this.form.afterAction(this, true);
30660             return;
30661         }
30662         if(result.errors){
30663             this.form.markInvalid(result.errors);
30664             this.failureType = Roo.form.Action.SERVER_INVALID;
30665         }
30666         this.form.afterAction(this, false);
30667     },
30668     failure : function(response)
30669     {
30670         this.uploadComplete= true;
30671         if (this.haveProgress) {
30672             Roo.MessageBox.hide();
30673         }
30674         
30675         this.response = response;
30676         this.failureType = Roo.form.Action.CONNECT_FAILURE;
30677         this.form.afterAction(this, false);
30678     },
30679     
30680     handleResponse : function(response){
30681         if(this.form.errorReader){
30682             var rs = this.form.errorReader.read(response);
30683             var errors = [];
30684             if(rs.records){
30685                 for(var i = 0, len = rs.records.length; i < len; i++) {
30686                     var r = rs.records[i];
30687                     errors[i] = r.data;
30688                 }
30689             }
30690             if(errors.length < 1){
30691                 errors = null;
30692             }
30693             return {
30694                 success : rs.success,
30695                 errors : errors
30696             };
30697         }
30698         var ret = false;
30699         try {
30700             ret = Roo.decode(response.responseText);
30701         } catch (e) {
30702             ret = {
30703                 success: false,
30704                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30705                 errors : []
30706             };
30707         }
30708         return ret;
30709         
30710     }
30711 });
30712
30713
30714 Roo.form.Action.Load = function(form, options){
30715     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30716     this.reader = this.form.reader;
30717 };
30718
30719 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30720     type : 'load',
30721
30722     run : function(){
30723         
30724         Roo.Ajax.request(Roo.apply(
30725                 this.createCallback(), {
30726                     method:this.getMethod(),
30727                     url:this.getUrl(false),
30728                     params:this.getParams()
30729         }));
30730     },
30731
30732     success : function(response){
30733         
30734         var result = this.processResponse(response);
30735         if(result === true || !result.success || !result.data){
30736             this.failureType = Roo.form.Action.LOAD_FAILURE;
30737             this.form.afterAction(this, false);
30738             return;
30739         }
30740         this.form.clearInvalid();
30741         this.form.setValues(result.data);
30742         this.form.afterAction(this, true);
30743     },
30744
30745     handleResponse : function(response){
30746         if(this.form.reader){
30747             var rs = this.form.reader.read(response);
30748             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30749             return {
30750                 success : rs.success,
30751                 data : data
30752             };
30753         }
30754         return Roo.decode(response.responseText);
30755     }
30756 });
30757
30758 Roo.form.Action.ACTION_TYPES = {
30759     'load' : Roo.form.Action.Load,
30760     'submit' : Roo.form.Action.Submit
30761 };/*
30762  * Based on:
30763  * Ext JS Library 1.1.1
30764  * Copyright(c) 2006-2007, Ext JS, LLC.
30765  *
30766  * Originally Released Under LGPL - original licence link has changed is not relivant.
30767  *
30768  * Fork - LGPL
30769  * <script type="text/javascript">
30770  */
30771  
30772 /**
30773  * @class Roo.form.Layout
30774  * @extends Roo.Component
30775  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30776  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30777  * @constructor
30778  * @param {Object} config Configuration options
30779  */
30780 Roo.form.Layout = function(config){
30781     var xitems = [];
30782     if (config.items) {
30783         xitems = config.items;
30784         delete config.items;
30785     }
30786     Roo.form.Layout.superclass.constructor.call(this, config);
30787     this.stack = [];
30788     Roo.each(xitems, this.addxtype, this);
30789      
30790 };
30791
30792 Roo.extend(Roo.form.Layout, Roo.Component, {
30793     /**
30794      * @cfg {String/Object} autoCreate
30795      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30796      */
30797     /**
30798      * @cfg {String/Object/Function} style
30799      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30800      * a function which returns such a specification.
30801      */
30802     /**
30803      * @cfg {String} labelAlign
30804      * Valid values are "left," "top" and "right" (defaults to "left")
30805      */
30806     /**
30807      * @cfg {Number} labelWidth
30808      * Fixed width in pixels of all field labels (defaults to undefined)
30809      */
30810     /**
30811      * @cfg {Boolean} clear
30812      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30813      */
30814     clear : true,
30815     /**
30816      * @cfg {String} labelSeparator
30817      * The separator to use after field labels (defaults to ':')
30818      */
30819     labelSeparator : ':',
30820     /**
30821      * @cfg {Boolean} hideLabels
30822      * True to suppress the display of field labels in this layout (defaults to false)
30823      */
30824     hideLabels : false,
30825
30826     // private
30827     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30828     
30829     isLayout : true,
30830     
30831     // private
30832     onRender : function(ct, position){
30833         if(this.el){ // from markup
30834             this.el = Roo.get(this.el);
30835         }else {  // generate
30836             var cfg = this.getAutoCreate();
30837             this.el = ct.createChild(cfg, position);
30838         }
30839         if(this.style){
30840             this.el.applyStyles(this.style);
30841         }
30842         if(this.labelAlign){
30843             this.el.addClass('x-form-label-'+this.labelAlign);
30844         }
30845         if(this.hideLabels){
30846             this.labelStyle = "display:none";
30847             this.elementStyle = "padding-left:0;";
30848         }else{
30849             if(typeof this.labelWidth == 'number'){
30850                 this.labelStyle = "width:"+this.labelWidth+"px;";
30851                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30852             }
30853             if(this.labelAlign == 'top'){
30854                 this.labelStyle = "width:auto;";
30855                 this.elementStyle = "padding-left:0;";
30856             }
30857         }
30858         var stack = this.stack;
30859         var slen = stack.length;
30860         if(slen > 0){
30861             if(!this.fieldTpl){
30862                 var t = new Roo.Template(
30863                     '<div class="x-form-item {5}">',
30864                         '<label for="{0}" style="{2}">{1}{4}</label>',
30865                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30866                         '</div>',
30867                     '</div><div class="x-form-clear-left"></div>'
30868                 );
30869                 t.disableFormats = true;
30870                 t.compile();
30871                 Roo.form.Layout.prototype.fieldTpl = t;
30872             }
30873             for(var i = 0; i < slen; i++) {
30874                 if(stack[i].isFormField){
30875                     this.renderField(stack[i]);
30876                 }else{
30877                     this.renderComponent(stack[i]);
30878                 }
30879             }
30880         }
30881         if(this.clear){
30882             this.el.createChild({cls:'x-form-clear'});
30883         }
30884     },
30885
30886     // private
30887     renderField : function(f){
30888         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30889                f.id, //0
30890                f.fieldLabel, //1
30891                f.labelStyle||this.labelStyle||'', //2
30892                this.elementStyle||'', //3
30893                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30894                f.itemCls||this.itemCls||''  //5
30895        ], true).getPrevSibling());
30896     },
30897
30898     // private
30899     renderComponent : function(c){
30900         c.render(c.isLayout ? this.el : this.el.createChild());    
30901     },
30902     /**
30903      * Adds a object form elements (using the xtype property as the factory method.)
30904      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30905      * @param {Object} config 
30906      */
30907     addxtype : function(o)
30908     {
30909         // create the lement.
30910         o.form = this.form;
30911         var fe = Roo.factory(o, Roo.form);
30912         this.form.allItems.push(fe);
30913         this.stack.push(fe);
30914         
30915         if (fe.isFormField) {
30916             this.form.items.add(fe);
30917         }
30918          
30919         return fe;
30920     }
30921 });
30922
30923 /**
30924  * @class Roo.form.Column
30925  * @extends Roo.form.Layout
30926  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30927  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30928  * @constructor
30929  * @param {Object} config Configuration options
30930  */
30931 Roo.form.Column = function(config){
30932     Roo.form.Column.superclass.constructor.call(this, config);
30933 };
30934
30935 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30936     /**
30937      * @cfg {Number/String} width
30938      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30939      */
30940     /**
30941      * @cfg {String/Object} autoCreate
30942      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30943      */
30944
30945     // private
30946     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30947
30948     // private
30949     onRender : function(ct, position){
30950         Roo.form.Column.superclass.onRender.call(this, ct, position);
30951         if(this.width){
30952             this.el.setWidth(this.width);
30953         }
30954     }
30955 });
30956
30957
30958 /**
30959  * @class Roo.form.Row
30960  * @extends Roo.form.Layout
30961  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30962  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30963  * @constructor
30964  * @param {Object} config Configuration options
30965  */
30966
30967  
30968 Roo.form.Row = function(config){
30969     Roo.form.Row.superclass.constructor.call(this, config);
30970 };
30971  
30972 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30973       /**
30974      * @cfg {Number/String} width
30975      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30976      */
30977     /**
30978      * @cfg {Number/String} height
30979      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30980      */
30981     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30982     
30983     padWidth : 20,
30984     // private
30985     onRender : function(ct, position){
30986         //console.log('row render');
30987         if(!this.rowTpl){
30988             var t = new Roo.Template(
30989                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30990                     '<label for="{0}" style="{2}">{1}{4}</label>',
30991                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30992                     '</div>',
30993                 '</div>'
30994             );
30995             t.disableFormats = true;
30996             t.compile();
30997             Roo.form.Layout.prototype.rowTpl = t;
30998         }
30999         this.fieldTpl = this.rowTpl;
31000         
31001         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
31002         var labelWidth = 100;
31003         
31004         if ((this.labelAlign != 'top')) {
31005             if (typeof this.labelWidth == 'number') {
31006                 labelWidth = this.labelWidth
31007             }
31008             this.padWidth =  20 + labelWidth;
31009             
31010         }
31011         
31012         Roo.form.Column.superclass.onRender.call(this, ct, position);
31013         if(this.width){
31014             this.el.setWidth(this.width);
31015         }
31016         if(this.height){
31017             this.el.setHeight(this.height);
31018         }
31019     },
31020     
31021     // private
31022     renderField : function(f){
31023         f.fieldEl = this.fieldTpl.append(this.el, [
31024                f.id, f.fieldLabel,
31025                f.labelStyle||this.labelStyle||'',
31026                this.elementStyle||'',
31027                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
31028                f.itemCls||this.itemCls||'',
31029                f.width ? f.width + this.padWidth : 160 + this.padWidth
31030        ],true);
31031     }
31032 });
31033  
31034
31035 /**
31036  * @class Roo.form.FieldSet
31037  * @extends Roo.form.Layout
31038  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
31039  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
31040  * @constructor
31041  * @param {Object} config Configuration options
31042  */
31043 Roo.form.FieldSet = function(config){
31044     Roo.form.FieldSet.superclass.constructor.call(this, config);
31045 };
31046
31047 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
31048     /**
31049      * @cfg {String} legend
31050      * The text to display as the legend for the FieldSet (defaults to '')
31051      */
31052     /**
31053      * @cfg {String/Object} autoCreate
31054      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
31055      */
31056
31057     // private
31058     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
31059
31060     // private
31061     onRender : function(ct, position){
31062         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
31063         if(this.legend){
31064             this.setLegend(this.legend);
31065         }
31066     },
31067
31068     // private
31069     setLegend : function(text){
31070         if(this.rendered){
31071             this.el.child('legend').update(text);
31072         }
31073     }
31074 });/*
31075  * Based on:
31076  * Ext JS Library 1.1.1
31077  * Copyright(c) 2006-2007, Ext JS, LLC.
31078  *
31079  * Originally Released Under LGPL - original licence link has changed is not relivant.
31080  *
31081  * Fork - LGPL
31082  * <script type="text/javascript">
31083  */
31084 /**
31085  * @class Roo.form.VTypes
31086  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
31087  * @static
31088  */
31089 Roo.form.VTypes = function(){
31090     // closure these in so they are only created once.
31091     var alpha = /^[a-zA-Z_]+$/;
31092     var alphanum = /^[a-zA-Z0-9_]+$/;
31093     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
31094     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
31095
31096     // All these messages and functions are configurable
31097     return {
31098         /**
31099          * The function used to validate email addresses
31100          * @param {String} value The email address
31101          */
31102         'email' : function(v){
31103             return email.test(v);
31104         },
31105         /**
31106          * The error text to display when the email validation function returns false
31107          * @type String
31108          */
31109         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
31110         /**
31111          * The keystroke filter mask to be applied on email input
31112          * @type RegExp
31113          */
31114         'emailMask' : /[a-z0-9_\.\-@]/i,
31115
31116         /**
31117          * The function used to validate URLs
31118          * @param {String} value The URL
31119          */
31120         'url' : function(v){
31121             return url.test(v);
31122         },
31123         /**
31124          * The error text to display when the url validation function returns false
31125          * @type String
31126          */
31127         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
31128         
31129         /**
31130          * The function used to validate alpha values
31131          * @param {String} value The value
31132          */
31133         'alpha' : function(v){
31134             return alpha.test(v);
31135         },
31136         /**
31137          * The error text to display when the alpha validation function returns false
31138          * @type String
31139          */
31140         'alphaText' : 'This field should only contain letters and _',
31141         /**
31142          * The keystroke filter mask to be applied on alpha input
31143          * @type RegExp
31144          */
31145         'alphaMask' : /[a-z_]/i,
31146
31147         /**
31148          * The function used to validate alphanumeric values
31149          * @param {String} value The value
31150          */
31151         'alphanum' : function(v){
31152             return alphanum.test(v);
31153         },
31154         /**
31155          * The error text to display when the alphanumeric validation function returns false
31156          * @type String
31157          */
31158         'alphanumText' : 'This field should only contain letters, numbers and _',
31159         /**
31160          * The keystroke filter mask to be applied on alphanumeric input
31161          * @type RegExp
31162          */
31163         'alphanumMask' : /[a-z0-9_]/i
31164     };
31165 }();//<script type="text/javascript">
31166
31167 /**
31168  * @class Roo.form.FCKeditor
31169  * @extends Roo.form.TextArea
31170  * Wrapper around the FCKEditor http://www.fckeditor.net
31171  * @constructor
31172  * Creates a new FCKeditor
31173  * @param {Object} config Configuration options
31174  */
31175 Roo.form.FCKeditor = function(config){
31176     Roo.form.FCKeditor.superclass.constructor.call(this, config);
31177     this.addEvents({
31178          /**
31179          * @event editorinit
31180          * Fired when the editor is initialized - you can add extra handlers here..
31181          * @param {FCKeditor} this
31182          * @param {Object} the FCK object.
31183          */
31184         editorinit : true
31185     });
31186     
31187     
31188 };
31189 Roo.form.FCKeditor.editors = { };
31190 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
31191 {
31192     //defaultAutoCreate : {
31193     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
31194     //},
31195     // private
31196     /**
31197      * @cfg {Object} fck options - see fck manual for details.
31198      */
31199     fckconfig : false,
31200     
31201     /**
31202      * @cfg {Object} fck toolbar set (Basic or Default)
31203      */
31204     toolbarSet : 'Basic',
31205     /**
31206      * @cfg {Object} fck BasePath
31207      */ 
31208     basePath : '/fckeditor/',
31209     
31210     
31211     frame : false,
31212     
31213     value : '',
31214     
31215    
31216     onRender : function(ct, position)
31217     {
31218         if(!this.el){
31219             this.defaultAutoCreate = {
31220                 tag: "textarea",
31221                 style:"width:300px;height:60px;",
31222                 autocomplete: "new-password"
31223             };
31224         }
31225         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31226         /*
31227         if(this.grow){
31228             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31229             if(this.preventScrollbars){
31230                 this.el.setStyle("overflow", "hidden");
31231             }
31232             this.el.setHeight(this.growMin);
31233         }
31234         */
31235         //console.log('onrender' + this.getId() );
31236         Roo.form.FCKeditor.editors[this.getId()] = this;
31237          
31238
31239         this.replaceTextarea() ;
31240         
31241     },
31242     
31243     getEditor : function() {
31244         return this.fckEditor;
31245     },
31246     /**
31247      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
31248      * @param {Mixed} value The value to set
31249      */
31250     
31251     
31252     setValue : function(value)
31253     {
31254         //console.log('setValue: ' + value);
31255         
31256         if(typeof(value) == 'undefined') { // not sure why this is happending...
31257             return;
31258         }
31259         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31260         
31261         //if(!this.el || !this.getEditor()) {
31262         //    this.value = value;
31263             //this.setValue.defer(100,this,[value]);    
31264         //    return;
31265         //} 
31266         
31267         if(!this.getEditor()) {
31268             return;
31269         }
31270         
31271         this.getEditor().SetData(value);
31272         
31273         //
31274
31275     },
31276
31277     /**
31278      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
31279      * @return {Mixed} value The field value
31280      */
31281     getValue : function()
31282     {
31283         
31284         if (this.frame && this.frame.dom.style.display == 'none') {
31285             return Roo.form.FCKeditor.superclass.getValue.call(this);
31286         }
31287         
31288         if(!this.el || !this.getEditor()) {
31289            
31290            // this.getValue.defer(100,this); 
31291             return this.value;
31292         }
31293        
31294         
31295         var value=this.getEditor().GetData();
31296         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31297         return Roo.form.FCKeditor.superclass.getValue.call(this);
31298         
31299
31300     },
31301
31302     /**
31303      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
31304      * @return {Mixed} value The field value
31305      */
31306     getRawValue : function()
31307     {
31308         if (this.frame && this.frame.dom.style.display == 'none') {
31309             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31310         }
31311         
31312         if(!this.el || !this.getEditor()) {
31313             //this.getRawValue.defer(100,this); 
31314             return this.value;
31315             return;
31316         }
31317         
31318         
31319         
31320         var value=this.getEditor().GetData();
31321         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31322         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31323          
31324     },
31325     
31326     setSize : function(w,h) {
31327         
31328         
31329         
31330         //if (this.frame && this.frame.dom.style.display == 'none') {
31331         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31332         //    return;
31333         //}
31334         //if(!this.el || !this.getEditor()) {
31335         //    this.setSize.defer(100,this, [w,h]); 
31336         //    return;
31337         //}
31338         
31339         
31340         
31341         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31342         
31343         this.frame.dom.setAttribute('width', w);
31344         this.frame.dom.setAttribute('height', h);
31345         this.frame.setSize(w,h);
31346         
31347     },
31348     
31349     toggleSourceEdit : function(value) {
31350         
31351       
31352          
31353         this.el.dom.style.display = value ? '' : 'none';
31354         this.frame.dom.style.display = value ?  'none' : '';
31355         
31356     },
31357     
31358     
31359     focus: function(tag)
31360     {
31361         if (this.frame.dom.style.display == 'none') {
31362             return Roo.form.FCKeditor.superclass.focus.call(this);
31363         }
31364         if(!this.el || !this.getEditor()) {
31365             this.focus.defer(100,this, [tag]); 
31366             return;
31367         }
31368         
31369         
31370         
31371         
31372         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31373         this.getEditor().Focus();
31374         if (tgs.length) {
31375             if (!this.getEditor().Selection.GetSelection()) {
31376                 this.focus.defer(100,this, [tag]); 
31377                 return;
31378             }
31379             
31380             
31381             var r = this.getEditor().EditorDocument.createRange();
31382             r.setStart(tgs[0],0);
31383             r.setEnd(tgs[0],0);
31384             this.getEditor().Selection.GetSelection().removeAllRanges();
31385             this.getEditor().Selection.GetSelection().addRange(r);
31386             this.getEditor().Focus();
31387         }
31388         
31389     },
31390     
31391     
31392     
31393     replaceTextarea : function()
31394     {
31395         if ( document.getElementById( this.getId() + '___Frame' ) ) {
31396             return ;
31397         }
31398         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31399         //{
31400             // We must check the elements firstly using the Id and then the name.
31401         var oTextarea = document.getElementById( this.getId() );
31402         
31403         var colElementsByName = document.getElementsByName( this.getId() ) ;
31404          
31405         oTextarea.style.display = 'none' ;
31406
31407         if ( oTextarea.tabIndex ) {            
31408             this.TabIndex = oTextarea.tabIndex ;
31409         }
31410         
31411         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31412         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31413         this.frame = Roo.get(this.getId() + '___Frame')
31414     },
31415     
31416     _getConfigHtml : function()
31417     {
31418         var sConfig = '' ;
31419
31420         for ( var o in this.fckconfig ) {
31421             sConfig += sConfig.length > 0  ? '&amp;' : '';
31422             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31423         }
31424
31425         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31426     },
31427     
31428     
31429     _getIFrameHtml : function()
31430     {
31431         var sFile = 'fckeditor.html' ;
31432         /* no idea what this is about..
31433         try
31434         {
31435             if ( (/fcksource=true/i).test( window.top.location.search ) )
31436                 sFile = 'fckeditor.original.html' ;
31437         }
31438         catch (e) { 
31439         */
31440
31441         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31442         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
31443         
31444         
31445         var html = '<iframe id="' + this.getId() +
31446             '___Frame" src="' + sLink +
31447             '" width="' + this.width +
31448             '" height="' + this.height + '"' +
31449             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
31450             ' frameborder="0" scrolling="no"></iframe>' ;
31451
31452         return html ;
31453     },
31454     
31455     _insertHtmlBefore : function( html, element )
31456     {
31457         if ( element.insertAdjacentHTML )       {
31458             // IE
31459             element.insertAdjacentHTML( 'beforeBegin', html ) ;
31460         } else { // Gecko
31461             var oRange = document.createRange() ;
31462             oRange.setStartBefore( element ) ;
31463             var oFragment = oRange.createContextualFragment( html );
31464             element.parentNode.insertBefore( oFragment, element ) ;
31465         }
31466     }
31467     
31468     
31469   
31470     
31471     
31472     
31473     
31474
31475 });
31476
31477 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31478
31479 function FCKeditor_OnComplete(editorInstance){
31480     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31481     f.fckEditor = editorInstance;
31482     //console.log("loaded");
31483     f.fireEvent('editorinit', f, editorInstance);
31484
31485   
31486
31487  
31488
31489
31490
31491
31492
31493
31494
31495
31496
31497
31498
31499
31500
31501
31502
31503 //<script type="text/javascript">
31504 /**
31505  * @class Roo.form.GridField
31506  * @extends Roo.form.Field
31507  * Embed a grid (or editable grid into a form)
31508  * STATUS ALPHA
31509  * 
31510  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31511  * it needs 
31512  * xgrid.store = Roo.data.Store
31513  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31514  * xgrid.store.reader = Roo.data.JsonReader 
31515  * 
31516  * 
31517  * @constructor
31518  * Creates a new GridField
31519  * @param {Object} config Configuration options
31520  */
31521 Roo.form.GridField = function(config){
31522     Roo.form.GridField.superclass.constructor.call(this, config);
31523      
31524 };
31525
31526 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
31527     /**
31528      * @cfg {Number} width  - used to restrict width of grid..
31529      */
31530     width : 100,
31531     /**
31532      * @cfg {Number} height - used to restrict height of grid..
31533      */
31534     height : 50,
31535      /**
31536      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31537          * 
31538          *}
31539      */
31540     xgrid : false, 
31541     /**
31542      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31543      * {tag: "input", type: "checkbox", autocomplete: "off"})
31544      */
31545    // defaultAutoCreate : { tag: 'div' },
31546     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31547     /**
31548      * @cfg {String} addTitle Text to include for adding a title.
31549      */
31550     addTitle : false,
31551     //
31552     onResize : function(){
31553         Roo.form.Field.superclass.onResize.apply(this, arguments);
31554     },
31555
31556     initEvents : function(){
31557         // Roo.form.Checkbox.superclass.initEvents.call(this);
31558         // has no events...
31559        
31560     },
31561
31562
31563     getResizeEl : function(){
31564         return this.wrap;
31565     },
31566
31567     getPositionEl : function(){
31568         return this.wrap;
31569     },
31570
31571     // private
31572     onRender : function(ct, position){
31573         
31574         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31575         var style = this.style;
31576         delete this.style;
31577         
31578         Roo.form.GridField.superclass.onRender.call(this, ct, position);
31579         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31580         this.viewEl = this.wrap.createChild({ tag: 'div' });
31581         if (style) {
31582             this.viewEl.applyStyles(style);
31583         }
31584         if (this.width) {
31585             this.viewEl.setWidth(this.width);
31586         }
31587         if (this.height) {
31588             this.viewEl.setHeight(this.height);
31589         }
31590         //if(this.inputValue !== undefined){
31591         //this.setValue(this.value);
31592         
31593         
31594         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31595         
31596         
31597         this.grid.render();
31598         this.grid.getDataSource().on('remove', this.refreshValue, this);
31599         this.grid.getDataSource().on('update', this.refreshValue, this);
31600         this.grid.on('afteredit', this.refreshValue, this);
31601  
31602     },
31603      
31604     
31605     /**
31606      * Sets the value of the item. 
31607      * @param {String} either an object  or a string..
31608      */
31609     setValue : function(v){
31610         //this.value = v;
31611         v = v || []; // empty set..
31612         // this does not seem smart - it really only affects memoryproxy grids..
31613         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31614             var ds = this.grid.getDataSource();
31615             // assumes a json reader..
31616             var data = {}
31617             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
31618             ds.loadData( data);
31619         }
31620         // clear selection so it does not get stale.
31621         if (this.grid.sm) { 
31622             this.grid.sm.clearSelections();
31623         }
31624         
31625         Roo.form.GridField.superclass.setValue.call(this, v);
31626         this.refreshValue();
31627         // should load data in the grid really....
31628     },
31629     
31630     // private
31631     refreshValue: function() {
31632          var val = [];
31633         this.grid.getDataSource().each(function(r) {
31634             val.push(r.data);
31635         });
31636         this.el.dom.value = Roo.encode(val);
31637     }
31638     
31639      
31640     
31641     
31642 });/*
31643  * Based on:
31644  * Ext JS Library 1.1.1
31645  * Copyright(c) 2006-2007, Ext JS, LLC.
31646  *
31647  * Originally Released Under LGPL - original licence link has changed is not relivant.
31648  *
31649  * Fork - LGPL
31650  * <script type="text/javascript">
31651  */
31652 /**
31653  * @class Roo.form.DisplayField
31654  * @extends Roo.form.Field
31655  * A generic Field to display non-editable data.
31656  * @cfg {Boolean} closable (true|false) default false
31657  * @constructor
31658  * Creates a new Display Field item.
31659  * @param {Object} config Configuration options
31660  */
31661 Roo.form.DisplayField = function(config){
31662     Roo.form.DisplayField.superclass.constructor.call(this, config);
31663     
31664     this.addEvents({
31665         /**
31666          * @event close
31667          * Fires after the click the close btn
31668              * @param {Roo.form.DisplayField} this
31669              */
31670         close : true
31671     });
31672 };
31673
31674 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
31675     inputType:      'hidden',
31676     allowBlank:     true,
31677     readOnly:         true,
31678     
31679  
31680     /**
31681      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31682      */
31683     focusClass : undefined,
31684     /**
31685      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31686      */
31687     fieldClass: 'x-form-field',
31688     
31689      /**
31690      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31691      */
31692     valueRenderer: undefined,
31693     
31694     width: 100,
31695     /**
31696      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31697      * {tag: "input", type: "checkbox", autocomplete: "off"})
31698      */
31699      
31700  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31701  
31702     closable : false,
31703     
31704     onResize : function(){
31705         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31706         
31707     },
31708
31709     initEvents : function(){
31710         // Roo.form.Checkbox.superclass.initEvents.call(this);
31711         // has no events...
31712         
31713         if(this.closable){
31714             this.closeEl.on('click', this.onClose, this);
31715         }
31716        
31717     },
31718
31719
31720     getResizeEl : function(){
31721         return this.wrap;
31722     },
31723
31724     getPositionEl : function(){
31725         return this.wrap;
31726     },
31727
31728     // private
31729     onRender : function(ct, position){
31730         
31731         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31732         //if(this.inputValue !== undefined){
31733         this.wrap = this.el.wrap();
31734         
31735         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31736         
31737         if(this.closable){
31738             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31739         }
31740         
31741         if (this.bodyStyle) {
31742             this.viewEl.applyStyles(this.bodyStyle);
31743         }
31744         //this.viewEl.setStyle('padding', '2px');
31745         
31746         this.setValue(this.value);
31747         
31748     },
31749 /*
31750     // private
31751     initValue : Roo.emptyFn,
31752
31753   */
31754
31755         // private
31756     onClick : function(){
31757         
31758     },
31759
31760     /**
31761      * Sets the checked state of the checkbox.
31762      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31763      */
31764     setValue : function(v){
31765         this.value = v;
31766         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31767         // this might be called before we have a dom element..
31768         if (!this.viewEl) {
31769             return;
31770         }
31771         this.viewEl.dom.innerHTML = html;
31772         Roo.form.DisplayField.superclass.setValue.call(this, v);
31773
31774     },
31775     
31776     onClose : function(e)
31777     {
31778         e.preventDefault();
31779         
31780         this.fireEvent('close', this);
31781     }
31782 });/*
31783  * 
31784  * Licence- LGPL
31785  * 
31786  */
31787
31788 /**
31789  * @class Roo.form.DayPicker
31790  * @extends Roo.form.Field
31791  * A Day picker show [M] [T] [W] ....
31792  * @constructor
31793  * Creates a new Day Picker
31794  * @param {Object} config Configuration options
31795  */
31796 Roo.form.DayPicker= function(config){
31797     Roo.form.DayPicker.superclass.constructor.call(this, config);
31798      
31799 };
31800
31801 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31802     /**
31803      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31804      */
31805     focusClass : undefined,
31806     /**
31807      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31808      */
31809     fieldClass: "x-form-field",
31810    
31811     /**
31812      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31813      * {tag: "input", type: "checkbox", autocomplete: "off"})
31814      */
31815     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31816     
31817    
31818     actionMode : 'viewEl', 
31819     //
31820     // private
31821  
31822     inputType : 'hidden',
31823     
31824      
31825     inputElement: false, // real input element?
31826     basedOn: false, // ????
31827     
31828     isFormField: true, // not sure where this is needed!!!!
31829
31830     onResize : function(){
31831         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31832         if(!this.boxLabel){
31833             this.el.alignTo(this.wrap, 'c-c');
31834         }
31835     },
31836
31837     initEvents : function(){
31838         Roo.form.Checkbox.superclass.initEvents.call(this);
31839         this.el.on("click", this.onClick,  this);
31840         this.el.on("change", this.onClick,  this);
31841     },
31842
31843
31844     getResizeEl : function(){
31845         return this.wrap;
31846     },
31847
31848     getPositionEl : function(){
31849         return this.wrap;
31850     },
31851
31852     
31853     // private
31854     onRender : function(ct, position){
31855         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31856        
31857         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31858         
31859         var r1 = '<table><tr>';
31860         var r2 = '<tr class="x-form-daypick-icons">';
31861         for (var i=0; i < 7; i++) {
31862             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31863             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31864         }
31865         
31866         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31867         viewEl.select('img').on('click', this.onClick, this);
31868         this.viewEl = viewEl;   
31869         
31870         
31871         // this will not work on Chrome!!!
31872         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31873         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31874         
31875         
31876           
31877
31878     },
31879
31880     // private
31881     initValue : Roo.emptyFn,
31882
31883     /**
31884      * Returns the checked state of the checkbox.
31885      * @return {Boolean} True if checked, else false
31886      */
31887     getValue : function(){
31888         return this.el.dom.value;
31889         
31890     },
31891
31892         // private
31893     onClick : function(e){ 
31894         //this.setChecked(!this.checked);
31895         Roo.get(e.target).toggleClass('x-menu-item-checked');
31896         this.refreshValue();
31897         //if(this.el.dom.checked != this.checked){
31898         //    this.setValue(this.el.dom.checked);
31899        // }
31900     },
31901     
31902     // private
31903     refreshValue : function()
31904     {
31905         var val = '';
31906         this.viewEl.select('img',true).each(function(e,i,n)  {
31907             val += e.is(".x-menu-item-checked") ? String(n) : '';
31908         });
31909         this.setValue(val, true);
31910     },
31911
31912     /**
31913      * Sets the checked state of the checkbox.
31914      * On is always based on a string comparison between inputValue and the param.
31915      * @param {Boolean/String} value - the value to set 
31916      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31917      */
31918     setValue : function(v,suppressEvent){
31919         if (!this.el.dom) {
31920             return;
31921         }
31922         var old = this.el.dom.value ;
31923         this.el.dom.value = v;
31924         if (suppressEvent) {
31925             return ;
31926         }
31927          
31928         // update display..
31929         this.viewEl.select('img',true).each(function(e,i,n)  {
31930             
31931             var on = e.is(".x-menu-item-checked");
31932             var newv = v.indexOf(String(n)) > -1;
31933             if (on != newv) {
31934                 e.toggleClass('x-menu-item-checked');
31935             }
31936             
31937         });
31938         
31939         
31940         this.fireEvent('change', this, v, old);
31941         
31942         
31943     },
31944    
31945     // handle setting of hidden value by some other method!!?!?
31946     setFromHidden: function()
31947     {
31948         if(!this.el){
31949             return;
31950         }
31951         //console.log("SET FROM HIDDEN");
31952         //alert('setFrom hidden');
31953         this.setValue(this.el.dom.value);
31954     },
31955     
31956     onDestroy : function()
31957     {
31958         if(this.viewEl){
31959             Roo.get(this.viewEl).remove();
31960         }
31961          
31962         Roo.form.DayPicker.superclass.onDestroy.call(this);
31963     }
31964
31965 });/*
31966  * RooJS Library 1.1.1
31967  * Copyright(c) 2008-2011  Alan Knowles
31968  *
31969  * License - LGPL
31970  */
31971  
31972
31973 /**
31974  * @class Roo.form.ComboCheck
31975  * @extends Roo.form.ComboBox
31976  * A combobox for multiple select items.
31977  *
31978  * FIXME - could do with a reset button..
31979  * 
31980  * @constructor
31981  * Create a new ComboCheck
31982  * @param {Object} config Configuration options
31983  */
31984 Roo.form.ComboCheck = function(config){
31985     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31986     // should verify some data...
31987     // like
31988     // hiddenName = required..
31989     // displayField = required
31990     // valudField == required
31991     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31992     var _t = this;
31993     Roo.each(req, function(e) {
31994         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31995             throw "Roo.form.ComboCheck : missing value for: " + e;
31996         }
31997     });
31998     
31999     
32000 };
32001
32002 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
32003      
32004      
32005     editable : false,
32006      
32007     selectedClass: 'x-menu-item-checked', 
32008     
32009     // private
32010     onRender : function(ct, position){
32011         var _t = this;
32012         
32013         
32014         
32015         if(!this.tpl){
32016             var cls = 'x-combo-list';
32017
32018             
32019             this.tpl =  new Roo.Template({
32020                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
32021                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
32022                    '<span>{' + this.displayField + '}</span>' +
32023                     '</div>' 
32024                 
32025             });
32026         }
32027  
32028         
32029         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
32030         this.view.singleSelect = false;
32031         this.view.multiSelect = true;
32032         this.view.toggleSelect = true;
32033         this.pageTb.add(new Roo.Toolbar.Fill(), {
32034             
32035             text: 'Done',
32036             handler: function()
32037             {
32038                 _t.collapse();
32039             }
32040         });
32041     },
32042     
32043     onViewOver : function(e, t){
32044         // do nothing...
32045         return;
32046         
32047     },
32048     
32049     onViewClick : function(doFocus,index){
32050         return;
32051         
32052     },
32053     select: function () {
32054         //Roo.log("SELECT CALLED");
32055     },
32056      
32057     selectByValue : function(xv, scrollIntoView){
32058         var ar = this.getValueArray();
32059         var sels = [];
32060         
32061         Roo.each(ar, function(v) {
32062             if(v === undefined || v === null){
32063                 return;
32064             }
32065             var r = this.findRecord(this.valueField, v);
32066             if(r){
32067                 sels.push(this.store.indexOf(r))
32068                 
32069             }
32070         },this);
32071         this.view.select(sels);
32072         return false;
32073     },
32074     
32075     
32076     
32077     onSelect : function(record, index){
32078        // Roo.log("onselect Called");
32079        // this is only called by the clear button now..
32080         this.view.clearSelections();
32081         this.setValue('[]');
32082         if (this.value != this.valueBefore) {
32083             this.fireEvent('change', this, this.value, this.valueBefore);
32084             this.valueBefore = this.value;
32085         }
32086     },
32087     getValueArray : function()
32088     {
32089         var ar = [] ;
32090         
32091         try {
32092             //Roo.log(this.value);
32093             if (typeof(this.value) == 'undefined') {
32094                 return [];
32095             }
32096             var ar = Roo.decode(this.value);
32097             return  ar instanceof Array ? ar : []; //?? valid?
32098             
32099         } catch(e) {
32100             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
32101             return [];
32102         }
32103          
32104     },
32105     expand : function ()
32106     {
32107         
32108         Roo.form.ComboCheck.superclass.expand.call(this);
32109         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
32110         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
32111         
32112
32113     },
32114     
32115     collapse : function(){
32116         Roo.form.ComboCheck.superclass.collapse.call(this);
32117         var sl = this.view.getSelectedIndexes();
32118         var st = this.store;
32119         var nv = [];
32120         var tv = [];
32121         var r;
32122         Roo.each(sl, function(i) {
32123             r = st.getAt(i);
32124             nv.push(r.get(this.valueField));
32125         },this);
32126         this.setValue(Roo.encode(nv));
32127         if (this.value != this.valueBefore) {
32128
32129             this.fireEvent('change', this, this.value, this.valueBefore);
32130             this.valueBefore = this.value;
32131         }
32132         
32133     },
32134     
32135     setValue : function(v){
32136         // Roo.log(v);
32137         this.value = v;
32138         
32139         var vals = this.getValueArray();
32140         var tv = [];
32141         Roo.each(vals, function(k) {
32142             var r = this.findRecord(this.valueField, k);
32143             if(r){
32144                 tv.push(r.data[this.displayField]);
32145             }else if(this.valueNotFoundText !== undefined){
32146                 tv.push( this.valueNotFoundText );
32147             }
32148         },this);
32149        // Roo.log(tv);
32150         
32151         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
32152         this.hiddenField.value = v;
32153         this.value = v;
32154     }
32155     
32156 });/*
32157  * Based on:
32158  * Ext JS Library 1.1.1
32159  * Copyright(c) 2006-2007, Ext JS, LLC.
32160  *
32161  * Originally Released Under LGPL - original licence link has changed is not relivant.
32162  *
32163  * Fork - LGPL
32164  * <script type="text/javascript">
32165  */
32166  
32167 /**
32168  * @class Roo.form.Signature
32169  * @extends Roo.form.Field
32170  * Signature field.  
32171  * @constructor
32172  * 
32173  * @param {Object} config Configuration options
32174  */
32175
32176 Roo.form.Signature = function(config){
32177     Roo.form.Signature.superclass.constructor.call(this, config);
32178     
32179     this.addEvents({// not in used??
32180          /**
32181          * @event confirm
32182          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
32183              * @param {Roo.form.Signature} combo This combo box
32184              */
32185         'confirm' : true,
32186         /**
32187          * @event reset
32188          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
32189              * @param {Roo.form.ComboBox} combo This combo box
32190              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
32191              */
32192         'reset' : true
32193     });
32194 };
32195
32196 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
32197     /**
32198      * @cfg {Object} labels Label to use when rendering a form.
32199      * defaults to 
32200      * labels : { 
32201      *      clear : "Clear",
32202      *      confirm : "Confirm"
32203      *  }
32204      */
32205     labels : { 
32206         clear : "Clear",
32207         confirm : "Confirm"
32208     },
32209     /**
32210      * @cfg {Number} width The signature panel width (defaults to 300)
32211      */
32212     width: 300,
32213     /**
32214      * @cfg {Number} height The signature panel height (defaults to 100)
32215      */
32216     height : 100,
32217     /**
32218      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32219      */
32220     allowBlank : false,
32221     
32222     //private
32223     // {Object} signPanel The signature SVG panel element (defaults to {})
32224     signPanel : {},
32225     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32226     isMouseDown : false,
32227     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32228     isConfirmed : false,
32229     // {String} signatureTmp SVG mapping string (defaults to empty string)
32230     signatureTmp : '',
32231     
32232     
32233     defaultAutoCreate : { // modified by initCompnoent..
32234         tag: "input",
32235         type:"hidden"
32236     },
32237
32238     // private
32239     onRender : function(ct, position){
32240         
32241         Roo.form.Signature.superclass.onRender.call(this, ct, position);
32242         
32243         this.wrap = this.el.wrap({
32244             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32245         });
32246         
32247         this.createToolbar(this);
32248         this.signPanel = this.wrap.createChild({
32249                 tag: 'div',
32250                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32251             }, this.el
32252         );
32253             
32254         this.svgID = Roo.id();
32255         this.svgEl = this.signPanel.createChild({
32256               xmlns : 'http://www.w3.org/2000/svg',
32257               tag : 'svg',
32258               id : this.svgID + "-svg",
32259               width: this.width,
32260               height: this.height,
32261               viewBox: '0 0 '+this.width+' '+this.height,
32262               cn : [
32263                 {
32264                     tag: "rect",
32265                     id: this.svgID + "-svg-r",
32266                     width: this.width,
32267                     height: this.height,
32268                     fill: "#ffa"
32269                 },
32270                 {
32271                     tag: "line",
32272                     id: this.svgID + "-svg-l",
32273                     x1: "0", // start
32274                     y1: (this.height*0.8), // start set the line in 80% of height
32275                     x2: this.width, // end
32276                     y2: (this.height*0.8), // end set the line in 80% of height
32277                     'stroke': "#666",
32278                     'stroke-width': "1",
32279                     'stroke-dasharray': "3",
32280                     'shape-rendering': "crispEdges",
32281                     'pointer-events': "none"
32282                 },
32283                 {
32284                     tag: "path",
32285                     id: this.svgID + "-svg-p",
32286                     'stroke': "navy",
32287                     'stroke-width': "3",
32288                     'fill': "none",
32289                     'pointer-events': 'none'
32290                 }
32291               ]
32292         });
32293         this.createSVG();
32294         this.svgBox = this.svgEl.dom.getScreenCTM();
32295     },
32296     createSVG : function(){ 
32297         var svg = this.signPanel;
32298         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32299         var t = this;
32300
32301         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32302         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32303         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32304         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32305         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32306         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32307         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32308         
32309     },
32310     isTouchEvent : function(e){
32311         return e.type.match(/^touch/);
32312     },
32313     getCoords : function (e) {
32314         var pt    = this.svgEl.dom.createSVGPoint();
32315         pt.x = e.clientX; 
32316         pt.y = e.clientY;
32317         if (this.isTouchEvent(e)) {
32318             pt.x =  e.targetTouches[0].clientX;
32319             pt.y = e.targetTouches[0].clientY;
32320         }
32321         var a = this.svgEl.dom.getScreenCTM();
32322         var b = a.inverse();
32323         var mx = pt.matrixTransform(b);
32324         return mx.x + ',' + mx.y;
32325     },
32326     //mouse event headler 
32327     down : function (e) {
32328         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32329         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32330         
32331         this.isMouseDown = true;
32332         
32333         e.preventDefault();
32334     },
32335     move : function (e) {
32336         if (this.isMouseDown) {
32337             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32338             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32339         }
32340         
32341         e.preventDefault();
32342     },
32343     up : function (e) {
32344         this.isMouseDown = false;
32345         var sp = this.signatureTmp.split(' ');
32346         
32347         if(sp.length > 1){
32348             if(!sp[sp.length-2].match(/^L/)){
32349                 sp.pop();
32350                 sp.pop();
32351                 sp.push("");
32352                 this.signatureTmp = sp.join(" ");
32353             }
32354         }
32355         if(this.getValue() != this.signatureTmp){
32356             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32357             this.isConfirmed = false;
32358         }
32359         e.preventDefault();
32360     },
32361     
32362     /**
32363      * Protected method that will not generally be called directly. It
32364      * is called when the editor creates its toolbar. Override this method if you need to
32365      * add custom toolbar buttons.
32366      * @param {HtmlEditor} editor
32367      */
32368     createToolbar : function(editor){
32369          function btn(id, toggle, handler){
32370             var xid = fid + '-'+ id ;
32371             return {
32372                 id : xid,
32373                 cmd : id,
32374                 cls : 'x-btn-icon x-edit-'+id,
32375                 enableToggle:toggle !== false,
32376                 scope: editor, // was editor...
32377                 handler:handler||editor.relayBtnCmd,
32378                 clickEvent:'mousedown',
32379                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32380                 tabIndex:-1
32381             };
32382         }
32383         
32384         
32385         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32386         this.tb = tb;
32387         this.tb.add(
32388            {
32389                 cls : ' x-signature-btn x-signature-'+id,
32390                 scope: editor, // was editor...
32391                 handler: this.reset,
32392                 clickEvent:'mousedown',
32393                 text: this.labels.clear
32394             },
32395             {
32396                  xtype : 'Fill',
32397                  xns: Roo.Toolbar
32398             }, 
32399             {
32400                 cls : '  x-signature-btn x-signature-'+id,
32401                 scope: editor, // was editor...
32402                 handler: this.confirmHandler,
32403                 clickEvent:'mousedown',
32404                 text: this.labels.confirm
32405             }
32406         );
32407     
32408     },
32409     //public
32410     /**
32411      * when user is clicked confirm then show this image.....
32412      * 
32413      * @return {String} Image Data URI
32414      */
32415     getImageDataURI : function(){
32416         var svg = this.svgEl.dom.parentNode.innerHTML;
32417         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32418         return src; 
32419     },
32420     /**
32421      * 
32422      * @return {Boolean} this.isConfirmed
32423      */
32424     getConfirmed : function(){
32425         return this.isConfirmed;
32426     },
32427     /**
32428      * 
32429      * @return {Number} this.width
32430      */
32431     getWidth : function(){
32432         return this.width;
32433     },
32434     /**
32435      * 
32436      * @return {Number} this.height
32437      */
32438     getHeight : function(){
32439         return this.height;
32440     },
32441     // private
32442     getSignature : function(){
32443         return this.signatureTmp;
32444     },
32445     // private
32446     reset : function(){
32447         this.signatureTmp = '';
32448         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32449         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32450         this.isConfirmed = false;
32451         Roo.form.Signature.superclass.reset.call(this);
32452     },
32453     setSignature : function(s){
32454         this.signatureTmp = s;
32455         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32456         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32457         this.setValue(s);
32458         this.isConfirmed = false;
32459         Roo.form.Signature.superclass.reset.call(this);
32460     }, 
32461     test : function(){
32462 //        Roo.log(this.signPanel.dom.contentWindow.up())
32463     },
32464     //private
32465     setConfirmed : function(){
32466         
32467         
32468         
32469 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32470     },
32471     // private
32472     confirmHandler : function(){
32473         if(!this.getSignature()){
32474             return;
32475         }
32476         
32477         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32478         this.setValue(this.getSignature());
32479         this.isConfirmed = true;
32480         
32481         this.fireEvent('confirm', this);
32482     },
32483     // private
32484     // Subclasses should provide the validation implementation by overriding this
32485     validateValue : function(value){
32486         if(this.allowBlank){
32487             return true;
32488         }
32489         
32490         if(this.isConfirmed){
32491             return true;
32492         }
32493         return false;
32494     }
32495 });/*
32496  * Based on:
32497  * Ext JS Library 1.1.1
32498  * Copyright(c) 2006-2007, Ext JS, LLC.
32499  *
32500  * Originally Released Under LGPL - original licence link has changed is not relivant.
32501  *
32502  * Fork - LGPL
32503  * <script type="text/javascript">
32504  */
32505  
32506
32507 /**
32508  * @class Roo.form.ComboBox
32509  * @extends Roo.form.TriggerField
32510  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32511  * @constructor
32512  * Create a new ComboBox.
32513  * @param {Object} config Configuration options
32514  */
32515 Roo.form.Select = function(config){
32516     Roo.form.Select.superclass.constructor.call(this, config);
32517      
32518 };
32519
32520 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32521     /**
32522      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32523      */
32524     /**
32525      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32526      * rendering into an Roo.Editor, defaults to false)
32527      */
32528     /**
32529      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32530      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32531      */
32532     /**
32533      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32534      */
32535     /**
32536      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32537      * the dropdown list (defaults to undefined, with no header element)
32538      */
32539
32540      /**
32541      * @cfg {String/Roo.Template} tpl The template to use to render the output
32542      */
32543      
32544     // private
32545     defaultAutoCreate : {tag: "select"  },
32546     /**
32547      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32548      */
32549     listWidth: undefined,
32550     /**
32551      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32552      * mode = 'remote' or 'text' if mode = 'local')
32553      */
32554     displayField: undefined,
32555     /**
32556      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32557      * mode = 'remote' or 'value' if mode = 'local'). 
32558      * Note: use of a valueField requires the user make a selection
32559      * in order for a value to be mapped.
32560      */
32561     valueField: undefined,
32562     
32563     
32564     /**
32565      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32566      * field's data value (defaults to the underlying DOM element's name)
32567      */
32568     hiddenName: undefined,
32569     /**
32570      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32571      */
32572     listClass: '',
32573     /**
32574      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32575      */
32576     selectedClass: 'x-combo-selected',
32577     /**
32578      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
32579      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32580      * which displays a downward arrow icon).
32581      */
32582     triggerClass : 'x-form-arrow-trigger',
32583     /**
32584      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32585      */
32586     shadow:'sides',
32587     /**
32588      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32589      * anchor positions (defaults to 'tl-bl')
32590      */
32591     listAlign: 'tl-bl?',
32592     /**
32593      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32594      */
32595     maxHeight: 300,
32596     /**
32597      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
32598      * query specified by the allQuery config option (defaults to 'query')
32599      */
32600     triggerAction: 'query',
32601     /**
32602      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32603      * (defaults to 4, does not apply if editable = false)
32604      */
32605     minChars : 4,
32606     /**
32607      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32608      * delay (typeAheadDelay) if it matches a known value (defaults to false)
32609      */
32610     typeAhead: false,
32611     /**
32612      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32613      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32614      */
32615     queryDelay: 500,
32616     /**
32617      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32618      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
32619      */
32620     pageSize: 0,
32621     /**
32622      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
32623      * when editable = true (defaults to false)
32624      */
32625     selectOnFocus:false,
32626     /**
32627      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32628      */
32629     queryParam: 'query',
32630     /**
32631      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
32632      * when mode = 'remote' (defaults to 'Loading...')
32633      */
32634     loadingText: 'Loading...',
32635     /**
32636      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32637      */
32638     resizable: false,
32639     /**
32640      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32641      */
32642     handleHeight : 8,
32643     /**
32644      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32645      * traditional select (defaults to true)
32646      */
32647     editable: true,
32648     /**
32649      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32650      */
32651     allQuery: '',
32652     /**
32653      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32654      */
32655     mode: 'remote',
32656     /**
32657      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32658      * listWidth has a higher value)
32659      */
32660     minListWidth : 70,
32661     /**
32662      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32663      * allow the user to set arbitrary text into the field (defaults to false)
32664      */
32665     forceSelection:false,
32666     /**
32667      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32668      * if typeAhead = true (defaults to 250)
32669      */
32670     typeAheadDelay : 250,
32671     /**
32672      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32673      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32674      */
32675     valueNotFoundText : undefined,
32676     
32677     /**
32678      * @cfg {String} defaultValue The value displayed after loading the store.
32679      */
32680     defaultValue: '',
32681     
32682     /**
32683      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32684      */
32685     blockFocus : false,
32686     
32687     /**
32688      * @cfg {Boolean} disableClear Disable showing of clear button.
32689      */
32690     disableClear : false,
32691     /**
32692      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
32693      */
32694     alwaysQuery : false,
32695     
32696     //private
32697     addicon : false,
32698     editicon: false,
32699     
32700     // element that contains real text value.. (when hidden is used..)
32701      
32702     // private
32703     onRender : function(ct, position){
32704         Roo.form.Field.prototype.onRender.call(this, ct, position);
32705         
32706         if(this.store){
32707             this.store.on('beforeload', this.onBeforeLoad, this);
32708             this.store.on('load', this.onLoad, this);
32709             this.store.on('loadexception', this.onLoadException, this);
32710             this.store.load({});
32711         }
32712         
32713         
32714         
32715     },
32716
32717     // private
32718     initEvents : function(){
32719         //Roo.form.ComboBox.superclass.initEvents.call(this);
32720  
32721     },
32722
32723     onDestroy : function(){
32724        
32725         if(this.store){
32726             this.store.un('beforeload', this.onBeforeLoad, this);
32727             this.store.un('load', this.onLoad, this);
32728             this.store.un('loadexception', this.onLoadException, this);
32729         }
32730         //Roo.form.ComboBox.superclass.onDestroy.call(this);
32731     },
32732
32733     // private
32734     fireKey : function(e){
32735         if(e.isNavKeyPress() && !this.list.isVisible()){
32736             this.fireEvent("specialkey", this, e);
32737         }
32738     },
32739
32740     // private
32741     onResize: function(w, h){
32742         
32743         return; 
32744     
32745         
32746     },
32747
32748     /**
32749      * Allow or prevent the user from directly editing the field text.  If false is passed,
32750      * the user will only be able to select from the items defined in the dropdown list.  This method
32751      * is the runtime equivalent of setting the 'editable' config option at config time.
32752      * @param {Boolean} value True to allow the user to directly edit the field text
32753      */
32754     setEditable : function(value){
32755          
32756     },
32757
32758     // private
32759     onBeforeLoad : function(){
32760         
32761         Roo.log("Select before load");
32762         return;
32763     
32764         this.innerList.update(this.loadingText ?
32765                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32766         //this.restrictHeight();
32767         this.selectedIndex = -1;
32768     },
32769
32770     // private
32771     onLoad : function(){
32772
32773     
32774         var dom = this.el.dom;
32775         dom.innerHTML = '';
32776          var od = dom.ownerDocument;
32777          
32778         if (this.emptyText) {
32779             var op = od.createElement('option');
32780             op.setAttribute('value', '');
32781             op.innerHTML = String.format('{0}', this.emptyText);
32782             dom.appendChild(op);
32783         }
32784         if(this.store.getCount() > 0){
32785            
32786             var vf = this.valueField;
32787             var df = this.displayField;
32788             this.store.data.each(function(r) {
32789                 // which colmsn to use... testing - cdoe / title..
32790                 var op = od.createElement('option');
32791                 op.setAttribute('value', r.data[vf]);
32792                 op.innerHTML = String.format('{0}', r.data[df]);
32793                 dom.appendChild(op);
32794             });
32795             if (typeof(this.defaultValue != 'undefined')) {
32796                 this.setValue(this.defaultValue);
32797             }
32798             
32799              
32800         }else{
32801             //this.onEmptyResults();
32802         }
32803         //this.el.focus();
32804     },
32805     // private
32806     onLoadException : function()
32807     {
32808         dom.innerHTML = '';
32809             
32810         Roo.log("Select on load exception");
32811         return;
32812     
32813         this.collapse();
32814         Roo.log(this.store.reader.jsonData);
32815         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32816             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32817         }
32818         
32819         
32820     },
32821     // private
32822     onTypeAhead : function(){
32823          
32824     },
32825
32826     // private
32827     onSelect : function(record, index){
32828         Roo.log('on select?');
32829         return;
32830         if(this.fireEvent('beforeselect', this, record, index) !== false){
32831             this.setFromData(index > -1 ? record.data : false);
32832             this.collapse();
32833             this.fireEvent('select', this, record, index);
32834         }
32835     },
32836
32837     /**
32838      * Returns the currently selected field value or empty string if no value is set.
32839      * @return {String} value The selected value
32840      */
32841     getValue : function(){
32842         var dom = this.el.dom;
32843         this.value = dom.options[dom.selectedIndex].value;
32844         return this.value;
32845         
32846     },
32847
32848     /**
32849      * Clears any text/value currently set in the field
32850      */
32851     clearValue : function(){
32852         this.value = '';
32853         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32854         
32855     },
32856
32857     /**
32858      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32859      * will be displayed in the field.  If the value does not match the data value of an existing item,
32860      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32861      * Otherwise the field will be blank (although the value will still be set).
32862      * @param {String} value The value to match
32863      */
32864     setValue : function(v){
32865         var d = this.el.dom;
32866         for (var i =0; i < d.options.length;i++) {
32867             if (v == d.options[i].value) {
32868                 d.selectedIndex = i;
32869                 this.value = v;
32870                 return;
32871             }
32872         }
32873         this.clearValue();
32874     },
32875     /**
32876      * @property {Object} the last set data for the element
32877      */
32878     
32879     lastData : false,
32880     /**
32881      * Sets the value of the field based on a object which is related to the record format for the store.
32882      * @param {Object} value the value to set as. or false on reset?
32883      */
32884     setFromData : function(o){
32885         Roo.log('setfrom data?');
32886          
32887         
32888         
32889     },
32890     // private
32891     reset : function(){
32892         this.clearValue();
32893     },
32894     // private
32895     findRecord : function(prop, value){
32896         
32897         return false;
32898     
32899         var record;
32900         if(this.store.getCount() > 0){
32901             this.store.each(function(r){
32902                 if(r.data[prop] == value){
32903                     record = r;
32904                     return false;
32905                 }
32906                 return true;
32907             });
32908         }
32909         return record;
32910     },
32911     
32912     getName: function()
32913     {
32914         // returns hidden if it's set..
32915         if (!this.rendered) {return ''};
32916         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32917         
32918     },
32919      
32920
32921     
32922
32923     // private
32924     onEmptyResults : function(){
32925         Roo.log('empty results');
32926         //this.collapse();
32927     },
32928
32929     /**
32930      * Returns true if the dropdown list is expanded, else false.
32931      */
32932     isExpanded : function(){
32933         return false;
32934     },
32935
32936     /**
32937      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32938      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32939      * @param {String} value The data value of the item to select
32940      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32941      * selected item if it is not currently in view (defaults to true)
32942      * @return {Boolean} True if the value matched an item in the list, else false
32943      */
32944     selectByValue : function(v, scrollIntoView){
32945         Roo.log('select By Value');
32946         return false;
32947     
32948         if(v !== undefined && v !== null){
32949             var r = this.findRecord(this.valueField || this.displayField, v);
32950             if(r){
32951                 this.select(this.store.indexOf(r), scrollIntoView);
32952                 return true;
32953             }
32954         }
32955         return false;
32956     },
32957
32958     /**
32959      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32960      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32961      * @param {Number} index The zero-based index of the list item to select
32962      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32963      * selected item if it is not currently in view (defaults to true)
32964      */
32965     select : function(index, scrollIntoView){
32966         Roo.log('select ');
32967         return  ;
32968         
32969         this.selectedIndex = index;
32970         this.view.select(index);
32971         if(scrollIntoView !== false){
32972             var el = this.view.getNode(index);
32973             if(el){
32974                 this.innerList.scrollChildIntoView(el, false);
32975             }
32976         }
32977     },
32978
32979       
32980
32981     // private
32982     validateBlur : function(){
32983         
32984         return;
32985         
32986     },
32987
32988     // private
32989     initQuery : function(){
32990         this.doQuery(this.getRawValue());
32991     },
32992
32993     // private
32994     doForce : function(){
32995         if(this.el.dom.value.length > 0){
32996             this.el.dom.value =
32997                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32998              
32999         }
33000     },
33001
33002     /**
33003      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
33004      * query allowing the query action to be canceled if needed.
33005      * @param {String} query The SQL query to execute
33006      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
33007      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
33008      * saved in the current store (defaults to false)
33009      */
33010     doQuery : function(q, forceAll){
33011         
33012         Roo.log('doQuery?');
33013         if(q === undefined || q === null){
33014             q = '';
33015         }
33016         var qe = {
33017             query: q,
33018             forceAll: forceAll,
33019             combo: this,
33020             cancel:false
33021         };
33022         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
33023             return false;
33024         }
33025         q = qe.query;
33026         forceAll = qe.forceAll;
33027         if(forceAll === true || (q.length >= this.minChars)){
33028             if(this.lastQuery != q || this.alwaysQuery){
33029                 this.lastQuery = q;
33030                 if(this.mode == 'local'){
33031                     this.selectedIndex = -1;
33032                     if(forceAll){
33033                         this.store.clearFilter();
33034                     }else{
33035                         this.store.filter(this.displayField, q);
33036                     }
33037                     this.onLoad();
33038                 }else{
33039                     this.store.baseParams[this.queryParam] = q;
33040                     this.store.load({
33041                         params: this.getParams(q)
33042                     });
33043                     this.expand();
33044                 }
33045             }else{
33046                 this.selectedIndex = -1;
33047                 this.onLoad();   
33048             }
33049         }
33050     },
33051
33052     // private
33053     getParams : function(q){
33054         var p = {};
33055         //p[this.queryParam] = q;
33056         if(this.pageSize){
33057             p.start = 0;
33058             p.limit = this.pageSize;
33059         }
33060         return p;
33061     },
33062
33063     /**
33064      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
33065      */
33066     collapse : function(){
33067         
33068     },
33069
33070     // private
33071     collapseIf : function(e){
33072         
33073     },
33074
33075     /**
33076      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
33077      */
33078     expand : function(){
33079         
33080     } ,
33081
33082     // private
33083      
33084
33085     /** 
33086     * @cfg {Boolean} grow 
33087     * @hide 
33088     */
33089     /** 
33090     * @cfg {Number} growMin 
33091     * @hide 
33092     */
33093     /** 
33094     * @cfg {Number} growMax 
33095     * @hide 
33096     */
33097     /**
33098      * @hide
33099      * @method autoSize
33100      */
33101     
33102     setWidth : function()
33103     {
33104         
33105     },
33106     getResizeEl : function(){
33107         return this.el;
33108     }
33109 });//<script type="text/javasscript">
33110  
33111
33112 /**
33113  * @class Roo.DDView
33114  * A DnD enabled version of Roo.View.
33115  * @param {Element/String} container The Element in which to create the View.
33116  * @param {String} tpl The template string used to create the markup for each element of the View
33117  * @param {Object} config The configuration properties. These include all the config options of
33118  * {@link Roo.View} plus some specific to this class.<br>
33119  * <p>
33120  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
33121  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
33122  * <p>
33123  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
33124 .x-view-drag-insert-above {
33125         border-top:1px dotted #3366cc;
33126 }
33127 .x-view-drag-insert-below {
33128         border-bottom:1px dotted #3366cc;
33129 }
33130 </code></pre>
33131  * 
33132  */
33133  
33134 Roo.DDView = function(container, tpl, config) {
33135     Roo.DDView.superclass.constructor.apply(this, arguments);
33136     this.getEl().setStyle("outline", "0px none");
33137     this.getEl().unselectable();
33138     if (this.dragGroup) {
33139         this.setDraggable(this.dragGroup.split(","));
33140     }
33141     if (this.dropGroup) {
33142         this.setDroppable(this.dropGroup.split(","));
33143     }
33144     if (this.deletable) {
33145         this.setDeletable();
33146     }
33147     this.isDirtyFlag = false;
33148         this.addEvents({
33149                 "drop" : true
33150         });
33151 };
33152
33153 Roo.extend(Roo.DDView, Roo.View, {
33154 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
33155 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
33156 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
33157 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
33158
33159         isFormField: true,
33160
33161         reset: Roo.emptyFn,
33162         
33163         clearInvalid: Roo.form.Field.prototype.clearInvalid,
33164
33165         validate: function() {
33166                 return true;
33167         },
33168         
33169         destroy: function() {
33170                 this.purgeListeners();
33171                 this.getEl.removeAllListeners();
33172                 this.getEl().remove();
33173                 if (this.dragZone) {
33174                         if (this.dragZone.destroy) {
33175                                 this.dragZone.destroy();
33176                         }
33177                 }
33178                 if (this.dropZone) {
33179                         if (this.dropZone.destroy) {
33180                                 this.dropZone.destroy();
33181                         }
33182                 }
33183         },
33184
33185 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
33186         getName: function() {
33187                 return this.name;
33188         },
33189
33190 /**     Loads the View from a JSON string representing the Records to put into the Store. */
33191         setValue: function(v) {
33192                 if (!this.store) {
33193                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
33194                 }
33195                 var data = {};
33196                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
33197                 this.store.proxy = new Roo.data.MemoryProxy(data);
33198                 this.store.load();
33199         },
33200
33201 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
33202         getValue: function() {
33203                 var result = '(';
33204                 this.store.each(function(rec) {
33205                         result += rec.id + ',';
33206                 });
33207                 return result.substr(0, result.length - 1) + ')';
33208         },
33209         
33210         getIds: function() {
33211                 var i = 0, result = new Array(this.store.getCount());
33212                 this.store.each(function(rec) {
33213                         result[i++] = rec.id;
33214                 });
33215                 return result;
33216         },
33217         
33218         isDirty: function() {
33219                 return this.isDirtyFlag;
33220         },
33221
33222 /**
33223  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
33224  *      whole Element becomes the target, and this causes the drop gesture to append.
33225  */
33226     getTargetFromEvent : function(e) {
33227                 var target = e.getTarget();
33228                 while ((target !== null) && (target.parentNode != this.el.dom)) {
33229                 target = target.parentNode;
33230                 }
33231                 if (!target) {
33232                         target = this.el.dom.lastChild || this.el.dom;
33233                 }
33234                 return target;
33235     },
33236
33237 /**
33238  *      Create the drag data which consists of an object which has the property "ddel" as
33239  *      the drag proxy element. 
33240  */
33241     getDragData : function(e) {
33242         var target = this.findItemFromChild(e.getTarget());
33243                 if(target) {
33244                         this.handleSelection(e);
33245                         var selNodes = this.getSelectedNodes();
33246             var dragData = {
33247                 source: this,
33248                 copy: this.copy || (this.allowCopy && e.ctrlKey),
33249                 nodes: selNodes,
33250                 records: []
33251                         };
33252                         var selectedIndices = this.getSelectedIndexes();
33253                         for (var i = 0; i < selectedIndices.length; i++) {
33254                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
33255                         }
33256                         if (selNodes.length == 1) {
33257                                 dragData.ddel = target.cloneNode(true); // the div element
33258                         } else {
33259                                 var div = document.createElement('div'); // create the multi element drag "ghost"
33260                                 div.className = 'multi-proxy';
33261                                 for (var i = 0, len = selNodes.length; i < len; i++) {
33262                                         div.appendChild(selNodes[i].cloneNode(true));
33263                                 }
33264                                 dragData.ddel = div;
33265                         }
33266             //console.log(dragData)
33267             //console.log(dragData.ddel.innerHTML)
33268                         return dragData;
33269                 }
33270         //console.log('nodragData')
33271                 return false;
33272     },
33273     
33274 /**     Specify to which ddGroup items in this DDView may be dragged. */
33275     setDraggable: function(ddGroup) {
33276         if (ddGroup instanceof Array) {
33277                 Roo.each(ddGroup, this.setDraggable, this);
33278                 return;
33279         }
33280         if (this.dragZone) {
33281                 this.dragZone.addToGroup(ddGroup);
33282         } else {
33283                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33284                                 containerScroll: true,
33285                                 ddGroup: ddGroup 
33286
33287                         });
33288 //                      Draggability implies selection. DragZone's mousedown selects the element.
33289                         if (!this.multiSelect) { this.singleSelect = true; }
33290
33291 //                      Wire the DragZone's handlers up to methods in *this*
33292                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
33293                 }
33294     },
33295
33296 /**     Specify from which ddGroup this DDView accepts drops. */
33297     setDroppable: function(ddGroup) {
33298         if (ddGroup instanceof Array) {
33299                 Roo.each(ddGroup, this.setDroppable, this);
33300                 return;
33301         }
33302         if (this.dropZone) {
33303                 this.dropZone.addToGroup(ddGroup);
33304         } else {
33305                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33306                                 containerScroll: true,
33307                                 ddGroup: ddGroup
33308                         });
33309
33310 //                      Wire the DropZone's handlers up to methods in *this*
33311                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33312                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33313                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33314                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33315                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33316                 }
33317     },
33318
33319 /**     Decide whether to drop above or below a View node. */
33320     getDropPoint : function(e, n, dd){
33321         if (n == this.el.dom) { return "above"; }
33322                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33323                 var c = t + (b - t) / 2;
33324                 var y = Roo.lib.Event.getPageY(e);
33325                 if(y <= c) {
33326                         return "above";
33327                 }else{
33328                         return "below";
33329                 }
33330     },
33331
33332     onNodeEnter : function(n, dd, e, data){
33333                 return false;
33334     },
33335     
33336     onNodeOver : function(n, dd, e, data){
33337                 var pt = this.getDropPoint(e, n, dd);
33338                 // set the insert point style on the target node
33339                 var dragElClass = this.dropNotAllowed;
33340                 if (pt) {
33341                         var targetElClass;
33342                         if (pt == "above"){
33343                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33344                                 targetElClass = "x-view-drag-insert-above";
33345                         } else {
33346                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33347                                 targetElClass = "x-view-drag-insert-below";
33348                         }
33349                         if (this.lastInsertClass != targetElClass){
33350                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33351                                 this.lastInsertClass = targetElClass;
33352                         }
33353                 }
33354                 return dragElClass;
33355         },
33356
33357     onNodeOut : function(n, dd, e, data){
33358                 this.removeDropIndicators(n);
33359     },
33360
33361     onNodeDrop : function(n, dd, e, data){
33362         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33363                 return false;
33364         }
33365         var pt = this.getDropPoint(e, n, dd);
33366                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33367                 if (pt == "below") { insertAt++; }
33368                 for (var i = 0; i < data.records.length; i++) {
33369                         var r = data.records[i];
33370                         var dup = this.store.getById(r.id);
33371                         if (dup && (dd != this.dragZone)) {
33372                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33373                         } else {
33374                                 if (data.copy) {
33375                                         this.store.insert(insertAt++, r.copy());
33376                                 } else {
33377                                         data.source.isDirtyFlag = true;
33378                                         r.store.remove(r);
33379                                         this.store.insert(insertAt++, r);
33380                                 }
33381                                 this.isDirtyFlag = true;
33382                         }
33383                 }
33384                 this.dragZone.cachedTarget = null;
33385                 return true;
33386     },
33387
33388     removeDropIndicators : function(n){
33389                 if(n){
33390                         Roo.fly(n).removeClass([
33391                                 "x-view-drag-insert-above",
33392                                 "x-view-drag-insert-below"]);
33393                         this.lastInsertClass = "_noclass";
33394                 }
33395     },
33396
33397 /**
33398  *      Utility method. Add a delete option to the DDView's context menu.
33399  *      @param {String} imageUrl The URL of the "delete" icon image.
33400  */
33401         setDeletable: function(imageUrl) {
33402                 if (!this.singleSelect && !this.multiSelect) {
33403                         this.singleSelect = true;
33404                 }
33405                 var c = this.getContextMenu();
33406                 this.contextMenu.on("itemclick", function(item) {
33407                         switch (item.id) {
33408                                 case "delete":
33409                                         this.remove(this.getSelectedIndexes());
33410                                         break;
33411                         }
33412                 }, this);
33413                 this.contextMenu.add({
33414                         icon: imageUrl,
33415                         id: "delete",
33416                         text: 'Delete'
33417                 });
33418         },
33419         
33420 /**     Return the context menu for this DDView. */
33421         getContextMenu: function() {
33422                 if (!this.contextMenu) {
33423 //                      Create the View's context menu
33424                         this.contextMenu = new Roo.menu.Menu({
33425                                 id: this.id + "-contextmenu"
33426                         });
33427                         this.el.on("contextmenu", this.showContextMenu, this);
33428                 }
33429                 return this.contextMenu;
33430         },
33431         
33432         disableContextMenu: function() {
33433                 if (this.contextMenu) {
33434                         this.el.un("contextmenu", this.showContextMenu, this);
33435                 }
33436         },
33437
33438         showContextMenu: function(e, item) {
33439         item = this.findItemFromChild(e.getTarget());
33440                 if (item) {
33441                         e.stopEvent();
33442                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33443                         this.contextMenu.showAt(e.getXY());
33444             }
33445     },
33446
33447 /**
33448  *      Remove {@link Roo.data.Record}s at the specified indices.
33449  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33450  */
33451     remove: function(selectedIndices) {
33452                 selectedIndices = [].concat(selectedIndices);
33453                 for (var i = 0; i < selectedIndices.length; i++) {
33454                         var rec = this.store.getAt(selectedIndices[i]);
33455                         this.store.remove(rec);
33456                 }
33457     },
33458
33459 /**
33460  *      Double click fires the event, but also, if this is draggable, and there is only one other
33461  *      related DropZone, it transfers the selected node.
33462  */
33463     onDblClick : function(e){
33464         var item = this.findItemFromChild(e.getTarget());
33465         if(item){
33466             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33467                 return false;
33468             }
33469             if (this.dragGroup) {
33470                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33471                     while (targets.indexOf(this.dropZone) > -1) {
33472                             targets.remove(this.dropZone);
33473                                 }
33474                     if (targets.length == 1) {
33475                                         this.dragZone.cachedTarget = null;
33476                         var el = Roo.get(targets[0].getEl());
33477                         var box = el.getBox(true);
33478                         targets[0].onNodeDrop(el.dom, {
33479                                 target: el.dom,
33480                                 xy: [box.x, box.y + box.height - 1]
33481                         }, null, this.getDragData(e));
33482                     }
33483                 }
33484         }
33485     },
33486     
33487     handleSelection: function(e) {
33488                 this.dragZone.cachedTarget = null;
33489         var item = this.findItemFromChild(e.getTarget());
33490         if (!item) {
33491                 this.clearSelections(true);
33492                 return;
33493         }
33494                 if (item && (this.multiSelect || this.singleSelect)){
33495                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33496                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33497                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33498                                 this.unselect(item);
33499                         } else {
33500                                 this.select(item, this.multiSelect && e.ctrlKey);
33501                                 this.lastSelection = item;
33502                         }
33503                 }
33504     },
33505
33506     onItemClick : function(item, index, e){
33507                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33508                         return false;
33509                 }
33510                 return true;
33511     },
33512
33513     unselect : function(nodeInfo, suppressEvent){
33514                 var node = this.getNode(nodeInfo);
33515                 if(node && this.isSelected(node)){
33516                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33517                                 Roo.fly(node).removeClass(this.selectedClass);
33518                                 this.selections.remove(node);
33519                                 if(!suppressEvent){
33520                                         this.fireEvent("selectionchange", this, this.selections);
33521                                 }
33522                         }
33523                 }
33524     }
33525 });
33526 /*
33527  * Based on:
33528  * Ext JS Library 1.1.1
33529  * Copyright(c) 2006-2007, Ext JS, LLC.
33530  *
33531  * Originally Released Under LGPL - original licence link has changed is not relivant.
33532  *
33533  * Fork - LGPL
33534  * <script type="text/javascript">
33535  */
33536  
33537 /**
33538  * @class Roo.LayoutManager
33539  * @extends Roo.util.Observable
33540  * Base class for layout managers.
33541  */
33542 Roo.LayoutManager = function(container, config){
33543     Roo.LayoutManager.superclass.constructor.call(this);
33544     this.el = Roo.get(container);
33545     // ie scrollbar fix
33546     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33547         document.body.scroll = "no";
33548     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33549         this.el.position('relative');
33550     }
33551     this.id = this.el.id;
33552     this.el.addClass("x-layout-container");
33553     /** false to disable window resize monitoring @type Boolean */
33554     this.monitorWindowResize = true;
33555     this.regions = {};
33556     this.addEvents({
33557         /**
33558          * @event layout
33559          * Fires when a layout is performed. 
33560          * @param {Roo.LayoutManager} this
33561          */
33562         "layout" : true,
33563         /**
33564          * @event regionresized
33565          * Fires when the user resizes a region. 
33566          * @param {Roo.LayoutRegion} region The resized region
33567          * @param {Number} newSize The new size (width for east/west, height for north/south)
33568          */
33569         "regionresized" : true,
33570         /**
33571          * @event regioncollapsed
33572          * Fires when a region is collapsed. 
33573          * @param {Roo.LayoutRegion} region The collapsed region
33574          */
33575         "regioncollapsed" : true,
33576         /**
33577          * @event regionexpanded
33578          * Fires when a region is expanded.  
33579          * @param {Roo.LayoutRegion} region The expanded region
33580          */
33581         "regionexpanded" : true
33582     });
33583     this.updating = false;
33584     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33585 };
33586
33587 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33588     /**
33589      * Returns true if this layout is currently being updated
33590      * @return {Boolean}
33591      */
33592     isUpdating : function(){
33593         return this.updating; 
33594     },
33595     
33596     /**
33597      * Suspend the LayoutManager from doing auto-layouts while
33598      * making multiple add or remove calls
33599      */
33600     beginUpdate : function(){
33601         this.updating = true;    
33602     },
33603     
33604     /**
33605      * Restore auto-layouts and optionally disable the manager from performing a layout
33606      * @param {Boolean} noLayout true to disable a layout update 
33607      */
33608     endUpdate : function(noLayout){
33609         this.updating = false;
33610         if(!noLayout){
33611             this.layout();
33612         }    
33613     },
33614     
33615     layout: function(){
33616         
33617     },
33618     
33619     onRegionResized : function(region, newSize){
33620         this.fireEvent("regionresized", region, newSize);
33621         this.layout();
33622     },
33623     
33624     onRegionCollapsed : function(region){
33625         this.fireEvent("regioncollapsed", region);
33626     },
33627     
33628     onRegionExpanded : function(region){
33629         this.fireEvent("regionexpanded", region);
33630     },
33631         
33632     /**
33633      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33634      * performs box-model adjustments.
33635      * @return {Object} The size as an object {width: (the width), height: (the height)}
33636      */
33637     getViewSize : function(){
33638         var size;
33639         if(this.el.dom != document.body){
33640             size = this.el.getSize();
33641         }else{
33642             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33643         }
33644         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33645         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33646         return size;
33647     },
33648     
33649     /**
33650      * Returns the Element this layout is bound to.
33651      * @return {Roo.Element}
33652      */
33653     getEl : function(){
33654         return this.el;
33655     },
33656     
33657     /**
33658      * Returns the specified region.
33659      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33660      * @return {Roo.LayoutRegion}
33661      */
33662     getRegion : function(target){
33663         return this.regions[target.toLowerCase()];
33664     },
33665     
33666     onWindowResize : function(){
33667         if(this.monitorWindowResize){
33668             this.layout();
33669         }
33670     }
33671 });/*
33672  * Based on:
33673  * Ext JS Library 1.1.1
33674  * Copyright(c) 2006-2007, Ext JS, LLC.
33675  *
33676  * Originally Released Under LGPL - original licence link has changed is not relivant.
33677  *
33678  * Fork - LGPL
33679  * <script type="text/javascript">
33680  */
33681 /**
33682  * @class Roo.BorderLayout
33683  * @extends Roo.LayoutManager
33684  * @children Roo.ContentPanel
33685  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33686  * please see: <br><br>
33687  * <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>
33688  * <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>
33689  * Example:
33690  <pre><code>
33691  var layout = new Roo.BorderLayout(document.body, {
33692     north: {
33693         initialSize: 25,
33694         titlebar: false
33695     },
33696     west: {
33697         split:true,
33698         initialSize: 200,
33699         minSize: 175,
33700         maxSize: 400,
33701         titlebar: true,
33702         collapsible: true
33703     },
33704     east: {
33705         split:true,
33706         initialSize: 202,
33707         minSize: 175,
33708         maxSize: 400,
33709         titlebar: true,
33710         collapsible: true
33711     },
33712     south: {
33713         split:true,
33714         initialSize: 100,
33715         minSize: 100,
33716         maxSize: 200,
33717         titlebar: true,
33718         collapsible: true
33719     },
33720     center: {
33721         titlebar: true,
33722         autoScroll:true,
33723         resizeTabs: true,
33724         minTabWidth: 50,
33725         preferredTabWidth: 150
33726     }
33727 });
33728
33729 // shorthand
33730 var CP = Roo.ContentPanel;
33731
33732 layout.beginUpdate();
33733 layout.add("north", new CP("north", "North"));
33734 layout.add("south", new CP("south", {title: "South", closable: true}));
33735 layout.add("west", new CP("west", {title: "West"}));
33736 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33737 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33738 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33739 layout.getRegion("center").showPanel("center1");
33740 layout.endUpdate();
33741 </code></pre>
33742
33743 <b>The container the layout is rendered into can be either the body element or any other element.
33744 If it is not the body element, the container needs to either be an absolute positioned element,
33745 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33746 the container size if it is not the body element.</b>
33747
33748 * @constructor
33749 * Create a new BorderLayout
33750 * @param {String/HTMLElement/Element} container The container this layout is bound to
33751 * @param {Object} config Configuration options
33752  */
33753 Roo.BorderLayout = function(container, config){
33754     config = config || {};
33755     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33756     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33757     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33758         var target = this.factory.validRegions[i];
33759         if(config[target]){
33760             this.addRegion(target, config[target]);
33761         }
33762     }
33763 };
33764
33765 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33766         
33767         /**
33768          * @cfg {Roo.LayoutRegion} east
33769          */
33770         /**
33771          * @cfg {Roo.LayoutRegion} west
33772          */
33773         /**
33774          * @cfg {Roo.LayoutRegion} north
33775          */
33776         /**
33777          * @cfg {Roo.LayoutRegion} south
33778          */
33779         /**
33780          * @cfg {Roo.LayoutRegion} center
33781          */
33782     /**
33783      * Creates and adds a new region if it doesn't already exist.
33784      * @param {String} target The target region key (north, south, east, west or center).
33785      * @param {Object} config The regions config object
33786      * @return {BorderLayoutRegion} The new region
33787      */
33788     addRegion : function(target, config){
33789         if(!this.regions[target]){
33790             var r = this.factory.create(target, this, config);
33791             this.bindRegion(target, r);
33792         }
33793         return this.regions[target];
33794     },
33795
33796     // private (kinda)
33797     bindRegion : function(name, r){
33798         this.regions[name] = r;
33799         r.on("visibilitychange", this.layout, this);
33800         r.on("paneladded", this.layout, this);
33801         r.on("panelremoved", this.layout, this);
33802         r.on("invalidated", this.layout, this);
33803         r.on("resized", this.onRegionResized, this);
33804         r.on("collapsed", this.onRegionCollapsed, this);
33805         r.on("expanded", this.onRegionExpanded, this);
33806     },
33807
33808     /**
33809      * Performs a layout update.
33810      */
33811     layout : function(){
33812         if(this.updating) {
33813             return;
33814         }
33815         var size = this.getViewSize();
33816         var w = size.width;
33817         var h = size.height;
33818         var centerW = w;
33819         var centerH = h;
33820         var centerY = 0;
33821         var centerX = 0;
33822         //var x = 0, y = 0;
33823
33824         var rs = this.regions;
33825         var north = rs["north"];
33826         var south = rs["south"]; 
33827         var west = rs["west"];
33828         var east = rs["east"];
33829         var center = rs["center"];
33830         //if(this.hideOnLayout){ // not supported anymore
33831             //c.el.setStyle("display", "none");
33832         //}
33833         if(north && north.isVisible()){
33834             var b = north.getBox();
33835             var m = north.getMargins();
33836             b.width = w - (m.left+m.right);
33837             b.x = m.left;
33838             b.y = m.top;
33839             centerY = b.height + b.y + m.bottom;
33840             centerH -= centerY;
33841             north.updateBox(this.safeBox(b));
33842         }
33843         if(south && south.isVisible()){
33844             var b = south.getBox();
33845             var m = south.getMargins();
33846             b.width = w - (m.left+m.right);
33847             b.x = m.left;
33848             var totalHeight = (b.height + m.top + m.bottom);
33849             b.y = h - totalHeight + m.top;
33850             centerH -= totalHeight;
33851             south.updateBox(this.safeBox(b));
33852         }
33853         if(west && west.isVisible()){
33854             var b = west.getBox();
33855             var m = west.getMargins();
33856             b.height = centerH - (m.top+m.bottom);
33857             b.x = m.left;
33858             b.y = centerY + m.top;
33859             var totalWidth = (b.width + m.left + m.right);
33860             centerX += totalWidth;
33861             centerW -= totalWidth;
33862             west.updateBox(this.safeBox(b));
33863         }
33864         if(east && east.isVisible()){
33865             var b = east.getBox();
33866             var m = east.getMargins();
33867             b.height = centerH - (m.top+m.bottom);
33868             var totalWidth = (b.width + m.left + m.right);
33869             b.x = w - totalWidth + m.left;
33870             b.y = centerY + m.top;
33871             centerW -= totalWidth;
33872             east.updateBox(this.safeBox(b));
33873         }
33874         if(center){
33875             var m = center.getMargins();
33876             var centerBox = {
33877                 x: centerX + m.left,
33878                 y: centerY + m.top,
33879                 width: centerW - (m.left+m.right),
33880                 height: centerH - (m.top+m.bottom)
33881             };
33882             //if(this.hideOnLayout){
33883                 //center.el.setStyle("display", "block");
33884             //}
33885             center.updateBox(this.safeBox(centerBox));
33886         }
33887         this.el.repaint();
33888         this.fireEvent("layout", this);
33889     },
33890
33891     // private
33892     safeBox : function(box){
33893         box.width = Math.max(0, box.width);
33894         box.height = Math.max(0, box.height);
33895         return box;
33896     },
33897
33898     /**
33899      * Adds a ContentPanel (or subclass) to this layout.
33900      * @param {String} target The target region key (north, south, east, west or center).
33901      * @param {Roo.ContentPanel} panel The panel to add
33902      * @return {Roo.ContentPanel} The added panel
33903      */
33904     add : function(target, panel){
33905          
33906         target = target.toLowerCase();
33907         return this.regions[target].add(panel);
33908     },
33909
33910     /**
33911      * Remove a ContentPanel (or subclass) to this layout.
33912      * @param {String} target The target region key (north, south, east, west or center).
33913      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33914      * @return {Roo.ContentPanel} The removed panel
33915      */
33916     remove : function(target, panel){
33917         target = target.toLowerCase();
33918         return this.regions[target].remove(panel);
33919     },
33920
33921     /**
33922      * Searches all regions for a panel with the specified id
33923      * @param {String} panelId
33924      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33925      */
33926     findPanel : function(panelId){
33927         var rs = this.regions;
33928         for(var target in rs){
33929             if(typeof rs[target] != "function"){
33930                 var p = rs[target].getPanel(panelId);
33931                 if(p){
33932                     return p;
33933                 }
33934             }
33935         }
33936         return null;
33937     },
33938
33939     /**
33940      * Searches all regions for a panel with the specified id and activates (shows) it.
33941      * @param {String/ContentPanel} panelId The panels id or the panel itself
33942      * @return {Roo.ContentPanel} The shown panel or null
33943      */
33944     showPanel : function(panelId) {
33945       var rs = this.regions;
33946       for(var target in rs){
33947          var r = rs[target];
33948          if(typeof r != "function"){
33949             if(r.hasPanel(panelId)){
33950                return r.showPanel(panelId);
33951             }
33952          }
33953       }
33954       return null;
33955    },
33956
33957    /**
33958      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33959      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33960      */
33961     restoreState : function(provider){
33962         if(!provider){
33963             provider = Roo.state.Manager;
33964         }
33965         var sm = new Roo.LayoutStateManager();
33966         sm.init(this, provider);
33967     },
33968
33969     /**
33970      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33971      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33972      * a valid ContentPanel config object.  Example:
33973      * <pre><code>
33974 // Create the main layout
33975 var layout = new Roo.BorderLayout('main-ct', {
33976     west: {
33977         split:true,
33978         minSize: 175,
33979         titlebar: true
33980     },
33981     center: {
33982         title:'Components'
33983     }
33984 }, 'main-ct');
33985
33986 // Create and add multiple ContentPanels at once via configs
33987 layout.batchAdd({
33988    west: {
33989        id: 'source-files',
33990        autoCreate:true,
33991        title:'Ext Source Files',
33992        autoScroll:true,
33993        fitToFrame:true
33994    },
33995    center : {
33996        el: cview,
33997        autoScroll:true,
33998        fitToFrame:true,
33999        toolbar: tb,
34000        resizeEl:'cbody'
34001    }
34002 });
34003 </code></pre>
34004      * @param {Object} regions An object containing ContentPanel configs by region name
34005      */
34006     batchAdd : function(regions){
34007         this.beginUpdate();
34008         for(var rname in regions){
34009             var lr = this.regions[rname];
34010             if(lr){
34011                 this.addTypedPanels(lr, regions[rname]);
34012             }
34013         }
34014         this.endUpdate();
34015     },
34016
34017     // private
34018     addTypedPanels : function(lr, ps){
34019         if(typeof ps == 'string'){
34020             lr.add(new Roo.ContentPanel(ps));
34021         }
34022         else if(ps instanceof Array){
34023             for(var i =0, len = ps.length; i < len; i++){
34024                 this.addTypedPanels(lr, ps[i]);
34025             }
34026         }
34027         else if(!ps.events){ // raw config?
34028             var el = ps.el;
34029             delete ps.el; // prevent conflict
34030             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
34031         }
34032         else {  // panel object assumed!
34033             lr.add(ps);
34034         }
34035     },
34036     /**
34037      * Adds a xtype elements to the layout.
34038      * <pre><code>
34039
34040 layout.addxtype({
34041        xtype : 'ContentPanel',
34042        region: 'west',
34043        items: [ .... ]
34044    }
34045 );
34046
34047 layout.addxtype({
34048         xtype : 'NestedLayoutPanel',
34049         region: 'west',
34050         layout: {
34051            center: { },
34052            west: { }   
34053         },
34054         items : [ ... list of content panels or nested layout panels.. ]
34055    }
34056 );
34057 </code></pre>
34058      * @param {Object} cfg Xtype definition of item to add.
34059      */
34060     addxtype : function(cfg)
34061     {
34062         // basically accepts a pannel...
34063         // can accept a layout region..!?!?
34064         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34065         
34066         if (!cfg.xtype.match(/Panel$/)) {
34067             return false;
34068         }
34069         var ret = false;
34070         
34071         if (typeof(cfg.region) == 'undefined') {
34072             Roo.log("Failed to add Panel, region was not set");
34073             Roo.log(cfg);
34074             return false;
34075         }
34076         var region = cfg.region;
34077         delete cfg.region;
34078         
34079           
34080         var xitems = [];
34081         if (cfg.items) {
34082             xitems = cfg.items;
34083             delete cfg.items;
34084         }
34085         var nb = false;
34086         
34087         switch(cfg.xtype) 
34088         {
34089             case 'ContentPanel':  // ContentPanel (el, cfg)
34090             case 'ScrollPanel':  // ContentPanel (el, cfg)
34091             case 'ViewPanel': 
34092                 if(cfg.autoCreate) {
34093                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34094                 } else {
34095                     var el = this.el.createChild();
34096                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34097                 }
34098                 
34099                 this.add(region, ret);
34100                 break;
34101             
34102             
34103             case 'TreePanel': // our new panel!
34104                 cfg.el = this.el.createChild();
34105                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34106                 this.add(region, ret);
34107                 break;
34108             
34109             case 'NestedLayoutPanel': 
34110                 // create a new Layout (which is  a Border Layout...
34111                 var el = this.el.createChild();
34112                 var clayout = cfg.layout;
34113                 delete cfg.layout;
34114                 clayout.items   = clayout.items  || [];
34115                 // replace this exitems with the clayout ones..
34116                 xitems = clayout.items;
34117                  
34118                 
34119                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34120                     cfg.background = false;
34121                 }
34122                 var layout = new Roo.BorderLayout(el, clayout);
34123                 
34124                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
34125                 //console.log('adding nested layout panel '  + cfg.toSource());
34126                 this.add(region, ret);
34127                 nb = {}; /// find first...
34128                 break;
34129                 
34130             case 'GridPanel': 
34131             
34132                 // needs grid and region
34133                 
34134                 //var el = this.getRegion(region).el.createChild();
34135                 var el = this.el.createChild();
34136                 // create the grid first...
34137                 
34138                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
34139                 delete cfg.grid;
34140                 if (region == 'center' && this.active ) {
34141                     cfg.background = false;
34142                 }
34143                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
34144                 
34145                 this.add(region, ret);
34146                 if (cfg.background) {
34147                     ret.on('activate', function(gp) {
34148                         if (!gp.grid.rendered) {
34149                             gp.grid.render();
34150                         }
34151                     });
34152                 } else {
34153                     grid.render();
34154                 }
34155                 break;
34156            
34157            
34158            
34159                 
34160                 
34161                 
34162             default:
34163                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34164                     
34165                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34166                     this.add(region, ret);
34167                 } else {
34168                 
34169                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
34170                     return null;
34171                 }
34172                 
34173              // GridPanel (grid, cfg)
34174             
34175         }
34176         this.beginUpdate();
34177         // add children..
34178         var region = '';
34179         var abn = {};
34180         Roo.each(xitems, function(i)  {
34181             region = nb && i.region ? i.region : false;
34182             
34183             var add = ret.addxtype(i);
34184            
34185             if (region) {
34186                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34187                 if (!i.background) {
34188                     abn[region] = nb[region] ;
34189                 }
34190             }
34191             
34192         });
34193         this.endUpdate();
34194
34195         // make the last non-background panel active..
34196         //if (nb) { Roo.log(abn); }
34197         if (nb) {
34198             
34199             for(var r in abn) {
34200                 region = this.getRegion(r);
34201                 if (region) {
34202                     // tried using nb[r], but it does not work..
34203                      
34204                     region.showPanel(abn[r]);
34205                    
34206                 }
34207             }
34208         }
34209         return ret;
34210         
34211     }
34212 });
34213
34214 /**
34215  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34216  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
34217  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34218  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
34219  * <pre><code>
34220 // shorthand
34221 var CP = Roo.ContentPanel;
34222
34223 var layout = Roo.BorderLayout.create({
34224     north: {
34225         initialSize: 25,
34226         titlebar: false,
34227         panels: [new CP("north", "North")]
34228     },
34229     west: {
34230         split:true,
34231         initialSize: 200,
34232         minSize: 175,
34233         maxSize: 400,
34234         titlebar: true,
34235         collapsible: true,
34236         panels: [new CP("west", {title: "West"})]
34237     },
34238     east: {
34239         split:true,
34240         initialSize: 202,
34241         minSize: 175,
34242         maxSize: 400,
34243         titlebar: true,
34244         collapsible: true,
34245         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34246     },
34247     south: {
34248         split:true,
34249         initialSize: 100,
34250         minSize: 100,
34251         maxSize: 200,
34252         titlebar: true,
34253         collapsible: true,
34254         panels: [new CP("south", {title: "South", closable: true})]
34255     },
34256     center: {
34257         titlebar: true,
34258         autoScroll:true,
34259         resizeTabs: true,
34260         minTabWidth: 50,
34261         preferredTabWidth: 150,
34262         panels: [
34263             new CP("center1", {title: "Close Me", closable: true}),
34264             new CP("center2", {title: "Center Panel", closable: false})
34265         ]
34266     }
34267 }, document.body);
34268
34269 layout.getRegion("center").showPanel("center1");
34270 </code></pre>
34271  * @param config
34272  * @param targetEl
34273  */
34274 Roo.BorderLayout.create = function(config, targetEl){
34275     var layout = new Roo.BorderLayout(targetEl || document.body, config);
34276     layout.beginUpdate();
34277     var regions = Roo.BorderLayout.RegionFactory.validRegions;
34278     for(var j = 0, jlen = regions.length; j < jlen; j++){
34279         var lr = regions[j];
34280         if(layout.regions[lr] && config[lr].panels){
34281             var r = layout.regions[lr];
34282             var ps = config[lr].panels;
34283             layout.addTypedPanels(r, ps);
34284         }
34285     }
34286     layout.endUpdate();
34287     return layout;
34288 };
34289
34290 // private
34291 Roo.BorderLayout.RegionFactory = {
34292     // private
34293     validRegions : ["north","south","east","west","center"],
34294
34295     // private
34296     create : function(target, mgr, config){
34297         target = target.toLowerCase();
34298         if(config.lightweight || config.basic){
34299             return new Roo.BasicLayoutRegion(mgr, config, target);
34300         }
34301         switch(target){
34302             case "north":
34303                 return new Roo.NorthLayoutRegion(mgr, config);
34304             case "south":
34305                 return new Roo.SouthLayoutRegion(mgr, config);
34306             case "east":
34307                 return new Roo.EastLayoutRegion(mgr, config);
34308             case "west":
34309                 return new Roo.WestLayoutRegion(mgr, config);
34310             case "center":
34311                 return new Roo.CenterLayoutRegion(mgr, config);
34312         }
34313         throw 'Layout region "'+target+'" not supported.';
34314     }
34315 };/*
34316  * Based on:
34317  * Ext JS Library 1.1.1
34318  * Copyright(c) 2006-2007, Ext JS, LLC.
34319  *
34320  * Originally Released Under LGPL - original licence link has changed is not relivant.
34321  *
34322  * Fork - LGPL
34323  * <script type="text/javascript">
34324  */
34325  
34326 /**
34327  * @class Roo.BasicLayoutRegion
34328  * @extends Roo.util.Observable
34329  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34330  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34331  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34332  */
34333 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34334     this.mgr = mgr;
34335     this.position  = pos;
34336     this.events = {
34337         /**
34338          * @scope Roo.BasicLayoutRegion
34339          */
34340         
34341         /**
34342          * @event beforeremove
34343          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34344          * @param {Roo.LayoutRegion} this
34345          * @param {Roo.ContentPanel} panel The panel
34346          * @param {Object} e The cancel event object
34347          */
34348         "beforeremove" : true,
34349         /**
34350          * @event invalidated
34351          * Fires when the layout for this region is changed.
34352          * @param {Roo.LayoutRegion} this
34353          */
34354         "invalidated" : true,
34355         /**
34356          * @event visibilitychange
34357          * Fires when this region is shown or hidden 
34358          * @param {Roo.LayoutRegion} this
34359          * @param {Boolean} visibility true or false
34360          */
34361         "visibilitychange" : true,
34362         /**
34363          * @event paneladded
34364          * Fires when a panel is added. 
34365          * @param {Roo.LayoutRegion} this
34366          * @param {Roo.ContentPanel} panel The panel
34367          */
34368         "paneladded" : true,
34369         /**
34370          * @event panelremoved
34371          * Fires when a panel is removed. 
34372          * @param {Roo.LayoutRegion} this
34373          * @param {Roo.ContentPanel} panel The panel
34374          */
34375         "panelremoved" : true,
34376         /**
34377          * @event beforecollapse
34378          * Fires when this region before collapse.
34379          * @param {Roo.LayoutRegion} this
34380          */
34381         "beforecollapse" : true,
34382         /**
34383          * @event collapsed
34384          * Fires when this region is collapsed.
34385          * @param {Roo.LayoutRegion} this
34386          */
34387         "collapsed" : true,
34388         /**
34389          * @event expanded
34390          * Fires when this region is expanded.
34391          * @param {Roo.LayoutRegion} this
34392          */
34393         "expanded" : true,
34394         /**
34395          * @event slideshow
34396          * Fires when this region is slid into view.
34397          * @param {Roo.LayoutRegion} this
34398          */
34399         "slideshow" : true,
34400         /**
34401          * @event slidehide
34402          * Fires when this region slides out of view. 
34403          * @param {Roo.LayoutRegion} this
34404          */
34405         "slidehide" : true,
34406         /**
34407          * @event panelactivated
34408          * Fires when a panel is activated. 
34409          * @param {Roo.LayoutRegion} this
34410          * @param {Roo.ContentPanel} panel The activated panel
34411          */
34412         "panelactivated" : true,
34413         /**
34414          * @event resized
34415          * Fires when the user resizes this region. 
34416          * @param {Roo.LayoutRegion} this
34417          * @param {Number} newSize The new size (width for east/west, height for north/south)
34418          */
34419         "resized" : true
34420     };
34421     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34422     this.panels = new Roo.util.MixedCollection();
34423     this.panels.getKey = this.getPanelId.createDelegate(this);
34424     this.box = null;
34425     this.activePanel = null;
34426     // ensure listeners are added...
34427     
34428     if (config.listeners || config.events) {
34429         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34430             listeners : config.listeners || {},
34431             events : config.events || {}
34432         });
34433     }
34434     
34435     if(skipConfig !== true){
34436         this.applyConfig(config);
34437     }
34438 };
34439
34440 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34441     getPanelId : function(p){
34442         return p.getId();
34443     },
34444     
34445     applyConfig : function(config){
34446         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34447         this.config = config;
34448         
34449     },
34450     
34451     /**
34452      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34453      * the width, for horizontal (north, south) the height.
34454      * @param {Number} newSize The new width or height
34455      */
34456     resizeTo : function(newSize){
34457         var el = this.el ? this.el :
34458                  (this.activePanel ? this.activePanel.getEl() : null);
34459         if(el){
34460             switch(this.position){
34461                 case "east":
34462                 case "west":
34463                     el.setWidth(newSize);
34464                     this.fireEvent("resized", this, newSize);
34465                 break;
34466                 case "north":
34467                 case "south":
34468                     el.setHeight(newSize);
34469                     this.fireEvent("resized", this, newSize);
34470                 break;                
34471             }
34472         }
34473     },
34474     
34475     getBox : function(){
34476         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34477     },
34478     
34479     getMargins : function(){
34480         return this.margins;
34481     },
34482     
34483     updateBox : function(box){
34484         this.box = box;
34485         var el = this.activePanel.getEl();
34486         el.dom.style.left = box.x + "px";
34487         el.dom.style.top = box.y + "px";
34488         this.activePanel.setSize(box.width, box.height);
34489     },
34490     
34491     /**
34492      * Returns the container element for this region.
34493      * @return {Roo.Element}
34494      */
34495     getEl : function(){
34496         return this.activePanel;
34497     },
34498     
34499     /**
34500      * Returns true if this region is currently visible.
34501      * @return {Boolean}
34502      */
34503     isVisible : function(){
34504         return this.activePanel ? true : false;
34505     },
34506     
34507     setActivePanel : function(panel){
34508         panel = this.getPanel(panel);
34509         if(this.activePanel && this.activePanel != panel){
34510             this.activePanel.setActiveState(false);
34511             this.activePanel.getEl().setLeftTop(-10000,-10000);
34512         }
34513         this.activePanel = panel;
34514         panel.setActiveState(true);
34515         if(this.box){
34516             panel.setSize(this.box.width, this.box.height);
34517         }
34518         this.fireEvent("panelactivated", this, panel);
34519         this.fireEvent("invalidated");
34520     },
34521     
34522     /**
34523      * Show the specified panel.
34524      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34525      * @return {Roo.ContentPanel} The shown panel or null
34526      */
34527     showPanel : function(panel){
34528         if(panel = this.getPanel(panel)){
34529             this.setActivePanel(panel);
34530         }
34531         return panel;
34532     },
34533     
34534     /**
34535      * Get the active panel for this region.
34536      * @return {Roo.ContentPanel} The active panel or null
34537      */
34538     getActivePanel : function(){
34539         return this.activePanel;
34540     },
34541     
34542     /**
34543      * Add the passed ContentPanel(s)
34544      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34545      * @return {Roo.ContentPanel} The panel added (if only one was added)
34546      */
34547     add : function(panel){
34548         if(arguments.length > 1){
34549             for(var i = 0, len = arguments.length; i < len; i++) {
34550                 this.add(arguments[i]);
34551             }
34552             return null;
34553         }
34554         if(this.hasPanel(panel)){
34555             this.showPanel(panel);
34556             return panel;
34557         }
34558         var el = panel.getEl();
34559         if(el.dom.parentNode != this.mgr.el.dom){
34560             this.mgr.el.dom.appendChild(el.dom);
34561         }
34562         if(panel.setRegion){
34563             panel.setRegion(this);
34564         }
34565         this.panels.add(panel);
34566         el.setStyle("position", "absolute");
34567         if(!panel.background){
34568             this.setActivePanel(panel);
34569             if(this.config.initialSize && this.panels.getCount()==1){
34570                 this.resizeTo(this.config.initialSize);
34571             }
34572         }
34573         this.fireEvent("paneladded", this, panel);
34574         return panel;
34575     },
34576     
34577     /**
34578      * Returns true if the panel is in this region.
34579      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34580      * @return {Boolean}
34581      */
34582     hasPanel : function(panel){
34583         if(typeof panel == "object"){ // must be panel obj
34584             panel = panel.getId();
34585         }
34586         return this.getPanel(panel) ? true : false;
34587     },
34588     
34589     /**
34590      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34591      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34592      * @param {Boolean} preservePanel Overrides the config preservePanel option
34593      * @return {Roo.ContentPanel} The panel that was removed
34594      */
34595     remove : function(panel, preservePanel){
34596         panel = this.getPanel(panel);
34597         if(!panel){
34598             return null;
34599         }
34600         var e = {};
34601         this.fireEvent("beforeremove", this, panel, e);
34602         if(e.cancel === true){
34603             return null;
34604         }
34605         var panelId = panel.getId();
34606         this.panels.removeKey(panelId);
34607         return panel;
34608     },
34609     
34610     /**
34611      * Returns the panel specified or null if it's not in this region.
34612      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34613      * @return {Roo.ContentPanel}
34614      */
34615     getPanel : function(id){
34616         if(typeof id == "object"){ // must be panel obj
34617             return id;
34618         }
34619         return this.panels.get(id);
34620     },
34621     
34622     /**
34623      * Returns this regions position (north/south/east/west/center).
34624      * @return {String} 
34625      */
34626     getPosition: function(){
34627         return this.position;    
34628     }
34629 });/*
34630  * Based on:
34631  * Ext JS Library 1.1.1
34632  * Copyright(c) 2006-2007, Ext JS, LLC.
34633  *
34634  * Originally Released Under LGPL - original licence link has changed is not relivant.
34635  *
34636  * Fork - LGPL
34637  * <script type="text/javascript">
34638  */
34639  
34640 /**
34641  * @class Roo.LayoutRegion
34642  * @extends Roo.BasicLayoutRegion
34643  * This class represents a region in a layout manager.
34644  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
34645  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
34646  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
34647  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34648  * @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})
34649  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34650  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
34651  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34652  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34653  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34654  * @cfg {String}    title           The title for the region (overrides panel titles)
34655  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34656  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34657  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34658  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34659  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34660  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34661  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34662  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34663  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34664  * @cfg {Boolean}   showPin         True to show a pin button
34665  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34666  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34667  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34668  * @cfg {Number}    width           For East/West panels
34669  * @cfg {Number}    height          For North/South panels
34670  * @cfg {Boolean}   split           To show the splitter
34671  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34672  */
34673 Roo.LayoutRegion = function(mgr, config, pos){
34674     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34675     var dh = Roo.DomHelper;
34676     /** This region's container element 
34677     * @type Roo.Element */
34678     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34679     /** This region's title element 
34680     * @type Roo.Element */
34681
34682     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34683         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34684         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34685     ]}, true);
34686     this.titleEl.enableDisplayMode();
34687     /** This region's title text element 
34688     * @type HTMLElement */
34689     this.titleTextEl = this.titleEl.dom.firstChild;
34690     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34691     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34692     this.closeBtn.enableDisplayMode();
34693     this.closeBtn.on("click", this.closeClicked, this);
34694     this.closeBtn.hide();
34695
34696     this.createBody(config);
34697     this.visible = true;
34698     this.collapsed = false;
34699
34700     if(config.hideWhenEmpty){
34701         this.hide();
34702         this.on("paneladded", this.validateVisibility, this);
34703         this.on("panelremoved", this.validateVisibility, this);
34704     }
34705     this.applyConfig(config);
34706 };
34707
34708 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34709
34710     createBody : function(){
34711         /** This region's body element 
34712         * @type Roo.Element */
34713         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34714     },
34715
34716     applyConfig : function(c){
34717         if(c.collapsible && this.position != "center" && !this.collapsedEl){
34718             var dh = Roo.DomHelper;
34719             if(c.titlebar !== false){
34720                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34721                 this.collapseBtn.on("click", this.collapse, this);
34722                 this.collapseBtn.enableDisplayMode();
34723
34724                 if(c.showPin === true || this.showPin){
34725                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34726                     this.stickBtn.enableDisplayMode();
34727                     this.stickBtn.on("click", this.expand, this);
34728                     this.stickBtn.hide();
34729                 }
34730             }
34731             /** This region's collapsed element
34732             * @type Roo.Element */
34733             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34734                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34735             ]}, true);
34736             if(c.floatable !== false){
34737                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34738                this.collapsedEl.on("click", this.collapseClick, this);
34739             }
34740
34741             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34742                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34743                    id: "message", unselectable: "on", style:{"float":"left"}});
34744                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34745              }
34746             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34747             this.expandBtn.on("click", this.expand, this);
34748         }
34749         if(this.collapseBtn){
34750             this.collapseBtn.setVisible(c.collapsible == true);
34751         }
34752         this.cmargins = c.cmargins || this.cmargins ||
34753                          (this.position == "west" || this.position == "east" ?
34754                              {top: 0, left: 2, right:2, bottom: 0} :
34755                              {top: 2, left: 0, right:0, bottom: 2});
34756         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34757         this.bottomTabs = c.tabPosition != "top";
34758         this.autoScroll = c.autoScroll || false;
34759         if(this.autoScroll){
34760             this.bodyEl.setStyle("overflow", "auto");
34761         }else{
34762             this.bodyEl.setStyle("overflow", "hidden");
34763         }
34764         //if(c.titlebar !== false){
34765             if((!c.titlebar && !c.title) || c.titlebar === false){
34766                 this.titleEl.hide();
34767             }else{
34768                 this.titleEl.show();
34769                 if(c.title){
34770                     this.titleTextEl.innerHTML = c.title;
34771                 }
34772             }
34773         //}
34774         this.duration = c.duration || .30;
34775         this.slideDuration = c.slideDuration || .45;
34776         this.config = c;
34777         if(c.collapsed){
34778             this.collapse(true);
34779         }
34780         if(c.hidden){
34781             this.hide();
34782         }
34783     },
34784     /**
34785      * Returns true if this region is currently visible.
34786      * @return {Boolean}
34787      */
34788     isVisible : function(){
34789         return this.visible;
34790     },
34791
34792     /**
34793      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34794      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34795      */
34796     setCollapsedTitle : function(title){
34797         title = title || "&#160;";
34798         if(this.collapsedTitleTextEl){
34799             this.collapsedTitleTextEl.innerHTML = title;
34800         }
34801     },
34802
34803     getBox : function(){
34804         var b;
34805         if(!this.collapsed){
34806             b = this.el.getBox(false, true);
34807         }else{
34808             b = this.collapsedEl.getBox(false, true);
34809         }
34810         return b;
34811     },
34812
34813     getMargins : function(){
34814         return this.collapsed ? this.cmargins : this.margins;
34815     },
34816
34817     highlight : function(){
34818         this.el.addClass("x-layout-panel-dragover");
34819     },
34820
34821     unhighlight : function(){
34822         this.el.removeClass("x-layout-panel-dragover");
34823     },
34824
34825     updateBox : function(box){
34826         this.box = box;
34827         if(!this.collapsed){
34828             this.el.dom.style.left = box.x + "px";
34829             this.el.dom.style.top = box.y + "px";
34830             this.updateBody(box.width, box.height);
34831         }else{
34832             this.collapsedEl.dom.style.left = box.x + "px";
34833             this.collapsedEl.dom.style.top = box.y + "px";
34834             this.collapsedEl.setSize(box.width, box.height);
34835         }
34836         if(this.tabs){
34837             this.tabs.autoSizeTabs();
34838         }
34839     },
34840
34841     updateBody : function(w, h){
34842         if(w !== null){
34843             this.el.setWidth(w);
34844             w -= this.el.getBorderWidth("rl");
34845             if(this.config.adjustments){
34846                 w += this.config.adjustments[0];
34847             }
34848         }
34849         if(h !== null){
34850             this.el.setHeight(h);
34851             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34852             h -= this.el.getBorderWidth("tb");
34853             if(this.config.adjustments){
34854                 h += this.config.adjustments[1];
34855             }
34856             this.bodyEl.setHeight(h);
34857             if(this.tabs){
34858                 h = this.tabs.syncHeight(h);
34859             }
34860         }
34861         if(this.panelSize){
34862             w = w !== null ? w : this.panelSize.width;
34863             h = h !== null ? h : this.panelSize.height;
34864         }
34865         if(this.activePanel){
34866             var el = this.activePanel.getEl();
34867             w = w !== null ? w : el.getWidth();
34868             h = h !== null ? h : el.getHeight();
34869             this.panelSize = {width: w, height: h};
34870             this.activePanel.setSize(w, h);
34871         }
34872         if(Roo.isIE && this.tabs){
34873             this.tabs.el.repaint();
34874         }
34875     },
34876
34877     /**
34878      * Returns the container element for this region.
34879      * @return {Roo.Element}
34880      */
34881     getEl : function(){
34882         return this.el;
34883     },
34884
34885     /**
34886      * Hides this region.
34887      */
34888     hide : function(){
34889         if(!this.collapsed){
34890             this.el.dom.style.left = "-2000px";
34891             this.el.hide();
34892         }else{
34893             this.collapsedEl.dom.style.left = "-2000px";
34894             this.collapsedEl.hide();
34895         }
34896         this.visible = false;
34897         this.fireEvent("visibilitychange", this, false);
34898     },
34899
34900     /**
34901      * Shows this region if it was previously hidden.
34902      */
34903     show : function(){
34904         if(!this.collapsed){
34905             this.el.show();
34906         }else{
34907             this.collapsedEl.show();
34908         }
34909         this.visible = true;
34910         this.fireEvent("visibilitychange", this, true);
34911     },
34912
34913     closeClicked : function(){
34914         if(this.activePanel){
34915             this.remove(this.activePanel);
34916         }
34917     },
34918
34919     collapseClick : function(e){
34920         if(this.isSlid){
34921            e.stopPropagation();
34922            this.slideIn();
34923         }else{
34924            e.stopPropagation();
34925            this.slideOut();
34926         }
34927     },
34928
34929     /**
34930      * Collapses this region.
34931      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34932      */
34933     collapse : function(skipAnim, skipCheck){
34934         if(this.collapsed) {
34935             return;
34936         }
34937         
34938         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34939             
34940             this.collapsed = true;
34941             if(this.split){
34942                 this.split.el.hide();
34943             }
34944             if(this.config.animate && skipAnim !== true){
34945                 this.fireEvent("invalidated", this);
34946                 this.animateCollapse();
34947             }else{
34948                 this.el.setLocation(-20000,-20000);
34949                 this.el.hide();
34950                 this.collapsedEl.show();
34951                 this.fireEvent("collapsed", this);
34952                 this.fireEvent("invalidated", this);
34953             }
34954         }
34955         
34956     },
34957
34958     animateCollapse : function(){
34959         // overridden
34960     },
34961
34962     /**
34963      * Expands this region if it was previously collapsed.
34964      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34965      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34966      */
34967     expand : function(e, skipAnim){
34968         if(e) {
34969             e.stopPropagation();
34970         }
34971         if(!this.collapsed || this.el.hasActiveFx()) {
34972             return;
34973         }
34974         if(this.isSlid){
34975             this.afterSlideIn();
34976             skipAnim = true;
34977         }
34978         this.collapsed = false;
34979         if(this.config.animate && skipAnim !== true){
34980             this.animateExpand();
34981         }else{
34982             this.el.show();
34983             if(this.split){
34984                 this.split.el.show();
34985             }
34986             this.collapsedEl.setLocation(-2000,-2000);
34987             this.collapsedEl.hide();
34988             this.fireEvent("invalidated", this);
34989             this.fireEvent("expanded", this);
34990         }
34991     },
34992
34993     animateExpand : function(){
34994         // overridden
34995     },
34996
34997     initTabs : function()
34998     {
34999         this.bodyEl.setStyle("overflow", "hidden");
35000         var ts = new Roo.TabPanel(
35001                 this.bodyEl.dom,
35002                 {
35003                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
35004                     disableTooltips: this.config.disableTabTips,
35005                     toolbar : this.config.toolbar
35006                 }
35007         );
35008         if(this.config.hideTabs){
35009             ts.stripWrap.setDisplayed(false);
35010         }
35011         this.tabs = ts;
35012         ts.resizeTabs = this.config.resizeTabs === true;
35013         ts.minTabWidth = this.config.minTabWidth || 40;
35014         ts.maxTabWidth = this.config.maxTabWidth || 250;
35015         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35016         ts.monitorResize = false;
35017         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35018         ts.bodyEl.addClass('x-layout-tabs-body');
35019         this.panels.each(this.initPanelAsTab, this);
35020     },
35021
35022     initPanelAsTab : function(panel){
35023         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
35024                     this.config.closeOnTab && panel.isClosable());
35025         if(panel.tabTip !== undefined){
35026             ti.setTooltip(panel.tabTip);
35027         }
35028         ti.on("activate", function(){
35029               this.setActivePanel(panel);
35030         }, this);
35031         if(this.config.closeOnTab){
35032             ti.on("beforeclose", function(t, e){
35033                 e.cancel = true;
35034                 this.remove(panel);
35035             }, this);
35036         }
35037         return ti;
35038     },
35039
35040     updatePanelTitle : function(panel, title){
35041         if(this.activePanel == panel){
35042             this.updateTitle(title);
35043         }
35044         if(this.tabs){
35045             var ti = this.tabs.getTab(panel.getEl().id);
35046             ti.setText(title);
35047             if(panel.tabTip !== undefined){
35048                 ti.setTooltip(panel.tabTip);
35049             }
35050         }
35051     },
35052
35053     updateTitle : function(title){
35054         if(this.titleTextEl && !this.config.title){
35055             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35056         }
35057     },
35058
35059     setActivePanel : function(panel){
35060         panel = this.getPanel(panel);
35061         if(this.activePanel && this.activePanel != panel){
35062             this.activePanel.setActiveState(false);
35063         }
35064         this.activePanel = panel;
35065         panel.setActiveState(true);
35066         if(this.panelSize){
35067             panel.setSize(this.panelSize.width, this.panelSize.height);
35068         }
35069         if(this.closeBtn){
35070             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35071         }
35072         this.updateTitle(panel.getTitle());
35073         if(this.tabs){
35074             this.fireEvent("invalidated", this);
35075         }
35076         this.fireEvent("panelactivated", this, panel);
35077     },
35078
35079     /**
35080      * Shows the specified panel.
35081      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35082      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35083      */
35084     showPanel : function(panel)
35085     {
35086         panel = this.getPanel(panel);
35087         if(panel){
35088             if(this.tabs){
35089                 var tab = this.tabs.getTab(panel.getEl().id);
35090                 if(tab.isHidden()){
35091                     this.tabs.unhideTab(tab.id);
35092                 }
35093                 tab.activate();
35094             }else{
35095                 this.setActivePanel(panel);
35096             }
35097         }
35098         return panel;
35099     },
35100
35101     /**
35102      * Get the active panel for this region.
35103      * @return {Roo.ContentPanel} The active panel or null
35104      */
35105     getActivePanel : function(){
35106         return this.activePanel;
35107     },
35108
35109     validateVisibility : function(){
35110         if(this.panels.getCount() < 1){
35111             this.updateTitle("&#160;");
35112             this.closeBtn.hide();
35113             this.hide();
35114         }else{
35115             if(!this.isVisible()){
35116                 this.show();
35117             }
35118         }
35119     },
35120
35121     /**
35122      * Adds the passed ContentPanel(s) to this region.
35123      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35124      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35125      */
35126     add : function(panel){
35127         if(arguments.length > 1){
35128             for(var i = 0, len = arguments.length; i < len; i++) {
35129                 this.add(arguments[i]);
35130             }
35131             return null;
35132         }
35133         if(this.hasPanel(panel)){
35134             this.showPanel(panel);
35135             return panel;
35136         }
35137         panel.setRegion(this);
35138         this.panels.add(panel);
35139         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35140             this.bodyEl.dom.appendChild(panel.getEl().dom);
35141             if(panel.background !== true){
35142                 this.setActivePanel(panel);
35143             }
35144             this.fireEvent("paneladded", this, panel);
35145             return panel;
35146         }
35147         if(!this.tabs){
35148             this.initTabs();
35149         }else{
35150             this.initPanelAsTab(panel);
35151         }
35152         if(panel.background !== true){
35153             this.tabs.activate(panel.getEl().id);
35154         }
35155         this.fireEvent("paneladded", this, panel);
35156         return panel;
35157     },
35158
35159     /**
35160      * Hides the tab for the specified panel.
35161      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35162      */
35163     hidePanel : function(panel){
35164         if(this.tabs && (panel = this.getPanel(panel))){
35165             this.tabs.hideTab(panel.getEl().id);
35166         }
35167     },
35168
35169     /**
35170      * Unhides the tab for a previously hidden panel.
35171      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35172      */
35173     unhidePanel : function(panel){
35174         if(this.tabs && (panel = this.getPanel(panel))){
35175             this.tabs.unhideTab(panel.getEl().id);
35176         }
35177     },
35178
35179     clearPanels : function(){
35180         while(this.panels.getCount() > 0){
35181              this.remove(this.panels.first());
35182         }
35183     },
35184
35185     /**
35186      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35187      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35188      * @param {Boolean} preservePanel Overrides the config preservePanel option
35189      * @return {Roo.ContentPanel} The panel that was removed
35190      */
35191     remove : function(panel, preservePanel){
35192         panel = this.getPanel(panel);
35193         if(!panel){
35194             return null;
35195         }
35196         var e = {};
35197         this.fireEvent("beforeremove", this, panel, e);
35198         if(e.cancel === true){
35199             return null;
35200         }
35201         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35202         var panelId = panel.getId();
35203         this.panels.removeKey(panelId);
35204         if(preservePanel){
35205             document.body.appendChild(panel.getEl().dom);
35206         }
35207         if(this.tabs){
35208             this.tabs.removeTab(panel.getEl().id);
35209         }else if (!preservePanel){
35210             this.bodyEl.dom.removeChild(panel.getEl().dom);
35211         }
35212         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35213             var p = this.panels.first();
35214             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35215             tempEl.appendChild(p.getEl().dom);
35216             this.bodyEl.update("");
35217             this.bodyEl.dom.appendChild(p.getEl().dom);
35218             tempEl = null;
35219             this.updateTitle(p.getTitle());
35220             this.tabs = null;
35221             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35222             this.setActivePanel(p);
35223         }
35224         panel.setRegion(null);
35225         if(this.activePanel == panel){
35226             this.activePanel = null;
35227         }
35228         if(this.config.autoDestroy !== false && preservePanel !== true){
35229             try{panel.destroy();}catch(e){}
35230         }
35231         this.fireEvent("panelremoved", this, panel);
35232         return panel;
35233     },
35234
35235     /**
35236      * Returns the TabPanel component used by this region
35237      * @return {Roo.TabPanel}
35238      */
35239     getTabs : function(){
35240         return this.tabs;
35241     },
35242
35243     createTool : function(parentEl, className){
35244         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35245             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
35246         btn.addClassOnOver("x-layout-tools-button-over");
35247         return btn;
35248     }
35249 });/*
35250  * Based on:
35251  * Ext JS Library 1.1.1
35252  * Copyright(c) 2006-2007, Ext JS, LLC.
35253  *
35254  * Originally Released Under LGPL - original licence link has changed is not relivant.
35255  *
35256  * Fork - LGPL
35257  * <script type="text/javascript">
35258  */
35259  
35260
35261
35262 /**
35263  * @class Roo.SplitLayoutRegion
35264  * @extends Roo.LayoutRegion
35265  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35266  */
35267 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35268     this.cursor = cursor;
35269     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35270 };
35271
35272 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35273     splitTip : "Drag to resize.",
35274     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35275     useSplitTips : false,
35276
35277     applyConfig : function(config){
35278         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35279         if(config.split){
35280             if(!this.split){
35281                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
35282                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
35283                 /** The SplitBar for this region 
35284                 * @type Roo.SplitBar */
35285                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35286                 this.split.on("moved", this.onSplitMove, this);
35287                 this.split.useShim = config.useShim === true;
35288                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35289                 if(this.useSplitTips){
35290                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35291                 }
35292                 if(config.collapsible){
35293                     this.split.el.on("dblclick", this.collapse,  this);
35294                 }
35295             }
35296             if(typeof config.minSize != "undefined"){
35297                 this.split.minSize = config.minSize;
35298             }
35299             if(typeof config.maxSize != "undefined"){
35300                 this.split.maxSize = config.maxSize;
35301             }
35302             if(config.hideWhenEmpty || config.hidden || config.collapsed){
35303                 this.hideSplitter();
35304             }
35305         }
35306     },
35307
35308     getHMaxSize : function(){
35309          var cmax = this.config.maxSize || 10000;
35310          var center = this.mgr.getRegion("center");
35311          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35312     },
35313
35314     getVMaxSize : function(){
35315          var cmax = this.config.maxSize || 10000;
35316          var center = this.mgr.getRegion("center");
35317          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35318     },
35319
35320     onSplitMove : function(split, newSize){
35321         this.fireEvent("resized", this, newSize);
35322     },
35323     
35324     /** 
35325      * Returns the {@link Roo.SplitBar} for this region.
35326      * @return {Roo.SplitBar}
35327      */
35328     getSplitBar : function(){
35329         return this.split;
35330     },
35331     
35332     hide : function(){
35333         this.hideSplitter();
35334         Roo.SplitLayoutRegion.superclass.hide.call(this);
35335     },
35336
35337     hideSplitter : function(){
35338         if(this.split){
35339             this.split.el.setLocation(-2000,-2000);
35340             this.split.el.hide();
35341         }
35342     },
35343
35344     show : function(){
35345         if(this.split){
35346             this.split.el.show();
35347         }
35348         Roo.SplitLayoutRegion.superclass.show.call(this);
35349     },
35350     
35351     beforeSlide: function(){
35352         if(Roo.isGecko){// firefox overflow auto bug workaround
35353             this.bodyEl.clip();
35354             if(this.tabs) {
35355                 this.tabs.bodyEl.clip();
35356             }
35357             if(this.activePanel){
35358                 this.activePanel.getEl().clip();
35359                 
35360                 if(this.activePanel.beforeSlide){
35361                     this.activePanel.beforeSlide();
35362                 }
35363             }
35364         }
35365     },
35366     
35367     afterSlide : function(){
35368         if(Roo.isGecko){// firefox overflow auto bug workaround
35369             this.bodyEl.unclip();
35370             if(this.tabs) {
35371                 this.tabs.bodyEl.unclip();
35372             }
35373             if(this.activePanel){
35374                 this.activePanel.getEl().unclip();
35375                 if(this.activePanel.afterSlide){
35376                     this.activePanel.afterSlide();
35377                 }
35378             }
35379         }
35380     },
35381
35382     initAutoHide : function(){
35383         if(this.autoHide !== false){
35384             if(!this.autoHideHd){
35385                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35386                 this.autoHideHd = {
35387                     "mouseout": function(e){
35388                         if(!e.within(this.el, true)){
35389                             st.delay(500);
35390                         }
35391                     },
35392                     "mouseover" : function(e){
35393                         st.cancel();
35394                     },
35395                     scope : this
35396                 };
35397             }
35398             this.el.on(this.autoHideHd);
35399         }
35400     },
35401
35402     clearAutoHide : function(){
35403         if(this.autoHide !== false){
35404             this.el.un("mouseout", this.autoHideHd.mouseout);
35405             this.el.un("mouseover", this.autoHideHd.mouseover);
35406         }
35407     },
35408
35409     clearMonitor : function(){
35410         Roo.get(document).un("click", this.slideInIf, this);
35411     },
35412
35413     // these names are backwards but not changed for compat
35414     slideOut : function(){
35415         if(this.isSlid || this.el.hasActiveFx()){
35416             return;
35417         }
35418         this.isSlid = true;
35419         if(this.collapseBtn){
35420             this.collapseBtn.hide();
35421         }
35422         this.closeBtnState = this.closeBtn.getStyle('display');
35423         this.closeBtn.hide();
35424         if(this.stickBtn){
35425             this.stickBtn.show();
35426         }
35427         this.el.show();
35428         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35429         this.beforeSlide();
35430         this.el.setStyle("z-index", 10001);
35431         this.el.slideIn(this.getSlideAnchor(), {
35432             callback: function(){
35433                 this.afterSlide();
35434                 this.initAutoHide();
35435                 Roo.get(document).on("click", this.slideInIf, this);
35436                 this.fireEvent("slideshow", this);
35437             },
35438             scope: this,
35439             block: true
35440         });
35441     },
35442
35443     afterSlideIn : function(){
35444         this.clearAutoHide();
35445         this.isSlid = false;
35446         this.clearMonitor();
35447         this.el.setStyle("z-index", "");
35448         if(this.collapseBtn){
35449             this.collapseBtn.show();
35450         }
35451         this.closeBtn.setStyle('display', this.closeBtnState);
35452         if(this.stickBtn){
35453             this.stickBtn.hide();
35454         }
35455         this.fireEvent("slidehide", this);
35456     },
35457
35458     slideIn : function(cb){
35459         if(!this.isSlid || this.el.hasActiveFx()){
35460             Roo.callback(cb);
35461             return;
35462         }
35463         this.isSlid = false;
35464         this.beforeSlide();
35465         this.el.slideOut(this.getSlideAnchor(), {
35466             callback: function(){
35467                 this.el.setLeftTop(-10000, -10000);
35468                 this.afterSlide();
35469                 this.afterSlideIn();
35470                 Roo.callback(cb);
35471             },
35472             scope: this,
35473             block: true
35474         });
35475     },
35476     
35477     slideInIf : function(e){
35478         if(!e.within(this.el)){
35479             this.slideIn();
35480         }
35481     },
35482
35483     animateCollapse : function(){
35484         this.beforeSlide();
35485         this.el.setStyle("z-index", 20000);
35486         var anchor = this.getSlideAnchor();
35487         this.el.slideOut(anchor, {
35488             callback : function(){
35489                 this.el.setStyle("z-index", "");
35490                 this.collapsedEl.slideIn(anchor, {duration:.3});
35491                 this.afterSlide();
35492                 this.el.setLocation(-10000,-10000);
35493                 this.el.hide();
35494                 this.fireEvent("collapsed", this);
35495             },
35496             scope: this,
35497             block: true
35498         });
35499     },
35500
35501     animateExpand : function(){
35502         this.beforeSlide();
35503         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35504         this.el.setStyle("z-index", 20000);
35505         this.collapsedEl.hide({
35506             duration:.1
35507         });
35508         this.el.slideIn(this.getSlideAnchor(), {
35509             callback : function(){
35510                 this.el.setStyle("z-index", "");
35511                 this.afterSlide();
35512                 if(this.split){
35513                     this.split.el.show();
35514                 }
35515                 this.fireEvent("invalidated", this);
35516                 this.fireEvent("expanded", this);
35517             },
35518             scope: this,
35519             block: true
35520         });
35521     },
35522
35523     anchors : {
35524         "west" : "left",
35525         "east" : "right",
35526         "north" : "top",
35527         "south" : "bottom"
35528     },
35529
35530     sanchors : {
35531         "west" : "l",
35532         "east" : "r",
35533         "north" : "t",
35534         "south" : "b"
35535     },
35536
35537     canchors : {
35538         "west" : "tl-tr",
35539         "east" : "tr-tl",
35540         "north" : "tl-bl",
35541         "south" : "bl-tl"
35542     },
35543
35544     getAnchor : function(){
35545         return this.anchors[this.position];
35546     },
35547
35548     getCollapseAnchor : function(){
35549         return this.canchors[this.position];
35550     },
35551
35552     getSlideAnchor : function(){
35553         return this.sanchors[this.position];
35554     },
35555
35556     getAlignAdj : function(){
35557         var cm = this.cmargins;
35558         switch(this.position){
35559             case "west":
35560                 return [0, 0];
35561             break;
35562             case "east":
35563                 return [0, 0];
35564             break;
35565             case "north":
35566                 return [0, 0];
35567             break;
35568             case "south":
35569                 return [0, 0];
35570             break;
35571         }
35572     },
35573
35574     getExpandAdj : function(){
35575         var c = this.collapsedEl, cm = this.cmargins;
35576         switch(this.position){
35577             case "west":
35578                 return [-(cm.right+c.getWidth()+cm.left), 0];
35579             break;
35580             case "east":
35581                 return [cm.right+c.getWidth()+cm.left, 0];
35582             break;
35583             case "north":
35584                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35585             break;
35586             case "south":
35587                 return [0, cm.top+cm.bottom+c.getHeight()];
35588             break;
35589         }
35590     }
35591 });/*
35592  * Based on:
35593  * Ext JS Library 1.1.1
35594  * Copyright(c) 2006-2007, Ext JS, LLC.
35595  *
35596  * Originally Released Under LGPL - original licence link has changed is not relivant.
35597  *
35598  * Fork - LGPL
35599  * <script type="text/javascript">
35600  */
35601 /*
35602  * These classes are private internal classes
35603  */
35604 Roo.CenterLayoutRegion = function(mgr, config){
35605     Roo.LayoutRegion.call(this, mgr, config, "center");
35606     this.visible = true;
35607     this.minWidth = config.minWidth || 20;
35608     this.minHeight = config.minHeight || 20;
35609 };
35610
35611 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35612     hide : function(){
35613         // center panel can't be hidden
35614     },
35615     
35616     show : function(){
35617         // center panel can't be hidden
35618     },
35619     
35620     getMinWidth: function(){
35621         return this.minWidth;
35622     },
35623     
35624     getMinHeight: function(){
35625         return this.minHeight;
35626     }
35627 });
35628
35629
35630 Roo.NorthLayoutRegion = function(mgr, config){
35631     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35632     if(this.split){
35633         this.split.placement = Roo.SplitBar.TOP;
35634         this.split.orientation = Roo.SplitBar.VERTICAL;
35635         this.split.el.addClass("x-layout-split-v");
35636     }
35637     var size = config.initialSize || config.height;
35638     if(typeof size != "undefined"){
35639         this.el.setHeight(size);
35640     }
35641 };
35642 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35643     orientation: Roo.SplitBar.VERTICAL,
35644     getBox : function(){
35645         if(this.collapsed){
35646             return this.collapsedEl.getBox();
35647         }
35648         var box = this.el.getBox();
35649         if(this.split){
35650             box.height += this.split.el.getHeight();
35651         }
35652         return box;
35653     },
35654     
35655     updateBox : function(box){
35656         if(this.split && !this.collapsed){
35657             box.height -= this.split.el.getHeight();
35658             this.split.el.setLeft(box.x);
35659             this.split.el.setTop(box.y+box.height);
35660             this.split.el.setWidth(box.width);
35661         }
35662         if(this.collapsed){
35663             this.updateBody(box.width, null);
35664         }
35665         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35666     }
35667 });
35668
35669 Roo.SouthLayoutRegion = function(mgr, config){
35670     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35671     if(this.split){
35672         this.split.placement = Roo.SplitBar.BOTTOM;
35673         this.split.orientation = Roo.SplitBar.VERTICAL;
35674         this.split.el.addClass("x-layout-split-v");
35675     }
35676     var size = config.initialSize || config.height;
35677     if(typeof size != "undefined"){
35678         this.el.setHeight(size);
35679     }
35680 };
35681 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35682     orientation: Roo.SplitBar.VERTICAL,
35683     getBox : function(){
35684         if(this.collapsed){
35685             return this.collapsedEl.getBox();
35686         }
35687         var box = this.el.getBox();
35688         if(this.split){
35689             var sh = this.split.el.getHeight();
35690             box.height += sh;
35691             box.y -= sh;
35692         }
35693         return box;
35694     },
35695     
35696     updateBox : function(box){
35697         if(this.split && !this.collapsed){
35698             var sh = this.split.el.getHeight();
35699             box.height -= sh;
35700             box.y += sh;
35701             this.split.el.setLeft(box.x);
35702             this.split.el.setTop(box.y-sh);
35703             this.split.el.setWidth(box.width);
35704         }
35705         if(this.collapsed){
35706             this.updateBody(box.width, null);
35707         }
35708         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35709     }
35710 });
35711
35712 Roo.EastLayoutRegion = function(mgr, config){
35713     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35714     if(this.split){
35715         this.split.placement = Roo.SplitBar.RIGHT;
35716         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35717         this.split.el.addClass("x-layout-split-h");
35718     }
35719     var size = config.initialSize || config.width;
35720     if(typeof size != "undefined"){
35721         this.el.setWidth(size);
35722     }
35723 };
35724 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35725     orientation: Roo.SplitBar.HORIZONTAL,
35726     getBox : function(){
35727         if(this.collapsed){
35728             return this.collapsedEl.getBox();
35729         }
35730         var box = this.el.getBox();
35731         if(this.split){
35732             var sw = this.split.el.getWidth();
35733             box.width += sw;
35734             box.x -= sw;
35735         }
35736         return box;
35737     },
35738
35739     updateBox : function(box){
35740         if(this.split && !this.collapsed){
35741             var sw = this.split.el.getWidth();
35742             box.width -= sw;
35743             this.split.el.setLeft(box.x);
35744             this.split.el.setTop(box.y);
35745             this.split.el.setHeight(box.height);
35746             box.x += sw;
35747         }
35748         if(this.collapsed){
35749             this.updateBody(null, box.height);
35750         }
35751         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35752     }
35753 });
35754
35755 Roo.WestLayoutRegion = function(mgr, config){
35756     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35757     if(this.split){
35758         this.split.placement = Roo.SplitBar.LEFT;
35759         this.split.orientation = Roo.SplitBar.HORIZONTAL;
35760         this.split.el.addClass("x-layout-split-h");
35761     }
35762     var size = config.initialSize || config.width;
35763     if(typeof size != "undefined"){
35764         this.el.setWidth(size);
35765     }
35766 };
35767 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35768     orientation: Roo.SplitBar.HORIZONTAL,
35769     getBox : function(){
35770         if(this.collapsed){
35771             return this.collapsedEl.getBox();
35772         }
35773         var box = this.el.getBox();
35774         if(this.split){
35775             box.width += this.split.el.getWidth();
35776         }
35777         return box;
35778     },
35779     
35780     updateBox : function(box){
35781         if(this.split && !this.collapsed){
35782             var sw = this.split.el.getWidth();
35783             box.width -= sw;
35784             this.split.el.setLeft(box.x+box.width);
35785             this.split.el.setTop(box.y);
35786             this.split.el.setHeight(box.height);
35787         }
35788         if(this.collapsed){
35789             this.updateBody(null, box.height);
35790         }
35791         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35792     }
35793 });
35794 /*
35795  * Based on:
35796  * Ext JS Library 1.1.1
35797  * Copyright(c) 2006-2007, Ext JS, LLC.
35798  *
35799  * Originally Released Under LGPL - original licence link has changed is not relivant.
35800  *
35801  * Fork - LGPL
35802  * <script type="text/javascript">
35803  */
35804  
35805  
35806 /*
35807  * Private internal class for reading and applying state
35808  */
35809 Roo.LayoutStateManager = function(layout){
35810      // default empty state
35811      this.state = {
35812         north: {},
35813         south: {},
35814         east: {},
35815         west: {}       
35816     };
35817 };
35818
35819 Roo.LayoutStateManager.prototype = {
35820     init : function(layout, provider){
35821         this.provider = provider;
35822         var state = provider.get(layout.id+"-layout-state");
35823         if(state){
35824             var wasUpdating = layout.isUpdating();
35825             if(!wasUpdating){
35826                 layout.beginUpdate();
35827             }
35828             for(var key in state){
35829                 if(typeof state[key] != "function"){
35830                     var rstate = state[key];
35831                     var r = layout.getRegion(key);
35832                     if(r && rstate){
35833                         if(rstate.size){
35834                             r.resizeTo(rstate.size);
35835                         }
35836                         if(rstate.collapsed == true){
35837                             r.collapse(true);
35838                         }else{
35839                             r.expand(null, true);
35840                         }
35841                     }
35842                 }
35843             }
35844             if(!wasUpdating){
35845                 layout.endUpdate();
35846             }
35847             this.state = state; 
35848         }
35849         this.layout = layout;
35850         layout.on("regionresized", this.onRegionResized, this);
35851         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35852         layout.on("regionexpanded", this.onRegionExpanded, this);
35853     },
35854     
35855     storeState : function(){
35856         this.provider.set(this.layout.id+"-layout-state", this.state);
35857     },
35858     
35859     onRegionResized : function(region, newSize){
35860         this.state[region.getPosition()].size = newSize;
35861         this.storeState();
35862     },
35863     
35864     onRegionCollapsed : function(region){
35865         this.state[region.getPosition()].collapsed = true;
35866         this.storeState();
35867     },
35868     
35869     onRegionExpanded : function(region){
35870         this.state[region.getPosition()].collapsed = false;
35871         this.storeState();
35872     }
35873 };/*
35874  * Based on:
35875  * Ext JS Library 1.1.1
35876  * Copyright(c) 2006-2007, Ext JS, LLC.
35877  *
35878  * Originally Released Under LGPL - original licence link has changed is not relivant.
35879  *
35880  * Fork - LGPL
35881  * <script type="text/javascript">
35882  */
35883 /**
35884  * @class Roo.ContentPanel
35885  * @extends Roo.util.Observable
35886  * @children Roo.form.Form Roo.JsonView Roo.View
35887  * @parent Roo.BorderLayout Roo.LayoutDialog builder
35888  * A basic ContentPanel element.
35889  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35890  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35891  * @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
35892  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35893  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35894  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35895  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
35896  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35897  * @cfg {String} title          The title for this panel
35898  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35899  * @cfg {String} url            Calls {@link #setUrl} with this value
35900  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35901  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35902  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35903  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35904  * @cfg {String}    style  Extra style to add to the content panel
35905  * @cfg {Roo.menu.Menu} menu  popup menu
35906
35907  * @constructor
35908  * Create a new ContentPanel.
35909  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35910  * @param {String/Object} config A string to set only the title or a config object
35911  * @param {String} content (optional) Set the HTML content for this panel
35912  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35913  */
35914 Roo.ContentPanel = function(el, config, content){
35915     
35916      
35917     /*
35918     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35919         config = el;
35920         el = Roo.id();
35921     }
35922     if (config && config.parentLayout) { 
35923         el = config.parentLayout.el.createChild(); 
35924     }
35925     */
35926     if(el.autoCreate){ // xtype is available if this is called from factory
35927         config = el;
35928         el = Roo.id();
35929     }
35930     this.el = Roo.get(el);
35931     if(!this.el && config && config.autoCreate){
35932         if(typeof config.autoCreate == "object"){
35933             if(!config.autoCreate.id){
35934                 config.autoCreate.id = config.id||el;
35935             }
35936             this.el = Roo.DomHelper.append(document.body,
35937                         config.autoCreate, true);
35938         }else{
35939             this.el = Roo.DomHelper.append(document.body,
35940                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35941         }
35942     }
35943     
35944     
35945     this.closable = false;
35946     this.loaded = false;
35947     this.active = false;
35948     if(typeof config == "string"){
35949         this.title = config;
35950     }else{
35951         Roo.apply(this, config);
35952     }
35953     
35954     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35955         this.wrapEl = this.el.wrap();
35956         this.toolbar.container = this.el.insertSibling(false, 'before');
35957         this.toolbar = new Roo.Toolbar(this.toolbar);
35958     }
35959     
35960     // xtype created footer. - not sure if will work as we normally have to render first..
35961     if (this.footer && !this.footer.el && this.footer.xtype) {
35962         if (!this.wrapEl) {
35963             this.wrapEl = this.el.wrap();
35964         }
35965     
35966         this.footer.container = this.wrapEl.createChild();
35967          
35968         this.footer = Roo.factory(this.footer, Roo);
35969         
35970     }
35971     
35972     if(this.resizeEl){
35973         this.resizeEl = Roo.get(this.resizeEl, true);
35974     }else{
35975         this.resizeEl = this.el;
35976     }
35977     // handle view.xtype
35978     
35979  
35980     
35981     
35982     this.addEvents({
35983         /**
35984          * @event activate
35985          * Fires when this panel is activated. 
35986          * @param {Roo.ContentPanel} this
35987          */
35988         "activate" : true,
35989         /**
35990          * @event deactivate
35991          * Fires when this panel is activated. 
35992          * @param {Roo.ContentPanel} this
35993          */
35994         "deactivate" : true,
35995
35996         /**
35997          * @event resize
35998          * Fires when this panel is resized if fitToFrame is true.
35999          * @param {Roo.ContentPanel} this
36000          * @param {Number} width The width after any component adjustments
36001          * @param {Number} height The height after any component adjustments
36002          */
36003         "resize" : true,
36004         
36005          /**
36006          * @event render
36007          * Fires when this tab is created
36008          * @param {Roo.ContentPanel} this
36009          */
36010         "render" : true
36011          
36012         
36013     });
36014     
36015
36016     
36017     
36018     if(this.autoScroll){
36019         this.resizeEl.setStyle("overflow", "auto");
36020     } else {
36021         // fix randome scrolling
36022         this.el.on('scroll', function() {
36023             Roo.log('fix random scolling');
36024             this.scrollTo('top',0); 
36025         });
36026     }
36027     content = content || this.content;
36028     if(content){
36029         this.setContent(content);
36030     }
36031     if(config && config.url){
36032         this.setUrl(this.url, this.params, this.loadOnce);
36033     }
36034     
36035     
36036     
36037     Roo.ContentPanel.superclass.constructor.call(this);
36038     
36039     if (this.view && typeof(this.view.xtype) != 'undefined') {
36040         this.view.el = this.el.appendChild(document.createElement("div"));
36041         this.view = Roo.factory(this.view); 
36042         this.view.render  &&  this.view.render(false, '');  
36043     }
36044     
36045     
36046     this.fireEvent('render', this);
36047 };
36048
36049 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
36050     tabTip:'',
36051     setRegion : function(region){
36052         this.region = region;
36053         if(region){
36054            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
36055         }else{
36056            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
36057         } 
36058     },
36059     
36060     /**
36061      * Returns the toolbar for this Panel if one was configured. 
36062      * @return {Roo.Toolbar} 
36063      */
36064     getToolbar : function(){
36065         return this.toolbar;
36066     },
36067     
36068     setActiveState : function(active){
36069         this.active = active;
36070         if(!active){
36071             this.fireEvent("deactivate", this);
36072         }else{
36073             this.fireEvent("activate", this);
36074         }
36075     },
36076     /**
36077      * Updates this panel's element
36078      * @param {String} content The new content
36079      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36080     */
36081     setContent : function(content, loadScripts){
36082         this.el.update(content, loadScripts);
36083     },
36084
36085     ignoreResize : function(w, h){
36086         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36087             return true;
36088         }else{
36089             this.lastSize = {width: w, height: h};
36090             return false;
36091         }
36092     },
36093     /**
36094      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36095      * @return {Roo.UpdateManager} The UpdateManager
36096      */
36097     getUpdateManager : function(){
36098         return this.el.getUpdateManager();
36099     },
36100      /**
36101      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36102      * @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:
36103 <pre><code>
36104 panel.load({
36105     url: "your-url.php",
36106     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36107     callback: yourFunction,
36108     scope: yourObject, //(optional scope)
36109     discardUrl: false,
36110     nocache: false,
36111     text: "Loading...",
36112     timeout: 30,
36113     scripts: false
36114 });
36115 </code></pre>
36116      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36117      * 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.
36118      * @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}
36119      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36120      * @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.
36121      * @return {Roo.ContentPanel} this
36122      */
36123     load : function(){
36124         var um = this.el.getUpdateManager();
36125         um.update.apply(um, arguments);
36126         return this;
36127     },
36128
36129
36130     /**
36131      * 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.
36132      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36133      * @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)
36134      * @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)
36135      * @return {Roo.UpdateManager} The UpdateManager
36136      */
36137     setUrl : function(url, params, loadOnce){
36138         if(this.refreshDelegate){
36139             this.removeListener("activate", this.refreshDelegate);
36140         }
36141         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36142         this.on("activate", this.refreshDelegate);
36143         return this.el.getUpdateManager();
36144     },
36145     
36146     _handleRefresh : function(url, params, loadOnce){
36147         if(!loadOnce || !this.loaded){
36148             var updater = this.el.getUpdateManager();
36149             updater.update(url, params, this._setLoaded.createDelegate(this));
36150         }
36151     },
36152     
36153     _setLoaded : function(){
36154         this.loaded = true;
36155     }, 
36156     
36157     /**
36158      * Returns this panel's id
36159      * @return {String} 
36160      */
36161     getId : function(){
36162         return this.el.id;
36163     },
36164     
36165     /** 
36166      * Returns this panel's element - used by regiosn to add.
36167      * @return {Roo.Element} 
36168      */
36169     getEl : function(){
36170         return this.wrapEl || this.el;
36171     },
36172     
36173     adjustForComponents : function(width, height)
36174     {
36175         //Roo.log('adjustForComponents ');
36176         if(this.resizeEl != this.el){
36177             width -= this.el.getFrameWidth('lr');
36178             height -= this.el.getFrameWidth('tb');
36179         }
36180         if(this.toolbar){
36181             var te = this.toolbar.getEl();
36182             height -= te.getHeight();
36183             te.setWidth(width);
36184         }
36185         if(this.footer){
36186             var te = this.footer.getEl();
36187             //Roo.log("footer:" + te.getHeight());
36188             
36189             height -= te.getHeight();
36190             te.setWidth(width);
36191         }
36192         
36193         
36194         if(this.adjustments){
36195             width += this.adjustments[0];
36196             height += this.adjustments[1];
36197         }
36198         return {"width": width, "height": height};
36199     },
36200     
36201     setSize : function(width, height){
36202         if(this.fitToFrame && !this.ignoreResize(width, height)){
36203             if(this.fitContainer && this.resizeEl != this.el){
36204                 this.el.setSize(width, height);
36205             }
36206             var size = this.adjustForComponents(width, height);
36207             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36208             this.fireEvent('resize', this, size.width, size.height);
36209         }
36210     },
36211     
36212     /**
36213      * Returns this panel's title
36214      * @return {String} 
36215      */
36216     getTitle : function(){
36217         return this.title;
36218     },
36219     
36220     /**
36221      * Set this panel's title
36222      * @param {String} title
36223      */
36224     setTitle : function(title){
36225         this.title = title;
36226         if(this.region){
36227             this.region.updatePanelTitle(this, title);
36228         }
36229     },
36230     
36231     /**
36232      * Returns true is this panel was configured to be closable
36233      * @return {Boolean} 
36234      */
36235     isClosable : function(){
36236         return this.closable;
36237     },
36238     
36239     beforeSlide : function(){
36240         this.el.clip();
36241         this.resizeEl.clip();
36242     },
36243     
36244     afterSlide : function(){
36245         this.el.unclip();
36246         this.resizeEl.unclip();
36247     },
36248     
36249     /**
36250      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36251      *   Will fail silently if the {@link #setUrl} method has not been called.
36252      *   This does not activate the panel, just updates its content.
36253      */
36254     refresh : function(){
36255         if(this.refreshDelegate){
36256            this.loaded = false;
36257            this.refreshDelegate();
36258         }
36259     },
36260     
36261     /**
36262      * Destroys this panel
36263      */
36264     destroy : function(){
36265         this.el.removeAllListeners();
36266         var tempEl = document.createElement("span");
36267         tempEl.appendChild(this.el.dom);
36268         tempEl.innerHTML = "";
36269         this.el.remove();
36270         this.el = null;
36271     },
36272     
36273     /**
36274      * form - if the content panel contains a form - this is a reference to it.
36275      * @type {Roo.form.Form}
36276      */
36277     form : false,
36278     /**
36279      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36280      *    This contains a reference to it.
36281      * @type {Roo.View}
36282      */
36283     view : false,
36284     
36285       /**
36286      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36287      * <pre><code>
36288
36289 layout.addxtype({
36290        xtype : 'Form',
36291        items: [ .... ]
36292    }
36293 );
36294
36295 </code></pre>
36296      * @param {Object} cfg Xtype definition of item to add.
36297      */
36298     
36299     addxtype : function(cfg) {
36300         // add form..
36301         if (cfg.xtype.match(/^Form$/)) {
36302             
36303             var el;
36304             //if (this.footer) {
36305             //    el = this.footer.container.insertSibling(false, 'before');
36306             //} else {
36307                 el = this.el.createChild();
36308             //}
36309
36310             this.form = new  Roo.form.Form(cfg);
36311             
36312             
36313             if ( this.form.allItems.length) {
36314                 this.form.render(el.dom);
36315             }
36316             return this.form;
36317         }
36318         // should only have one of theses..
36319         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36320             // views.. should not be just added - used named prop 'view''
36321             
36322             cfg.el = this.el.appendChild(document.createElement("div"));
36323             // factory?
36324             
36325             var ret = new Roo.factory(cfg);
36326              
36327              ret.render && ret.render(false, ''); // render blank..
36328             this.view = ret;
36329             return ret;
36330         }
36331         return false;
36332     }
36333 });
36334
36335
36336
36337
36338
36339
36340
36341
36342
36343
36344
36345
36346 /**
36347  * @class Roo.GridPanel
36348  * @extends Roo.ContentPanel
36349  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36350  * @constructor
36351  * Create a new GridPanel.
36352  * @cfg {Roo.grid.Grid} grid The grid for this panel
36353  */
36354 Roo.GridPanel = function(grid, config){
36355     
36356     // universal ctor...
36357     if (typeof(grid.grid) != 'undefined') {
36358         config = grid;
36359         grid = config.grid;
36360     }
36361     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36362         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36363         
36364     this.wrapper.dom.appendChild(grid.getGridEl().dom);
36365     
36366     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36367     
36368     if(this.toolbar){
36369         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36370     }
36371     // xtype created footer. - not sure if will work as we normally have to render first..
36372     if (this.footer && !this.footer.el && this.footer.xtype) {
36373         
36374         this.footer.container = this.grid.getView().getFooterPanel(true);
36375         this.footer.dataSource = this.grid.dataSource;
36376         this.footer = Roo.factory(this.footer, Roo);
36377         
36378     }
36379     
36380     grid.monitorWindowResize = false; // turn off autosizing
36381     grid.autoHeight = false;
36382     grid.autoWidth = false;
36383     this.grid = grid;
36384     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36385 };
36386
36387 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36388     getId : function(){
36389         return this.grid.id;
36390     },
36391     
36392     /**
36393      * Returns the grid for this panel
36394      * @return {Roo.grid.Grid} 
36395      */
36396     getGrid : function(){
36397         return this.grid;    
36398     },
36399     
36400     setSize : function(width, height){
36401         if(!this.ignoreResize(width, height)){
36402             var grid = this.grid;
36403             var size = this.adjustForComponents(width, height);
36404             grid.getGridEl().setSize(size.width, size.height);
36405             grid.autoSize();
36406         }
36407     },
36408     
36409     beforeSlide : function(){
36410         this.grid.getView().scroller.clip();
36411     },
36412     
36413     afterSlide : function(){
36414         this.grid.getView().scroller.unclip();
36415     },
36416     
36417     destroy : function(){
36418         this.grid.destroy();
36419         delete this.grid;
36420         Roo.GridPanel.superclass.destroy.call(this); 
36421     }
36422 });
36423
36424
36425 /**
36426  * @class Roo.NestedLayoutPanel
36427  * @extends Roo.ContentPanel
36428  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36429  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
36430  *
36431  * 
36432  * @constructor
36433  * Create a new NestedLayoutPanel.
36434  * 
36435  * 
36436  * @param {Roo.BorderLayout} layout [required] The layout for this panel
36437  * @param {String/Object} config A string to set only the title or a config object
36438  */
36439 Roo.NestedLayoutPanel = function(layout, config)
36440 {
36441     // construct with only one argument..
36442     /* FIXME - implement nicer consturctors
36443     if (layout.layout) {
36444         config = layout;
36445         layout = config.layout;
36446         delete config.layout;
36447     }
36448     if (layout.xtype && !layout.getEl) {
36449         // then layout needs constructing..
36450         layout = Roo.factory(layout, Roo);
36451     }
36452     */
36453     
36454     
36455     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36456     
36457     layout.monitorWindowResize = false; // turn off autosizing
36458     this.layout = layout;
36459     this.layout.getEl().addClass("x-layout-nested-layout");
36460     
36461     
36462     
36463     
36464 };
36465
36466 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36467
36468     layout : false,
36469
36470     setSize : function(width, height){
36471         if(!this.ignoreResize(width, height)){
36472             var size = this.adjustForComponents(width, height);
36473             var el = this.layout.getEl();
36474             el.setSize(size.width, size.height);
36475             var touch = el.dom.offsetWidth;
36476             this.layout.layout();
36477             // ie requires a double layout on the first pass
36478             if(Roo.isIE && !this.initialized){
36479                 this.initialized = true;
36480                 this.layout.layout();
36481             }
36482         }
36483     },
36484     
36485     // activate all subpanels if not currently active..
36486     
36487     setActiveState : function(active){
36488         this.active = active;
36489         if(!active){
36490             this.fireEvent("deactivate", this);
36491             return;
36492         }
36493         
36494         this.fireEvent("activate", this);
36495         // not sure if this should happen before or after..
36496         if (!this.layout) {
36497             return; // should not happen..
36498         }
36499         var reg = false;
36500         for (var r in this.layout.regions) {
36501             reg = this.layout.getRegion(r);
36502             if (reg.getActivePanel()) {
36503                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36504                 reg.setActivePanel(reg.getActivePanel());
36505                 continue;
36506             }
36507             if (!reg.panels.length) {
36508                 continue;
36509             }
36510             reg.showPanel(reg.getPanel(0));
36511         }
36512         
36513         
36514         
36515         
36516     },
36517     
36518     /**
36519      * Returns the nested BorderLayout for this panel
36520      * @return {Roo.BorderLayout}
36521      */
36522     getLayout : function(){
36523         return this.layout;
36524     },
36525     
36526      /**
36527      * Adds a xtype elements to the layout of the nested panel
36528      * <pre><code>
36529
36530 panel.addxtype({
36531        xtype : 'ContentPanel',
36532        region: 'west',
36533        items: [ .... ]
36534    }
36535 );
36536
36537 panel.addxtype({
36538         xtype : 'NestedLayoutPanel',
36539         region: 'west',
36540         layout: {
36541            center: { },
36542            west: { }   
36543         },
36544         items : [ ... list of content panels or nested layout panels.. ]
36545    }
36546 );
36547 </code></pre>
36548      * @param {Object} cfg Xtype definition of item to add.
36549      */
36550     addxtype : function(cfg) {
36551         return this.layout.addxtype(cfg);
36552     
36553     }
36554 });
36555
36556 Roo.ScrollPanel = function(el, config, content){
36557     config = config || {};
36558     config.fitToFrame = true;
36559     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36560     
36561     this.el.dom.style.overflow = "hidden";
36562     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36563     this.el.removeClass("x-layout-inactive-content");
36564     this.el.on("mousewheel", this.onWheel, this);
36565
36566     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
36567     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
36568     up.unselectable(); down.unselectable();
36569     up.on("click", this.scrollUp, this);
36570     down.on("click", this.scrollDown, this);
36571     up.addClassOnOver("x-scroller-btn-over");
36572     down.addClassOnOver("x-scroller-btn-over");
36573     up.addClassOnClick("x-scroller-btn-click");
36574     down.addClassOnClick("x-scroller-btn-click");
36575     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36576
36577     this.resizeEl = this.el;
36578     this.el = wrap; this.up = up; this.down = down;
36579 };
36580
36581 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36582     increment : 100,
36583     wheelIncrement : 5,
36584     scrollUp : function(){
36585         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36586     },
36587
36588     scrollDown : function(){
36589         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36590     },
36591
36592     afterScroll : function(){
36593         var el = this.resizeEl;
36594         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36595         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36596         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36597     },
36598
36599     setSize : function(){
36600         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36601         this.afterScroll();
36602     },
36603
36604     onWheel : function(e){
36605         var d = e.getWheelDelta();
36606         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36607         this.afterScroll();
36608         e.stopEvent();
36609     },
36610
36611     setContent : function(content, loadScripts){
36612         this.resizeEl.update(content, loadScripts);
36613     }
36614
36615 });
36616
36617
36618
36619 /**
36620  * @class Roo.TreePanel
36621  * @extends Roo.ContentPanel
36622  * @parent Roo.BorderLayout Roo.LayoutDialog builder
36623  * Treepanel component
36624  * 
36625  * @constructor
36626  * Create a new TreePanel. - defaults to fit/scoll contents.
36627  * @param {String/Object} config A string to set only the panel's title, or a config object
36628  */
36629 Roo.TreePanel = function(config){
36630     var el = config.el;
36631     var tree = config.tree;
36632     delete config.tree; 
36633     delete config.el; // hopefull!
36634     
36635     // wrapper for IE7 strict & safari scroll issue
36636     
36637     var treeEl = el.createChild();
36638     config.resizeEl = treeEl;
36639     
36640     
36641     
36642     Roo.TreePanel.superclass.constructor.call(this, el, config);
36643  
36644  
36645     this.tree = new Roo.tree.TreePanel(treeEl , tree);
36646     //console.log(tree);
36647     this.on('activate', function()
36648     {
36649         if (this.tree.rendered) {
36650             return;
36651         }
36652         //console.log('render tree');
36653         this.tree.render();
36654     });
36655     // this should not be needed.. - it's actually the 'el' that resizes?
36656     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36657     
36658     //this.on('resize',  function (cp, w, h) {
36659     //        this.tree.innerCt.setWidth(w);
36660     //        this.tree.innerCt.setHeight(h);
36661     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
36662     //});
36663
36664         
36665     
36666 };
36667
36668 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
36669     fitToFrame : true,
36670     autoScroll : true,
36671     /*
36672      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36673      */
36674     tree : false
36675
36676 });
36677 /*
36678  * Based on:
36679  * Ext JS Library 1.1.1
36680  * Copyright(c) 2006-2007, Ext JS, LLC.
36681  *
36682  * Originally Released Under LGPL - original licence link has changed is not relivant.
36683  *
36684  * Fork - LGPL
36685  * <script type="text/javascript">
36686  */
36687  
36688
36689 /**
36690  * @class Roo.ReaderLayout
36691  * @extends Roo.BorderLayout
36692  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
36693  * center region containing two nested regions (a top one for a list view and one for item preview below),
36694  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36695  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36696  * expedites the setup of the overall layout and regions for this common application style.
36697  * Example:
36698  <pre><code>
36699 var reader = new Roo.ReaderLayout();
36700 var CP = Roo.ContentPanel;  // shortcut for adding
36701
36702 reader.beginUpdate();
36703 reader.add("north", new CP("north", "North"));
36704 reader.add("west", new CP("west", {title: "West"}));
36705 reader.add("east", new CP("east", {title: "East"}));
36706
36707 reader.regions.listView.add(new CP("listView", "List"));
36708 reader.regions.preview.add(new CP("preview", "Preview"));
36709 reader.endUpdate();
36710 </code></pre>
36711 * @constructor
36712 * Create a new ReaderLayout
36713 * @param {Object} config Configuration options
36714 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36715 * document.body if omitted)
36716 */
36717 Roo.ReaderLayout = function(config, renderTo){
36718     var c = config || {size:{}};
36719     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36720         north: c.north !== false ? Roo.apply({
36721             split:false,
36722             initialSize: 32,
36723             titlebar: false
36724         }, c.north) : false,
36725         west: c.west !== false ? Roo.apply({
36726             split:true,
36727             initialSize: 200,
36728             minSize: 175,
36729             maxSize: 400,
36730             titlebar: true,
36731             collapsible: true,
36732             animate: true,
36733             margins:{left:5,right:0,bottom:5,top:5},
36734             cmargins:{left:5,right:5,bottom:5,top:5}
36735         }, c.west) : false,
36736         east: c.east !== false ? Roo.apply({
36737             split:true,
36738             initialSize: 200,
36739             minSize: 175,
36740             maxSize: 400,
36741             titlebar: true,
36742             collapsible: true,
36743             animate: true,
36744             margins:{left:0,right:5,bottom:5,top:5},
36745             cmargins:{left:5,right:5,bottom:5,top:5}
36746         }, c.east) : false,
36747         center: Roo.apply({
36748             tabPosition: 'top',
36749             autoScroll:false,
36750             closeOnTab: true,
36751             titlebar:false,
36752             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36753         }, c.center)
36754     });
36755
36756     this.el.addClass('x-reader');
36757
36758     this.beginUpdate();
36759
36760     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36761         south: c.preview !== false ? Roo.apply({
36762             split:true,
36763             initialSize: 200,
36764             minSize: 100,
36765             autoScroll:true,
36766             collapsible:true,
36767             titlebar: true,
36768             cmargins:{top:5,left:0, right:0, bottom:0}
36769         }, c.preview) : false,
36770         center: Roo.apply({
36771             autoScroll:false,
36772             titlebar:false,
36773             minHeight:200
36774         }, c.listView)
36775     });
36776     this.add('center', new Roo.NestedLayoutPanel(inner,
36777             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36778
36779     this.endUpdate();
36780
36781     this.regions.preview = inner.getRegion('south');
36782     this.regions.listView = inner.getRegion('center');
36783 };
36784
36785 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36786  * Based on:
36787  * Ext JS Library 1.1.1
36788  * Copyright(c) 2006-2007, Ext JS, LLC.
36789  *
36790  * Originally Released Under LGPL - original licence link has changed is not relivant.
36791  *
36792  * Fork - LGPL
36793  * <script type="text/javascript">
36794  */
36795  
36796 /**
36797  * @class Roo.grid.Grid
36798  * @extends Roo.util.Observable
36799  * This class represents the primary interface of a component based grid control.
36800  * <br><br>Usage:<pre><code>
36801  var grid = new Roo.grid.Grid("my-container-id", {
36802      ds: myDataStore,
36803      cm: myColModel,
36804      selModel: mySelectionModel,
36805      autoSizeColumns: true,
36806      monitorWindowResize: false,
36807      trackMouseOver: true
36808  });
36809  // set any options
36810  grid.render();
36811  * </code></pre>
36812  * <b>Common Problems:</b><br/>
36813  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36814  * element will correct this<br/>
36815  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36816  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36817  * are unpredictable.<br/>
36818  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36819  * grid to calculate dimensions/offsets.<br/>
36820   * @constructor
36821  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36822  * The container MUST have some type of size defined for the grid to fill. The container will be
36823  * automatically set to position relative if it isn't already.
36824  * @param {Object} config A config object that sets properties on this grid.
36825  */
36826 Roo.grid.Grid = function(container, config){
36827         // initialize the container
36828         this.container = Roo.get(container);
36829         this.container.update("");
36830         this.container.setStyle("overflow", "hidden");
36831     this.container.addClass('x-grid-container');
36832
36833     this.id = this.container.id;
36834
36835     Roo.apply(this, config);
36836     // check and correct shorthanded configs
36837     if(this.ds){
36838         this.dataSource = this.ds;
36839         delete this.ds;
36840     }
36841     if(this.cm){
36842         this.colModel = this.cm;
36843         delete this.cm;
36844     }
36845     if(this.sm){
36846         this.selModel = this.sm;
36847         delete this.sm;
36848     }
36849
36850     if (this.selModel) {
36851         this.selModel = Roo.factory(this.selModel, Roo.grid);
36852         this.sm = this.selModel;
36853         this.sm.xmodule = this.xmodule || false;
36854     }
36855     if (typeof(this.colModel.config) == 'undefined') {
36856         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36857         this.cm = this.colModel;
36858         this.cm.xmodule = this.xmodule || false;
36859     }
36860     if (this.dataSource) {
36861         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36862         this.ds = this.dataSource;
36863         this.ds.xmodule = this.xmodule || false;
36864          
36865     }
36866     
36867     
36868     
36869     if(this.width){
36870         this.container.setWidth(this.width);
36871     }
36872
36873     if(this.height){
36874         this.container.setHeight(this.height);
36875     }
36876     /** @private */
36877         this.addEvents({
36878         // raw events
36879         /**
36880          * @event click
36881          * The raw click event for the entire grid.
36882          * @param {Roo.EventObject} e
36883          */
36884         "click" : true,
36885         /**
36886          * @event dblclick
36887          * The raw dblclick event for the entire grid.
36888          * @param {Roo.EventObject} e
36889          */
36890         "dblclick" : true,
36891         /**
36892          * @event contextmenu
36893          * The raw contextmenu event for the entire grid.
36894          * @param {Roo.EventObject} e
36895          */
36896         "contextmenu" : true,
36897         /**
36898          * @event mousedown
36899          * The raw mousedown event for the entire grid.
36900          * @param {Roo.EventObject} e
36901          */
36902         "mousedown" : true,
36903         /**
36904          * @event mouseup
36905          * The raw mouseup event for the entire grid.
36906          * @param {Roo.EventObject} e
36907          */
36908         "mouseup" : true,
36909         /**
36910          * @event mouseover
36911          * The raw mouseover event for the entire grid.
36912          * @param {Roo.EventObject} e
36913          */
36914         "mouseover" : true,
36915         /**
36916          * @event mouseout
36917          * The raw mouseout event for the entire grid.
36918          * @param {Roo.EventObject} e
36919          */
36920         "mouseout" : true,
36921         /**
36922          * @event keypress
36923          * The raw keypress event for the entire grid.
36924          * @param {Roo.EventObject} e
36925          */
36926         "keypress" : true,
36927         /**
36928          * @event keydown
36929          * The raw keydown event for the entire grid.
36930          * @param {Roo.EventObject} e
36931          */
36932         "keydown" : true,
36933
36934         // custom events
36935
36936         /**
36937          * @event cellclick
36938          * Fires when a cell is clicked
36939          * @param {Grid} this
36940          * @param {Number} rowIndex
36941          * @param {Number} columnIndex
36942          * @param {Roo.EventObject} e
36943          */
36944         "cellclick" : true,
36945         /**
36946          * @event celldblclick
36947          * Fires when a cell is double clicked
36948          * @param {Grid} this
36949          * @param {Number} rowIndex
36950          * @param {Number} columnIndex
36951          * @param {Roo.EventObject} e
36952          */
36953         "celldblclick" : true,
36954         /**
36955          * @event rowclick
36956          * Fires when a row is clicked
36957          * @param {Grid} this
36958          * @param {Number} rowIndex
36959          * @param {Roo.EventObject} e
36960          */
36961         "rowclick" : true,
36962         /**
36963          * @event rowdblclick
36964          * Fires when a row is double clicked
36965          * @param {Grid} this
36966          * @param {Number} rowIndex
36967          * @param {Roo.EventObject} e
36968          */
36969         "rowdblclick" : true,
36970         /**
36971          * @event headerclick
36972          * Fires when a header is clicked
36973          * @param {Grid} this
36974          * @param {Number} columnIndex
36975          * @param {Roo.EventObject} e
36976          */
36977         "headerclick" : true,
36978         /**
36979          * @event headerdblclick
36980          * Fires when a header cell is double clicked
36981          * @param {Grid} this
36982          * @param {Number} columnIndex
36983          * @param {Roo.EventObject} e
36984          */
36985         "headerdblclick" : true,
36986         /**
36987          * @event rowcontextmenu
36988          * Fires when a row is right clicked
36989          * @param {Grid} this
36990          * @param {Number} rowIndex
36991          * @param {Roo.EventObject} e
36992          */
36993         "rowcontextmenu" : true,
36994         /**
36995          * @event cellcontextmenu
36996          * Fires when a cell is right clicked
36997          * @param {Grid} this
36998          * @param {Number} rowIndex
36999          * @param {Number} cellIndex
37000          * @param {Roo.EventObject} e
37001          */
37002          "cellcontextmenu" : true,
37003         /**
37004          * @event headercontextmenu
37005          * Fires when a header is right clicked
37006          * @param {Grid} this
37007          * @param {Number} columnIndex
37008          * @param {Roo.EventObject} e
37009          */
37010         "headercontextmenu" : true,
37011         /**
37012          * @event bodyscroll
37013          * Fires when the body element is scrolled
37014          * @param {Number} scrollLeft
37015          * @param {Number} scrollTop
37016          */
37017         "bodyscroll" : true,
37018         /**
37019          * @event columnresize
37020          * Fires when the user resizes a column
37021          * @param {Number} columnIndex
37022          * @param {Number} newSize
37023          */
37024         "columnresize" : true,
37025         /**
37026          * @event columnmove
37027          * Fires when the user moves a column
37028          * @param {Number} oldIndex
37029          * @param {Number} newIndex
37030          */
37031         "columnmove" : true,
37032         /**
37033          * @event startdrag
37034          * Fires when row(s) start being dragged
37035          * @param {Grid} this
37036          * @param {Roo.GridDD} dd The drag drop object
37037          * @param {event} e The raw browser event
37038          */
37039         "startdrag" : true,
37040         /**
37041          * @event enddrag
37042          * Fires when a drag operation is complete
37043          * @param {Grid} this
37044          * @param {Roo.GridDD} dd The drag drop object
37045          * @param {event} e The raw browser event
37046          */
37047         "enddrag" : true,
37048         /**
37049          * @event dragdrop
37050          * Fires when dragged row(s) are dropped on a valid DD target
37051          * @param {Grid} this
37052          * @param {Roo.GridDD} dd The drag drop object
37053          * @param {String} targetId The target drag drop object
37054          * @param {event} e The raw browser event
37055          */
37056         "dragdrop" : true,
37057         /**
37058          * @event dragover
37059          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37060          * @param {Grid} this
37061          * @param {Roo.GridDD} dd The drag drop object
37062          * @param {String} targetId The target drag drop object
37063          * @param {event} e The raw browser event
37064          */
37065         "dragover" : true,
37066         /**
37067          * @event dragenter
37068          *  Fires when the dragged row(s) first cross another DD target while being dragged
37069          * @param {Grid} this
37070          * @param {Roo.GridDD} dd The drag drop object
37071          * @param {String} targetId The target drag drop object
37072          * @param {event} e The raw browser event
37073          */
37074         "dragenter" : true,
37075         /**
37076          * @event dragout
37077          * Fires when the dragged row(s) leave another DD target while being dragged
37078          * @param {Grid} this
37079          * @param {Roo.GridDD} dd The drag drop object
37080          * @param {String} targetId The target drag drop object
37081          * @param {event} e The raw browser event
37082          */
37083         "dragout" : true,
37084         /**
37085          * @event rowclass
37086          * Fires when a row is rendered, so you can change add a style to it.
37087          * @param {GridView} gridview   The grid view
37088          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37089          */
37090         'rowclass' : true,
37091
37092         /**
37093          * @event render
37094          * Fires when the grid is rendered
37095          * @param {Grid} grid
37096          */
37097         'render' : true
37098     });
37099
37100     Roo.grid.Grid.superclass.constructor.call(this);
37101 };
37102 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
37103     
37104     /**
37105          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
37106          */
37107         /**
37108          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
37109          */
37110         /**
37111          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
37112          */
37113         /**
37114          * @cfg {Roo.data.Store} ds The data store for the grid
37115          */
37116         /**
37117          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
37118          */
37119         /**
37120      * @cfg {String} ddGroup - drag drop group.
37121      */
37122       /**
37123      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
37124      */
37125
37126     /**
37127      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
37128      */
37129     minColumnWidth : 25,
37130
37131     /**
37132      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
37133      * <b>on initial render.</b> It is more efficient to explicitly size the columns
37134      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
37135      */
37136     autoSizeColumns : false,
37137
37138     /**
37139      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
37140      */
37141     autoSizeHeaders : true,
37142
37143     /**
37144      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
37145      */
37146     monitorWindowResize : true,
37147
37148     /**
37149      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
37150      * rows measured to get a columns size. Default is 0 (all rows).
37151      */
37152     maxRowsToMeasure : 0,
37153
37154     /**
37155      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
37156      */
37157     trackMouseOver : true,
37158
37159     /**
37160     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
37161     */
37162       /**
37163     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
37164     */
37165     
37166     /**
37167     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
37168     */
37169     enableDragDrop : false,
37170     
37171     /**
37172     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
37173     */
37174     enableColumnMove : true,
37175     
37176     /**
37177     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
37178     */
37179     enableColumnHide : true,
37180     
37181     /**
37182     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
37183     */
37184     enableRowHeightSync : false,
37185     
37186     /**
37187     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
37188     */
37189     stripeRows : true,
37190     
37191     /**
37192     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
37193     */
37194     autoHeight : false,
37195
37196     /**
37197      * @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.
37198      */
37199     autoExpandColumn : false,
37200
37201     /**
37202     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
37203     * Default is 50.
37204     */
37205     autoExpandMin : 50,
37206
37207     /**
37208     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37209     */
37210     autoExpandMax : 1000,
37211
37212     /**
37213     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37214     */
37215     view : null,
37216
37217     /**
37218     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37219     */
37220     loadMask : false,
37221     /**
37222     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37223     */
37224     dropTarget: false,
37225      /**
37226     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37227     */ 
37228     sortColMenu : false,
37229     
37230     // private
37231     rendered : false,
37232
37233     /**
37234     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37235     * of a fixed width. Default is false.
37236     */
37237     /**
37238     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37239     */
37240     
37241     
37242     /**
37243     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37244     * %0 is replaced with the number of selected rows.
37245     */
37246     ddText : "{0} selected row{1}",
37247     
37248     
37249     /**
37250      * Called once after all setup has been completed and the grid is ready to be rendered.
37251      * @return {Roo.grid.Grid} this
37252      */
37253     render : function()
37254     {
37255         var c = this.container;
37256         // try to detect autoHeight/width mode
37257         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37258             this.autoHeight = true;
37259         }
37260         var view = this.getView();
37261         view.init(this);
37262
37263         c.on("click", this.onClick, this);
37264         c.on("dblclick", this.onDblClick, this);
37265         c.on("contextmenu", this.onContextMenu, this);
37266         c.on("keydown", this.onKeyDown, this);
37267         if (Roo.isTouch) {
37268             c.on("touchstart", this.onTouchStart, this);
37269         }
37270
37271         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37272
37273         this.getSelectionModel().init(this);
37274
37275         view.render();
37276
37277         if(this.loadMask){
37278             this.loadMask = new Roo.LoadMask(this.container,
37279                     Roo.apply({store:this.dataSource}, this.loadMask));
37280         }
37281         
37282         
37283         if (this.toolbar && this.toolbar.xtype) {
37284             this.toolbar.container = this.getView().getHeaderPanel(true);
37285             this.toolbar = new Roo.Toolbar(this.toolbar);
37286         }
37287         if (this.footer && this.footer.xtype) {
37288             this.footer.dataSource = this.getDataSource();
37289             this.footer.container = this.getView().getFooterPanel(true);
37290             this.footer = Roo.factory(this.footer, Roo);
37291         }
37292         if (this.dropTarget && this.dropTarget.xtype) {
37293             delete this.dropTarget.xtype;
37294             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37295         }
37296         
37297         
37298         this.rendered = true;
37299         this.fireEvent('render', this);
37300         return this;
37301     },
37302
37303     /**
37304      * Reconfigures the grid to use a different Store and Column Model.
37305      * The View will be bound to the new objects and refreshed.
37306      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37307      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37308      */
37309     reconfigure : function(dataSource, colModel){
37310         if(this.loadMask){
37311             this.loadMask.destroy();
37312             this.loadMask = new Roo.LoadMask(this.container,
37313                     Roo.apply({store:dataSource}, this.loadMask));
37314         }
37315         this.view.bind(dataSource, colModel);
37316         this.dataSource = dataSource;
37317         this.colModel = colModel;
37318         this.view.refresh(true);
37319     },
37320     /**
37321      * addColumns
37322      * Add's a column, default at the end..
37323      
37324      * @param {int} position to add (default end)
37325      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
37326      */
37327     addColumns : function(pos, ar)
37328     {
37329         
37330         for (var i =0;i< ar.length;i++) {
37331             var cfg = ar[i];
37332             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37333             this.cm.lookup[cfg.id] = cfg;
37334         }
37335         
37336         
37337         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37338             pos = this.cm.config.length; //this.cm.config.push(cfg);
37339         } 
37340         pos = Math.max(0,pos);
37341         ar.unshift(0);
37342         ar.unshift(pos);
37343         this.cm.config.splice.apply(this.cm.config, ar);
37344         
37345         
37346         
37347         this.view.generateRules(this.cm);
37348         this.view.refresh(true);
37349         
37350     },
37351     
37352     
37353     
37354     
37355     // private
37356     onKeyDown : function(e){
37357         this.fireEvent("keydown", e);
37358     },
37359
37360     /**
37361      * Destroy this grid.
37362      * @param {Boolean} removeEl True to remove the element
37363      */
37364     destroy : function(removeEl, keepListeners){
37365         if(this.loadMask){
37366             this.loadMask.destroy();
37367         }
37368         var c = this.container;
37369         c.removeAllListeners();
37370         this.view.destroy();
37371         this.colModel.purgeListeners();
37372         if(!keepListeners){
37373             this.purgeListeners();
37374         }
37375         c.update("");
37376         if(removeEl === true){
37377             c.remove();
37378         }
37379     },
37380
37381     // private
37382     processEvent : function(name, e){
37383         // does this fire select???
37384         //Roo.log('grid:processEvent '  + name);
37385         
37386         if (name != 'touchstart' ) {
37387             this.fireEvent(name, e);    
37388         }
37389         
37390         var t = e.getTarget();
37391         var v = this.view;
37392         var header = v.findHeaderIndex(t);
37393         if(header !== false){
37394             var ename = name == 'touchstart' ? 'click' : name;
37395              
37396             this.fireEvent("header" + ename, this, header, e);
37397         }else{
37398             var row = v.findRowIndex(t);
37399             var cell = v.findCellIndex(t);
37400             if (name == 'touchstart') {
37401                 // first touch is always a click.
37402                 // hopefull this happens after selection is updated.?
37403                 name = false;
37404                 
37405                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37406                     var cs = this.selModel.getSelectedCell();
37407                     if (row == cs[0] && cell == cs[1]){
37408                         name = 'dblclick';
37409                     }
37410                 }
37411                 if (typeof(this.selModel.getSelections) != 'undefined') {
37412                     var cs = this.selModel.getSelections();
37413                     var ds = this.dataSource;
37414                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
37415                         name = 'dblclick';
37416                     }
37417                 }
37418                 if (!name) {
37419                     return;
37420                 }
37421             }
37422             
37423             
37424             if(row !== false){
37425                 this.fireEvent("row" + name, this, row, e);
37426                 if(cell !== false){
37427                     this.fireEvent("cell" + name, this, row, cell, e);
37428                 }
37429             }
37430         }
37431     },
37432
37433     // private
37434     onClick : function(e){
37435         this.processEvent("click", e);
37436     },
37437    // private
37438     onTouchStart : function(e){
37439         this.processEvent("touchstart", e);
37440     },
37441
37442     // private
37443     onContextMenu : function(e, t){
37444         this.processEvent("contextmenu", e);
37445     },
37446
37447     // private
37448     onDblClick : function(e){
37449         this.processEvent("dblclick", e);
37450     },
37451
37452     // private
37453     walkCells : function(row, col, step, fn, scope){
37454         var cm = this.colModel, clen = cm.getColumnCount();
37455         var ds = this.dataSource, rlen = ds.getCount(), first = true;
37456         if(step < 0){
37457             if(col < 0){
37458                 row--;
37459                 first = false;
37460             }
37461             while(row >= 0){
37462                 if(!first){
37463                     col = clen-1;
37464                 }
37465                 first = false;
37466                 while(col >= 0){
37467                     if(fn.call(scope || this, row, col, cm) === true){
37468                         return [row, col];
37469                     }
37470                     col--;
37471                 }
37472                 row--;
37473             }
37474         } else {
37475             if(col >= clen){
37476                 row++;
37477                 first = false;
37478             }
37479             while(row < rlen){
37480                 if(!first){
37481                     col = 0;
37482                 }
37483                 first = false;
37484                 while(col < clen){
37485                     if(fn.call(scope || this, row, col, cm) === true){
37486                         return [row, col];
37487                     }
37488                     col++;
37489                 }
37490                 row++;
37491             }
37492         }
37493         return null;
37494     },
37495
37496     // private
37497     getSelections : function(){
37498         return this.selModel.getSelections();
37499     },
37500
37501     /**
37502      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37503      * but if manual update is required this method will initiate it.
37504      */
37505     autoSize : function(){
37506         if(this.rendered){
37507             this.view.layout();
37508             if(this.view.adjustForScroll){
37509                 this.view.adjustForScroll();
37510             }
37511         }
37512     },
37513
37514     /**
37515      * Returns the grid's underlying element.
37516      * @return {Element} The element
37517      */
37518     getGridEl : function(){
37519         return this.container;
37520     },
37521
37522     // private for compatibility, overridden by editor grid
37523     stopEditing : function(){},
37524
37525     /**
37526      * Returns the grid's SelectionModel.
37527      * @return {SelectionModel}
37528      */
37529     getSelectionModel : function(){
37530         if(!this.selModel){
37531             this.selModel = new Roo.grid.RowSelectionModel();
37532         }
37533         return this.selModel;
37534     },
37535
37536     /**
37537      * Returns the grid's DataSource.
37538      * @return {DataSource}
37539      */
37540     getDataSource : function(){
37541         return this.dataSource;
37542     },
37543
37544     /**
37545      * Returns the grid's ColumnModel.
37546      * @return {ColumnModel}
37547      */
37548     getColumnModel : function(){
37549         return this.colModel;
37550     },
37551
37552     /**
37553      * Returns the grid's GridView object.
37554      * @return {GridView}
37555      */
37556     getView : function(){
37557         if(!this.view){
37558             this.view = new Roo.grid.GridView(this.viewConfig);
37559             this.relayEvents(this.view, [
37560                 "beforerowremoved", "beforerowsinserted",
37561                 "beforerefresh", "rowremoved",
37562                 "rowsinserted", "rowupdated" ,"refresh"
37563             ]);
37564         }
37565         return this.view;
37566     },
37567     /**
37568      * Called to get grid's drag proxy text, by default returns this.ddText.
37569      * Override this to put something different in the dragged text.
37570      * @return {String}
37571      */
37572     getDragDropText : function(){
37573         var count = this.selModel.getCount();
37574         return String.format(this.ddText, count, count == 1 ? '' : 's');
37575     }
37576 });
37577 /*
37578  * Based on:
37579  * Ext JS Library 1.1.1
37580  * Copyright(c) 2006-2007, Ext JS, LLC.
37581  *
37582  * Originally Released Under LGPL - original licence link has changed is not relivant.
37583  *
37584  * Fork - LGPL
37585  * <script type="text/javascript">
37586  */
37587  /**
37588  * @class Roo.grid.AbstractGridView
37589  * @extends Roo.util.Observable
37590  * @abstract
37591  * Abstract base class for grid Views
37592  * @constructor
37593  */
37594 Roo.grid.AbstractGridView = function(){
37595         this.grid = null;
37596         
37597         this.events = {
37598             "beforerowremoved" : true,
37599             "beforerowsinserted" : true,
37600             "beforerefresh" : true,
37601             "rowremoved" : true,
37602             "rowsinserted" : true,
37603             "rowupdated" : true,
37604             "refresh" : true
37605         };
37606     Roo.grid.AbstractGridView.superclass.constructor.call(this);
37607 };
37608
37609 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37610     rowClass : "x-grid-row",
37611     cellClass : "x-grid-cell",
37612     tdClass : "x-grid-td",
37613     hdClass : "x-grid-hd",
37614     splitClass : "x-grid-hd-split",
37615     
37616     init: function(grid){
37617         this.grid = grid;
37618                 var cid = this.grid.getGridEl().id;
37619         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37620         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37621         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37622         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37623         },
37624         
37625     getColumnRenderers : function(){
37626         var renderers = [];
37627         var cm = this.grid.colModel;
37628         var colCount = cm.getColumnCount();
37629         for(var i = 0; i < colCount; i++){
37630             renderers[i] = cm.getRenderer(i);
37631         }
37632         return renderers;
37633     },
37634     
37635     getColumnIds : function(){
37636         var ids = [];
37637         var cm = this.grid.colModel;
37638         var colCount = cm.getColumnCount();
37639         for(var i = 0; i < colCount; i++){
37640             ids[i] = cm.getColumnId(i);
37641         }
37642         return ids;
37643     },
37644     
37645     getDataIndexes : function(){
37646         if(!this.indexMap){
37647             this.indexMap = this.buildIndexMap();
37648         }
37649         return this.indexMap.colToData;
37650     },
37651     
37652     getColumnIndexByDataIndex : function(dataIndex){
37653         if(!this.indexMap){
37654             this.indexMap = this.buildIndexMap();
37655         }
37656         return this.indexMap.dataToCol[dataIndex];
37657     },
37658     
37659     /**
37660      * Set a css style for a column dynamically. 
37661      * @param {Number} colIndex The index of the column
37662      * @param {String} name The css property name
37663      * @param {String} value The css value
37664      */
37665     setCSSStyle : function(colIndex, name, value){
37666         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37667         Roo.util.CSS.updateRule(selector, name, value);
37668     },
37669     
37670     generateRules : function(cm){
37671         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37672         Roo.util.CSS.removeStyleSheet(rulesId);
37673         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37674             var cid = cm.getColumnId(i);
37675             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37676                          this.tdSelector, cid, " {\n}\n",
37677                          this.hdSelector, cid, " {\n}\n",
37678                          this.splitSelector, cid, " {\n}\n");
37679         }
37680         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37681     }
37682 });/*
37683  * Based on:
37684  * Ext JS Library 1.1.1
37685  * Copyright(c) 2006-2007, Ext JS, LLC.
37686  *
37687  * Originally Released Under LGPL - original licence link has changed is not relivant.
37688  *
37689  * Fork - LGPL
37690  * <script type="text/javascript">
37691  */
37692
37693 // private
37694 // This is a support class used internally by the Grid components
37695 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37696     this.grid = grid;
37697     this.view = grid.getView();
37698     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37699     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37700     if(hd2){
37701         this.setHandleElId(Roo.id(hd));
37702         this.setOuterHandleElId(Roo.id(hd2));
37703     }
37704     this.scroll = false;
37705 };
37706 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37707     maxDragWidth: 120,
37708     getDragData : function(e){
37709         var t = Roo.lib.Event.getTarget(e);
37710         var h = this.view.findHeaderCell(t);
37711         if(h){
37712             return {ddel: h.firstChild, header:h};
37713         }
37714         return false;
37715     },
37716
37717     onInitDrag : function(e){
37718         this.view.headersDisabled = true;
37719         var clone = this.dragData.ddel.cloneNode(true);
37720         clone.id = Roo.id();
37721         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37722         this.proxy.update(clone);
37723         return true;
37724     },
37725
37726     afterValidDrop : function(){
37727         var v = this.view;
37728         setTimeout(function(){
37729             v.headersDisabled = false;
37730         }, 50);
37731     },
37732
37733     afterInvalidDrop : function(){
37734         var v = this.view;
37735         setTimeout(function(){
37736             v.headersDisabled = false;
37737         }, 50);
37738     }
37739 });
37740 /*
37741  * Based on:
37742  * Ext JS Library 1.1.1
37743  * Copyright(c) 2006-2007, Ext JS, LLC.
37744  *
37745  * Originally Released Under LGPL - original licence link has changed is not relivant.
37746  *
37747  * Fork - LGPL
37748  * <script type="text/javascript">
37749  */
37750 // private
37751 // This is a support class used internally by the Grid components
37752 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37753     this.grid = grid;
37754     this.view = grid.getView();
37755     // split the proxies so they don't interfere with mouse events
37756     this.proxyTop = Roo.DomHelper.append(document.body, {
37757         cls:"col-move-top", html:"&#160;"
37758     }, true);
37759     this.proxyBottom = Roo.DomHelper.append(document.body, {
37760         cls:"col-move-bottom", html:"&#160;"
37761     }, true);
37762     this.proxyTop.hide = this.proxyBottom.hide = function(){
37763         this.setLeftTop(-100,-100);
37764         this.setStyle("visibility", "hidden");
37765     };
37766     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37767     // temporarily disabled
37768     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37769     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37770 };
37771 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37772     proxyOffsets : [-4, -9],
37773     fly: Roo.Element.fly,
37774
37775     getTargetFromEvent : function(e){
37776         var t = Roo.lib.Event.getTarget(e);
37777         var cindex = this.view.findCellIndex(t);
37778         if(cindex !== false){
37779             return this.view.getHeaderCell(cindex);
37780         }
37781         return null;
37782     },
37783
37784     nextVisible : function(h){
37785         var v = this.view, cm = this.grid.colModel;
37786         h = h.nextSibling;
37787         while(h){
37788             if(!cm.isHidden(v.getCellIndex(h))){
37789                 return h;
37790             }
37791             h = h.nextSibling;
37792         }
37793         return null;
37794     },
37795
37796     prevVisible : function(h){
37797         var v = this.view, cm = this.grid.colModel;
37798         h = h.prevSibling;
37799         while(h){
37800             if(!cm.isHidden(v.getCellIndex(h))){
37801                 return h;
37802             }
37803             h = h.prevSibling;
37804         }
37805         return null;
37806     },
37807
37808     positionIndicator : function(h, n, e){
37809         var x = Roo.lib.Event.getPageX(e);
37810         var r = Roo.lib.Dom.getRegion(n.firstChild);
37811         var px, pt, py = r.top + this.proxyOffsets[1];
37812         if((r.right - x) <= (r.right-r.left)/2){
37813             px = r.right+this.view.borderWidth;
37814             pt = "after";
37815         }else{
37816             px = r.left;
37817             pt = "before";
37818         }
37819         var oldIndex = this.view.getCellIndex(h);
37820         var newIndex = this.view.getCellIndex(n);
37821
37822         if(this.grid.colModel.isFixed(newIndex)){
37823             return false;
37824         }
37825
37826         var locked = this.grid.colModel.isLocked(newIndex);
37827
37828         if(pt == "after"){
37829             newIndex++;
37830         }
37831         if(oldIndex < newIndex){
37832             newIndex--;
37833         }
37834         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37835             return false;
37836         }
37837         px +=  this.proxyOffsets[0];
37838         this.proxyTop.setLeftTop(px, py);
37839         this.proxyTop.show();
37840         if(!this.bottomOffset){
37841             this.bottomOffset = this.view.mainHd.getHeight();
37842         }
37843         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37844         this.proxyBottom.show();
37845         return pt;
37846     },
37847
37848     onNodeEnter : function(n, dd, e, data){
37849         if(data.header != n){
37850             this.positionIndicator(data.header, n, e);
37851         }
37852     },
37853
37854     onNodeOver : function(n, dd, e, data){
37855         var result = false;
37856         if(data.header != n){
37857             result = this.positionIndicator(data.header, n, e);
37858         }
37859         if(!result){
37860             this.proxyTop.hide();
37861             this.proxyBottom.hide();
37862         }
37863         return result ? this.dropAllowed : this.dropNotAllowed;
37864     },
37865
37866     onNodeOut : function(n, dd, e, data){
37867         this.proxyTop.hide();
37868         this.proxyBottom.hide();
37869     },
37870
37871     onNodeDrop : function(n, dd, e, data){
37872         var h = data.header;
37873         if(h != n){
37874             var cm = this.grid.colModel;
37875             var x = Roo.lib.Event.getPageX(e);
37876             var r = Roo.lib.Dom.getRegion(n.firstChild);
37877             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37878             var oldIndex = this.view.getCellIndex(h);
37879             var newIndex = this.view.getCellIndex(n);
37880             var locked = cm.isLocked(newIndex);
37881             if(pt == "after"){
37882                 newIndex++;
37883             }
37884             if(oldIndex < newIndex){
37885                 newIndex--;
37886             }
37887             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37888                 return false;
37889             }
37890             cm.setLocked(oldIndex, locked, true);
37891             cm.moveColumn(oldIndex, newIndex);
37892             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37893             return true;
37894         }
37895         return false;
37896     }
37897 });
37898 /*
37899  * Based on:
37900  * Ext JS Library 1.1.1
37901  * Copyright(c) 2006-2007, Ext JS, LLC.
37902  *
37903  * Originally Released Under LGPL - original licence link has changed is not relivant.
37904  *
37905  * Fork - LGPL
37906  * <script type="text/javascript">
37907  */
37908   
37909 /**
37910  * @class Roo.grid.GridView
37911  * @extends Roo.util.Observable
37912  *
37913  * @constructor
37914  * @param {Object} config
37915  */
37916 Roo.grid.GridView = function(config){
37917     Roo.grid.GridView.superclass.constructor.call(this);
37918     this.el = null;
37919
37920     Roo.apply(this, config);
37921 };
37922
37923 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37924
37925     unselectable :  'unselectable="on"',
37926     unselectableCls :  'x-unselectable',
37927     
37928     
37929     rowClass : "x-grid-row",
37930
37931     cellClass : "x-grid-col",
37932
37933     tdClass : "x-grid-td",
37934
37935     hdClass : "x-grid-hd",
37936
37937     splitClass : "x-grid-split",
37938
37939     sortClasses : ["sort-asc", "sort-desc"],
37940
37941     enableMoveAnim : false,
37942
37943     hlColor: "C3DAF9",
37944
37945     dh : Roo.DomHelper,
37946
37947     fly : Roo.Element.fly,
37948
37949     css : Roo.util.CSS,
37950
37951     borderWidth: 1,
37952
37953     splitOffset: 3,
37954
37955     scrollIncrement : 22,
37956
37957     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37958
37959     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37960
37961     bind : function(ds, cm){
37962         if(this.ds){
37963             this.ds.un("load", this.onLoad, this);
37964             this.ds.un("datachanged", this.onDataChange, this);
37965             this.ds.un("add", this.onAdd, this);
37966             this.ds.un("remove", this.onRemove, this);
37967             this.ds.un("update", this.onUpdate, this);
37968             this.ds.un("clear", this.onClear, this);
37969         }
37970         if(ds){
37971             ds.on("load", this.onLoad, this);
37972             ds.on("datachanged", this.onDataChange, this);
37973             ds.on("add", this.onAdd, this);
37974             ds.on("remove", this.onRemove, this);
37975             ds.on("update", this.onUpdate, this);
37976             ds.on("clear", this.onClear, this);
37977         }
37978         this.ds = ds;
37979
37980         if(this.cm){
37981             this.cm.un("widthchange", this.onColWidthChange, this);
37982             this.cm.un("headerchange", this.onHeaderChange, this);
37983             this.cm.un("hiddenchange", this.onHiddenChange, this);
37984             this.cm.un("columnmoved", this.onColumnMove, this);
37985             this.cm.un("columnlockchange", this.onColumnLock, this);
37986         }
37987         if(cm){
37988             this.generateRules(cm);
37989             cm.on("widthchange", this.onColWidthChange, this);
37990             cm.on("headerchange", this.onHeaderChange, this);
37991             cm.on("hiddenchange", this.onHiddenChange, this);
37992             cm.on("columnmoved", this.onColumnMove, this);
37993             cm.on("columnlockchange", this.onColumnLock, this);
37994         }
37995         this.cm = cm;
37996     },
37997
37998     init: function(grid){
37999         Roo.grid.GridView.superclass.init.call(this, grid);
38000
38001         this.bind(grid.dataSource, grid.colModel);
38002
38003         grid.on("headerclick", this.handleHeaderClick, this);
38004
38005         if(grid.trackMouseOver){
38006             grid.on("mouseover", this.onRowOver, this);
38007             grid.on("mouseout", this.onRowOut, this);
38008         }
38009         grid.cancelTextSelection = function(){};
38010         this.gridId = grid.id;
38011
38012         var tpls = this.templates || {};
38013
38014         if(!tpls.master){
38015             tpls.master = new Roo.Template(
38016                '<div class="x-grid" hidefocus="true">',
38017                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
38018                   '<div class="x-grid-topbar"></div>',
38019                   '<div class="x-grid-scroller"><div></div></div>',
38020                   '<div class="x-grid-locked">',
38021                       '<div class="x-grid-header">{lockedHeader}</div>',
38022                       '<div class="x-grid-body">{lockedBody}</div>',
38023                   "</div>",
38024                   '<div class="x-grid-viewport">',
38025                       '<div class="x-grid-header">{header}</div>',
38026                       '<div class="x-grid-body">{body}</div>',
38027                   "</div>",
38028                   '<div class="x-grid-bottombar"></div>',
38029                  
38030                   '<div class="x-grid-resize-proxy">&#160;</div>',
38031                "</div>"
38032             );
38033             tpls.master.disableformats = true;
38034         }
38035
38036         if(!tpls.header){
38037             tpls.header = new Roo.Template(
38038                '<table border="0" cellspacing="0" cellpadding="0">',
38039                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
38040                "</table>{splits}"
38041             );
38042             tpls.header.disableformats = true;
38043         }
38044         tpls.header.compile();
38045
38046         if(!tpls.hcell){
38047             tpls.hcell = new Roo.Template(
38048                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
38049                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
38050                 "</div></td>"
38051              );
38052              tpls.hcell.disableFormats = true;
38053         }
38054         tpls.hcell.compile();
38055
38056         if(!tpls.hsplit){
38057             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
38058                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
38059             tpls.hsplit.disableFormats = true;
38060         }
38061         tpls.hsplit.compile();
38062
38063         if(!tpls.body){
38064             tpls.body = new Roo.Template(
38065                '<table border="0" cellspacing="0" cellpadding="0">',
38066                "<tbody>{rows}</tbody>",
38067                "</table>"
38068             );
38069             tpls.body.disableFormats = true;
38070         }
38071         tpls.body.compile();
38072
38073         if(!tpls.row){
38074             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
38075             tpls.row.disableFormats = true;
38076         }
38077         tpls.row.compile();
38078
38079         if(!tpls.cell){
38080             tpls.cell = new Roo.Template(
38081                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
38082                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
38083                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
38084                 "</td>"
38085             );
38086             tpls.cell.disableFormats = true;
38087         }
38088         tpls.cell.compile();
38089
38090         this.templates = tpls;
38091     },
38092
38093     // remap these for backwards compat
38094     onColWidthChange : function(){
38095         this.updateColumns.apply(this, arguments);
38096     },
38097     onHeaderChange : function(){
38098         this.updateHeaders.apply(this, arguments);
38099     }, 
38100     onHiddenChange : function(){
38101         this.handleHiddenChange.apply(this, arguments);
38102     },
38103     onColumnMove : function(){
38104         this.handleColumnMove.apply(this, arguments);
38105     },
38106     onColumnLock : function(){
38107         this.handleLockChange.apply(this, arguments);
38108     },
38109
38110     onDataChange : function(){
38111         this.refresh();
38112         this.updateHeaderSortState();
38113     },
38114
38115     onClear : function(){
38116         this.refresh();
38117     },
38118
38119     onUpdate : function(ds, record){
38120         this.refreshRow(record);
38121     },
38122
38123     refreshRow : function(record){
38124         var ds = this.ds, index;
38125         if(typeof record == 'number'){
38126             index = record;
38127             record = ds.getAt(index);
38128         }else{
38129             index = ds.indexOf(record);
38130         }
38131         this.insertRows(ds, index, index, true);
38132         this.onRemove(ds, record, index+1, true);
38133         this.syncRowHeights(index, index);
38134         this.layout();
38135         this.fireEvent("rowupdated", this, index, record);
38136     },
38137
38138     onAdd : function(ds, records, index){
38139         this.insertRows(ds, index, index + (records.length-1));
38140     },
38141
38142     onRemove : function(ds, record, index, isUpdate){
38143         if(isUpdate !== true){
38144             this.fireEvent("beforerowremoved", this, index, record);
38145         }
38146         var bt = this.getBodyTable(), lt = this.getLockedTable();
38147         if(bt.rows[index]){
38148             bt.firstChild.removeChild(bt.rows[index]);
38149         }
38150         if(lt.rows[index]){
38151             lt.firstChild.removeChild(lt.rows[index]);
38152         }
38153         if(isUpdate !== true){
38154             this.stripeRows(index);
38155             this.syncRowHeights(index, index);
38156             this.layout();
38157             this.fireEvent("rowremoved", this, index, record);
38158         }
38159     },
38160
38161     onLoad : function(){
38162         this.scrollToTop();
38163     },
38164
38165     /**
38166      * Scrolls the grid to the top
38167      */
38168     scrollToTop : function(){
38169         if(this.scroller){
38170             this.scroller.dom.scrollTop = 0;
38171             this.syncScroll();
38172         }
38173     },
38174
38175     /**
38176      * Gets a panel in the header of the grid that can be used for toolbars etc.
38177      * After modifying the contents of this panel a call to grid.autoSize() may be
38178      * required to register any changes in size.
38179      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
38180      * @return Roo.Element
38181      */
38182     getHeaderPanel : function(doShow){
38183         if(doShow){
38184             this.headerPanel.show();
38185         }
38186         return this.headerPanel;
38187     },
38188
38189     /**
38190      * Gets a panel in the footer of the grid that can be used for toolbars etc.
38191      * After modifying the contents of this panel a call to grid.autoSize() may be
38192      * required to register any changes in size.
38193      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
38194      * @return Roo.Element
38195      */
38196     getFooterPanel : function(doShow){
38197         if(doShow){
38198             this.footerPanel.show();
38199         }
38200         return this.footerPanel;
38201     },
38202
38203     initElements : function(){
38204         var E = Roo.Element;
38205         var el = this.grid.getGridEl().dom.firstChild;
38206         var cs = el.childNodes;
38207
38208         this.el = new E(el);
38209         
38210          this.focusEl = new E(el.firstChild);
38211         this.focusEl.swallowEvent("click", true);
38212         
38213         this.headerPanel = new E(cs[1]);
38214         this.headerPanel.enableDisplayMode("block");
38215
38216         this.scroller = new E(cs[2]);
38217         this.scrollSizer = new E(this.scroller.dom.firstChild);
38218
38219         this.lockedWrap = new E(cs[3]);
38220         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38221         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38222
38223         this.mainWrap = new E(cs[4]);
38224         this.mainHd = new E(this.mainWrap.dom.firstChild);
38225         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38226
38227         this.footerPanel = new E(cs[5]);
38228         this.footerPanel.enableDisplayMode("block");
38229
38230         this.resizeProxy = new E(cs[6]);
38231
38232         this.headerSelector = String.format(
38233            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38234            this.lockedHd.id, this.mainHd.id
38235         );
38236
38237         this.splitterSelector = String.format(
38238            '#{0} div.x-grid-split, #{1} div.x-grid-split',
38239            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38240         );
38241     },
38242     idToCssName : function(s)
38243     {
38244         return s.replace(/[^a-z0-9]+/ig, '-');
38245     },
38246
38247     getHeaderCell : function(index){
38248         return Roo.DomQuery.select(this.headerSelector)[index];
38249     },
38250
38251     getHeaderCellMeasure : function(index){
38252         return this.getHeaderCell(index).firstChild;
38253     },
38254
38255     getHeaderCellText : function(index){
38256         return this.getHeaderCell(index).firstChild.firstChild;
38257     },
38258
38259     getLockedTable : function(){
38260         return this.lockedBody.dom.firstChild;
38261     },
38262
38263     getBodyTable : function(){
38264         return this.mainBody.dom.firstChild;
38265     },
38266
38267     getLockedRow : function(index){
38268         return this.getLockedTable().rows[index];
38269     },
38270
38271     getRow : function(index){
38272         return this.getBodyTable().rows[index];
38273     },
38274
38275     getRowComposite : function(index){
38276         if(!this.rowEl){
38277             this.rowEl = new Roo.CompositeElementLite();
38278         }
38279         var els = [], lrow, mrow;
38280         if(lrow = this.getLockedRow(index)){
38281             els.push(lrow);
38282         }
38283         if(mrow = this.getRow(index)){
38284             els.push(mrow);
38285         }
38286         this.rowEl.elements = els;
38287         return this.rowEl;
38288     },
38289     /**
38290      * Gets the 'td' of the cell
38291      * 
38292      * @param {Integer} rowIndex row to select
38293      * @param {Integer} colIndex column to select
38294      * 
38295      * @return {Object} 
38296      */
38297     getCell : function(rowIndex, colIndex){
38298         var locked = this.cm.getLockedCount();
38299         var source;
38300         if(colIndex < locked){
38301             source = this.lockedBody.dom.firstChild;
38302         }else{
38303             source = this.mainBody.dom.firstChild;
38304             colIndex -= locked;
38305         }
38306         return source.rows[rowIndex].childNodes[colIndex];
38307     },
38308
38309     getCellText : function(rowIndex, colIndex){
38310         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38311     },
38312
38313     getCellBox : function(cell){
38314         var b = this.fly(cell).getBox();
38315         if(Roo.isOpera){ // opera fails to report the Y
38316             b.y = cell.offsetTop + this.mainBody.getY();
38317         }
38318         return b;
38319     },
38320
38321     getCellIndex : function(cell){
38322         var id = String(cell.className).match(this.cellRE);
38323         if(id){
38324             return parseInt(id[1], 10);
38325         }
38326         return 0;
38327     },
38328
38329     findHeaderIndex : function(n){
38330         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38331         return r ? this.getCellIndex(r) : false;
38332     },
38333
38334     findHeaderCell : function(n){
38335         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38336         return r ? r : false;
38337     },
38338
38339     findRowIndex : function(n){
38340         if(!n){
38341             return false;
38342         }
38343         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38344         return r ? r.rowIndex : false;
38345     },
38346
38347     findCellIndex : function(node){
38348         var stop = this.el.dom;
38349         while(node && node != stop){
38350             if(this.findRE.test(node.className)){
38351                 return this.getCellIndex(node);
38352             }
38353             node = node.parentNode;
38354         }
38355         return false;
38356     },
38357
38358     getColumnId : function(index){
38359         return this.cm.getColumnId(index);
38360     },
38361
38362     getSplitters : function()
38363     {
38364         if(this.splitterSelector){
38365            return Roo.DomQuery.select(this.splitterSelector);
38366         }else{
38367             return null;
38368       }
38369     },
38370
38371     getSplitter : function(index){
38372         return this.getSplitters()[index];
38373     },
38374
38375     onRowOver : function(e, t){
38376         var row;
38377         if((row = this.findRowIndex(t)) !== false){
38378             this.getRowComposite(row).addClass("x-grid-row-over");
38379         }
38380     },
38381
38382     onRowOut : function(e, t){
38383         var row;
38384         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38385             this.getRowComposite(row).removeClass("x-grid-row-over");
38386         }
38387     },
38388
38389     renderHeaders : function(){
38390         var cm = this.cm;
38391         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38392         var cb = [], lb = [], sb = [], lsb = [], p = {};
38393         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38394             p.cellId = "x-grid-hd-0-" + i;
38395             p.splitId = "x-grid-csplit-0-" + i;
38396             p.id = cm.getColumnId(i);
38397             p.value = cm.getColumnHeader(i) || "";
38398             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
38399             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38400             if(!cm.isLocked(i)){
38401                 cb[cb.length] = ct.apply(p);
38402                 sb[sb.length] = st.apply(p);
38403             }else{
38404                 lb[lb.length] = ct.apply(p);
38405                 lsb[lsb.length] = st.apply(p);
38406             }
38407         }
38408         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38409                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38410     },
38411
38412     updateHeaders : function(){
38413         var html = this.renderHeaders();
38414         this.lockedHd.update(html[0]);
38415         this.mainHd.update(html[1]);
38416     },
38417
38418     /**
38419      * Focuses the specified row.
38420      * @param {Number} row The row index
38421      */
38422     focusRow : function(row)
38423     {
38424         //Roo.log('GridView.focusRow');
38425         var x = this.scroller.dom.scrollLeft;
38426         this.focusCell(row, 0, false);
38427         this.scroller.dom.scrollLeft = x;
38428     },
38429
38430     /**
38431      * Focuses the specified cell.
38432      * @param {Number} row The row index
38433      * @param {Number} col The column index
38434      * @param {Boolean} hscroll false to disable horizontal scrolling
38435      */
38436     focusCell : function(row, col, hscroll)
38437     {
38438         //Roo.log('GridView.focusCell');
38439         var el = this.ensureVisible(row, col, hscroll);
38440         this.focusEl.alignTo(el, "tl-tl");
38441         if(Roo.isGecko){
38442             this.focusEl.focus();
38443         }else{
38444             this.focusEl.focus.defer(1, this.focusEl);
38445         }
38446     },
38447
38448     /**
38449      * Scrolls the specified cell into view
38450      * @param {Number} row The row index
38451      * @param {Number} col The column index
38452      * @param {Boolean} hscroll false to disable horizontal scrolling
38453      */
38454     ensureVisible : function(row, col, hscroll)
38455     {
38456         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38457         //return null; //disable for testing.
38458         if(typeof row != "number"){
38459             row = row.rowIndex;
38460         }
38461         if(row < 0 && row >= this.ds.getCount()){
38462             return  null;
38463         }
38464         col = (col !== undefined ? col : 0);
38465         var cm = this.grid.colModel;
38466         while(cm.isHidden(col)){
38467             col++;
38468         }
38469
38470         var el = this.getCell(row, col);
38471         if(!el){
38472             return null;
38473         }
38474         var c = this.scroller.dom;
38475
38476         var ctop = parseInt(el.offsetTop, 10);
38477         var cleft = parseInt(el.offsetLeft, 10);
38478         var cbot = ctop + el.offsetHeight;
38479         var cright = cleft + el.offsetWidth;
38480         
38481         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38482         var stop = parseInt(c.scrollTop, 10);
38483         var sleft = parseInt(c.scrollLeft, 10);
38484         var sbot = stop + ch;
38485         var sright = sleft + c.clientWidth;
38486         /*
38487         Roo.log('GridView.ensureVisible:' +
38488                 ' ctop:' + ctop +
38489                 ' c.clientHeight:' + c.clientHeight +
38490                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38491                 ' stop:' + stop +
38492                 ' cbot:' + cbot +
38493                 ' sbot:' + sbot +
38494                 ' ch:' + ch  
38495                 );
38496         */
38497         if(ctop < stop){
38498             c.scrollTop = ctop;
38499             //Roo.log("set scrolltop to ctop DISABLE?");
38500         }else if(cbot > sbot){
38501             //Roo.log("set scrolltop to cbot-ch");
38502             c.scrollTop = cbot-ch;
38503         }
38504         
38505         if(hscroll !== false){
38506             if(cleft < sleft){
38507                 c.scrollLeft = cleft;
38508             }else if(cright > sright){
38509                 c.scrollLeft = cright-c.clientWidth;
38510             }
38511         }
38512          
38513         return el;
38514     },
38515
38516     updateColumns : function(){
38517         this.grid.stopEditing();
38518         var cm = this.grid.colModel, colIds = this.getColumnIds();
38519         //var totalWidth = cm.getTotalWidth();
38520         var pos = 0;
38521         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38522             //if(cm.isHidden(i)) continue;
38523             var w = cm.getColumnWidth(i);
38524             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38525             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38526         }
38527         this.updateSplitters();
38528     },
38529
38530     generateRules : function(cm){
38531         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38532         Roo.util.CSS.removeStyleSheet(rulesId);
38533         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38534             var cid = cm.getColumnId(i);
38535             var align = '';
38536             if(cm.config[i].align){
38537                 align = 'text-align:'+cm.config[i].align+';';
38538             }
38539             var hidden = '';
38540             if(cm.isHidden(i)){
38541                 hidden = 'display:none;';
38542             }
38543             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38544             ruleBuf.push(
38545                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38546                     this.hdSelector, cid, " {\n", align, width, "}\n",
38547                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
38548                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
38549         }
38550         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38551     },
38552
38553     updateSplitters : function(){
38554         var cm = this.cm, s = this.getSplitters();
38555         if(s){ // splitters not created yet
38556             var pos = 0, locked = true;
38557             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38558                 if(cm.isHidden(i)) {
38559                     continue;
38560                 }
38561                 var w = cm.getColumnWidth(i); // make sure it's a number
38562                 if(!cm.isLocked(i) && locked){
38563                     pos = 0;
38564                     locked = false;
38565                 }
38566                 pos += w;
38567                 s[i].style.left = (pos-this.splitOffset) + "px";
38568             }
38569         }
38570     },
38571
38572     handleHiddenChange : function(colModel, colIndex, hidden){
38573         if(hidden){
38574             this.hideColumn(colIndex);
38575         }else{
38576             this.unhideColumn(colIndex);
38577         }
38578     },
38579
38580     hideColumn : function(colIndex){
38581         var cid = this.getColumnId(colIndex);
38582         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38583         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38584         if(Roo.isSafari){
38585             this.updateHeaders();
38586         }
38587         this.updateSplitters();
38588         this.layout();
38589     },
38590
38591     unhideColumn : function(colIndex){
38592         var cid = this.getColumnId(colIndex);
38593         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38594         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38595
38596         if(Roo.isSafari){
38597             this.updateHeaders();
38598         }
38599         this.updateSplitters();
38600         this.layout();
38601     },
38602
38603     insertRows : function(dm, firstRow, lastRow, isUpdate){
38604         if(firstRow == 0 && lastRow == dm.getCount()-1){
38605             this.refresh();
38606         }else{
38607             if(!isUpdate){
38608                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38609             }
38610             var s = this.getScrollState();
38611             var markup = this.renderRows(firstRow, lastRow);
38612             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38613             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38614             this.restoreScroll(s);
38615             if(!isUpdate){
38616                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38617                 this.syncRowHeights(firstRow, lastRow);
38618                 this.stripeRows(firstRow);
38619                 this.layout();
38620             }
38621         }
38622     },
38623
38624     bufferRows : function(markup, target, index){
38625         var before = null, trows = target.rows, tbody = target.tBodies[0];
38626         if(index < trows.length){
38627             before = trows[index];
38628         }
38629         var b = document.createElement("div");
38630         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38631         var rows = b.firstChild.rows;
38632         for(var i = 0, len = rows.length; i < len; i++){
38633             if(before){
38634                 tbody.insertBefore(rows[0], before);
38635             }else{
38636                 tbody.appendChild(rows[0]);
38637             }
38638         }
38639         b.innerHTML = "";
38640         b = null;
38641     },
38642
38643     deleteRows : function(dm, firstRow, lastRow){
38644         if(dm.getRowCount()<1){
38645             this.fireEvent("beforerefresh", this);
38646             this.mainBody.update("");
38647             this.lockedBody.update("");
38648             this.fireEvent("refresh", this);
38649         }else{
38650             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38651             var bt = this.getBodyTable();
38652             var tbody = bt.firstChild;
38653             var rows = bt.rows;
38654             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38655                 tbody.removeChild(rows[firstRow]);
38656             }
38657             this.stripeRows(firstRow);
38658             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38659         }
38660     },
38661
38662     updateRows : function(dataSource, firstRow, lastRow){
38663         var s = this.getScrollState();
38664         this.refresh();
38665         this.restoreScroll(s);
38666     },
38667
38668     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38669         if(!noRefresh){
38670            this.refresh();
38671         }
38672         this.updateHeaderSortState();
38673     },
38674
38675     getScrollState : function(){
38676         
38677         var sb = this.scroller.dom;
38678         return {left: sb.scrollLeft, top: sb.scrollTop};
38679     },
38680
38681     stripeRows : function(startRow){
38682         if(!this.grid.stripeRows || this.ds.getCount() < 1){
38683             return;
38684         }
38685         startRow = startRow || 0;
38686         var rows = this.getBodyTable().rows;
38687         var lrows = this.getLockedTable().rows;
38688         var cls = ' x-grid-row-alt ';
38689         for(var i = startRow, len = rows.length; i < len; i++){
38690             var row = rows[i], lrow = lrows[i];
38691             var isAlt = ((i+1) % 2 == 0);
38692             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38693             if(isAlt == hasAlt){
38694                 continue;
38695             }
38696             if(isAlt){
38697                 row.className += " x-grid-row-alt";
38698             }else{
38699                 row.className = row.className.replace("x-grid-row-alt", "");
38700             }
38701             if(lrow){
38702                 lrow.className = row.className;
38703             }
38704         }
38705     },
38706
38707     restoreScroll : function(state){
38708         //Roo.log('GridView.restoreScroll');
38709         var sb = this.scroller.dom;
38710         sb.scrollLeft = state.left;
38711         sb.scrollTop = state.top;
38712         this.syncScroll();
38713     },
38714
38715     syncScroll : function(){
38716         //Roo.log('GridView.syncScroll');
38717         var sb = this.scroller.dom;
38718         var sh = this.mainHd.dom;
38719         var bs = this.mainBody.dom;
38720         var lv = this.lockedBody.dom;
38721         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38722         lv.scrollTop = bs.scrollTop = sb.scrollTop;
38723     },
38724
38725     handleScroll : function(e){
38726         this.syncScroll();
38727         var sb = this.scroller.dom;
38728         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38729         e.stopEvent();
38730     },
38731
38732     handleWheel : function(e){
38733         var d = e.getWheelDelta();
38734         this.scroller.dom.scrollTop -= d*22;
38735         // set this here to prevent jumpy scrolling on large tables
38736         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38737         e.stopEvent();
38738     },
38739
38740     renderRows : function(startRow, endRow){
38741         // pull in all the crap needed to render rows
38742         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38743         var colCount = cm.getColumnCount();
38744
38745         if(ds.getCount() < 1){
38746             return ["", ""];
38747         }
38748
38749         // build a map for all the columns
38750         var cs = [];
38751         for(var i = 0; i < colCount; i++){
38752             var name = cm.getDataIndex(i);
38753             cs[i] = {
38754                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38755                 renderer : cm.getRenderer(i),
38756                 id : cm.getColumnId(i),
38757                 locked : cm.isLocked(i),
38758                 has_editor : cm.isCellEditable(i)
38759             };
38760         }
38761
38762         startRow = startRow || 0;
38763         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38764
38765         // records to render
38766         var rs = ds.getRange(startRow, endRow);
38767
38768         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38769     },
38770
38771     // As much as I hate to duplicate code, this was branched because FireFox really hates
38772     // [].join("") on strings. The performance difference was substantial enough to
38773     // branch this function
38774     doRender : Roo.isGecko ?
38775             function(cs, rs, ds, startRow, colCount, stripe){
38776                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38777                 // buffers
38778                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38779                 
38780                 var hasListener = this.grid.hasListener('rowclass');
38781                 var rowcfg = {};
38782                 for(var j = 0, len = rs.length; j < len; j++){
38783                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38784                     for(var i = 0; i < colCount; i++){
38785                         c = cs[i];
38786                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38787                         p.id = c.id;
38788                         p.css = p.attr = "";
38789                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38790                         if(p.value == undefined || p.value === "") {
38791                             p.value = "&#160;";
38792                         }
38793                         if(c.has_editor){
38794                             p.css += ' x-grid-editable-cell';
38795                         }
38796                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38797                             p.css +=  ' x-grid-dirty-cell';
38798                         }
38799                         var markup = ct.apply(p);
38800                         if(!c.locked){
38801                             cb+= markup;
38802                         }else{
38803                             lcb+= markup;
38804                         }
38805                     }
38806                     var alt = [];
38807                     if(stripe && ((rowIndex+1) % 2 == 0)){
38808                         alt.push("x-grid-row-alt")
38809                     }
38810                     if(r.dirty){
38811                         alt.push(  " x-grid-dirty-row");
38812                     }
38813                     rp.cells = lcb;
38814                     if(this.getRowClass){
38815                         alt.push(this.getRowClass(r, rowIndex));
38816                     }
38817                     if (hasListener) {
38818                         rowcfg = {
38819                              
38820                             record: r,
38821                             rowIndex : rowIndex,
38822                             rowClass : ''
38823                         };
38824                         this.grid.fireEvent('rowclass', this, rowcfg);
38825                         alt.push(rowcfg.rowClass);
38826                     }
38827                     rp.alt = alt.join(" ");
38828                     lbuf+= rt.apply(rp);
38829                     rp.cells = cb;
38830                     buf+=  rt.apply(rp);
38831                 }
38832                 return [lbuf, buf];
38833             } :
38834             function(cs, rs, ds, startRow, colCount, stripe){
38835                 var ts = this.templates, ct = ts.cell, rt = ts.row;
38836                 // buffers
38837                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38838                 var hasListener = this.grid.hasListener('rowclass');
38839  
38840                 var rowcfg = {};
38841                 for(var j = 0, len = rs.length; j < len; j++){
38842                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38843                     for(var i = 0; i < colCount; i++){
38844                         c = cs[i];
38845                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38846                         p.id = c.id;
38847                         p.css = p.attr = "";
38848                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38849                         if(p.value == undefined || p.value === "") {
38850                             p.value = "&#160;";
38851                         }
38852                         //Roo.log(c);
38853                          if(c.has_editor){
38854                             p.css += ' x-grid-editable-cell';
38855                         }
38856                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38857                             p.css += ' x-grid-dirty-cell' 
38858                         }
38859                         
38860                         var markup = ct.apply(p);
38861                         if(!c.locked){
38862                             cb[cb.length] = markup;
38863                         }else{
38864                             lcb[lcb.length] = markup;
38865                         }
38866                     }
38867                     var alt = [];
38868                     if(stripe && ((rowIndex+1) % 2 == 0)){
38869                         alt.push( "x-grid-row-alt");
38870                     }
38871                     if(r.dirty){
38872                         alt.push(" x-grid-dirty-row");
38873                     }
38874                     rp.cells = lcb;
38875                     if(this.getRowClass){
38876                         alt.push( this.getRowClass(r, rowIndex));
38877                     }
38878                     if (hasListener) {
38879                         rowcfg = {
38880                              
38881                             record: r,
38882                             rowIndex : rowIndex,
38883                             rowClass : ''
38884                         };
38885                         this.grid.fireEvent('rowclass', this, rowcfg);
38886                         alt.push(rowcfg.rowClass);
38887                     }
38888                     
38889                     rp.alt = alt.join(" ");
38890                     rp.cells = lcb.join("");
38891                     lbuf[lbuf.length] = rt.apply(rp);
38892                     rp.cells = cb.join("");
38893                     buf[buf.length] =  rt.apply(rp);
38894                 }
38895                 return [lbuf.join(""), buf.join("")];
38896             },
38897
38898     renderBody : function(){
38899         var markup = this.renderRows();
38900         var bt = this.templates.body;
38901         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38902     },
38903
38904     /**
38905      * Refreshes the grid
38906      * @param {Boolean} headersToo
38907      */
38908     refresh : function(headersToo){
38909         this.fireEvent("beforerefresh", this);
38910         this.grid.stopEditing();
38911         var result = this.renderBody();
38912         this.lockedBody.update(result[0]);
38913         this.mainBody.update(result[1]);
38914         if(headersToo === true){
38915             this.updateHeaders();
38916             this.updateColumns();
38917             this.updateSplitters();
38918             this.updateHeaderSortState();
38919         }
38920         this.syncRowHeights();
38921         this.layout();
38922         this.fireEvent("refresh", this);
38923     },
38924
38925     handleColumnMove : function(cm, oldIndex, newIndex){
38926         this.indexMap = null;
38927         var s = this.getScrollState();
38928         this.refresh(true);
38929         this.restoreScroll(s);
38930         this.afterMove(newIndex);
38931     },
38932
38933     afterMove : function(colIndex){
38934         if(this.enableMoveAnim && Roo.enableFx){
38935             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38936         }
38937         // if multisort - fix sortOrder, and reload..
38938         if (this.grid.dataSource.multiSort) {
38939             // the we can call sort again..
38940             var dm = this.grid.dataSource;
38941             var cm = this.grid.colModel;
38942             var so = [];
38943             for(var i = 0; i < cm.config.length; i++ ) {
38944                 
38945                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38946                     continue; // dont' bother, it's not in sort list or being set.
38947                 }
38948                 
38949                 so.push(cm.config[i].dataIndex);
38950             };
38951             dm.sortOrder = so;
38952             dm.load(dm.lastOptions);
38953             
38954             
38955         }
38956         
38957     },
38958
38959     updateCell : function(dm, rowIndex, dataIndex){
38960         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38961         if(typeof colIndex == "undefined"){ // not present in grid
38962             return;
38963         }
38964         var cm = this.grid.colModel;
38965         var cell = this.getCell(rowIndex, colIndex);
38966         var cellText = this.getCellText(rowIndex, colIndex);
38967
38968         var p = {
38969             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38970             id : cm.getColumnId(colIndex),
38971             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38972         };
38973         var renderer = cm.getRenderer(colIndex);
38974         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38975         if(typeof val == "undefined" || val === "") {
38976             val = "&#160;";
38977         }
38978         cellText.innerHTML = val;
38979         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38980         this.syncRowHeights(rowIndex, rowIndex);
38981     },
38982
38983     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38984         var maxWidth = 0;
38985         if(this.grid.autoSizeHeaders){
38986             var h = this.getHeaderCellMeasure(colIndex);
38987             maxWidth = Math.max(maxWidth, h.scrollWidth);
38988         }
38989         var tb, index;
38990         if(this.cm.isLocked(colIndex)){
38991             tb = this.getLockedTable();
38992             index = colIndex;
38993         }else{
38994             tb = this.getBodyTable();
38995             index = colIndex - this.cm.getLockedCount();
38996         }
38997         if(tb && tb.rows){
38998             var rows = tb.rows;
38999             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
39000             for(var i = 0; i < stopIndex; i++){
39001                 var cell = rows[i].childNodes[index].firstChild;
39002                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
39003             }
39004         }
39005         return maxWidth + /*margin for error in IE*/ 5;
39006     },
39007     /**
39008      * Autofit a column to its content.
39009      * @param {Number} colIndex
39010      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
39011      */
39012      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
39013          if(this.cm.isHidden(colIndex)){
39014              return; // can't calc a hidden column
39015          }
39016         if(forceMinSize){
39017             var cid = this.cm.getColumnId(colIndex);
39018             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
39019            if(this.grid.autoSizeHeaders){
39020                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
39021            }
39022         }
39023         var newWidth = this.calcColumnWidth(colIndex);
39024         this.cm.setColumnWidth(colIndex,
39025             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
39026         if(!suppressEvent){
39027             this.grid.fireEvent("columnresize", colIndex, newWidth);
39028         }
39029     },
39030
39031     /**
39032      * Autofits all columns to their content and then expands to fit any extra space in the grid
39033      */
39034      autoSizeColumns : function(){
39035         var cm = this.grid.colModel;
39036         var colCount = cm.getColumnCount();
39037         for(var i = 0; i < colCount; i++){
39038             this.autoSizeColumn(i, true, true);
39039         }
39040         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
39041             this.fitColumns();
39042         }else{
39043             this.updateColumns();
39044             this.layout();
39045         }
39046     },
39047
39048     /**
39049      * Autofits all columns to the grid's width proportionate with their current size
39050      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
39051      */
39052     fitColumns : function(reserveScrollSpace){
39053         var cm = this.grid.colModel;
39054         var colCount = cm.getColumnCount();
39055         var cols = [];
39056         var width = 0;
39057         var i, w;
39058         for (i = 0; i < colCount; i++){
39059             if(!cm.isHidden(i) && !cm.isFixed(i)){
39060                 w = cm.getColumnWidth(i);
39061                 cols.push(i);
39062                 cols.push(w);
39063                 width += w;
39064             }
39065         }
39066         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
39067         if(reserveScrollSpace){
39068             avail -= 17;
39069         }
39070         var frac = (avail - cm.getTotalWidth())/width;
39071         while (cols.length){
39072             w = cols.pop();
39073             i = cols.pop();
39074             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
39075         }
39076         this.updateColumns();
39077         this.layout();
39078     },
39079
39080     onRowSelect : function(rowIndex){
39081         var row = this.getRowComposite(rowIndex);
39082         row.addClass("x-grid-row-selected");
39083     },
39084
39085     onRowDeselect : function(rowIndex){
39086         var row = this.getRowComposite(rowIndex);
39087         row.removeClass("x-grid-row-selected");
39088     },
39089
39090     onCellSelect : function(row, col){
39091         var cell = this.getCell(row, col);
39092         if(cell){
39093             Roo.fly(cell).addClass("x-grid-cell-selected");
39094         }
39095     },
39096
39097     onCellDeselect : function(row, col){
39098         var cell = this.getCell(row, col);
39099         if(cell){
39100             Roo.fly(cell).removeClass("x-grid-cell-selected");
39101         }
39102     },
39103
39104     updateHeaderSortState : function(){
39105         
39106         // sort state can be single { field: xxx, direction : yyy}
39107         // or   { xxx=>ASC , yyy : DESC ..... }
39108         
39109         var mstate = {};
39110         if (!this.ds.multiSort) { 
39111             var state = this.ds.getSortState();
39112             if(!state){
39113                 return;
39114             }
39115             mstate[state.field] = state.direction;
39116             // FIXME... - this is not used here.. but might be elsewhere..
39117             this.sortState = state;
39118             
39119         } else {
39120             mstate = this.ds.sortToggle;
39121         }
39122         //remove existing sort classes..
39123         
39124         var sc = this.sortClasses;
39125         var hds = this.el.select(this.headerSelector).removeClass(sc);
39126         
39127         for(var f in mstate) {
39128         
39129             var sortColumn = this.cm.findColumnIndex(f);
39130             
39131             if(sortColumn != -1){
39132                 var sortDir = mstate[f];        
39133                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
39134             }
39135         }
39136         
39137          
39138         
39139     },
39140
39141
39142     handleHeaderClick : function(g, index,e){
39143         
39144         Roo.log("header click");
39145         
39146         if (Roo.isTouch) {
39147             // touch events on header are handled by context
39148             this.handleHdCtx(g,index,e);
39149             return;
39150         }
39151         
39152         
39153         if(this.headersDisabled){
39154             return;
39155         }
39156         var dm = g.dataSource, cm = g.colModel;
39157         if(!cm.isSortable(index)){
39158             return;
39159         }
39160         g.stopEditing();
39161         
39162         if (dm.multiSort) {
39163             // update the sortOrder
39164             var so = [];
39165             for(var i = 0; i < cm.config.length; i++ ) {
39166                 
39167                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
39168                     continue; // dont' bother, it's not in sort list or being set.
39169                 }
39170                 
39171                 so.push(cm.config[i].dataIndex);
39172             };
39173             dm.sortOrder = so;
39174         }
39175         
39176         
39177         dm.sort(cm.getDataIndex(index));
39178     },
39179
39180
39181     destroy : function(){
39182         if(this.colMenu){
39183             this.colMenu.removeAll();
39184             Roo.menu.MenuMgr.unregister(this.colMenu);
39185             this.colMenu.getEl().remove();
39186             delete this.colMenu;
39187         }
39188         if(this.hmenu){
39189             this.hmenu.removeAll();
39190             Roo.menu.MenuMgr.unregister(this.hmenu);
39191             this.hmenu.getEl().remove();
39192             delete this.hmenu;
39193         }
39194         if(this.grid.enableColumnMove){
39195             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39196             if(dds){
39197                 for(var dd in dds){
39198                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
39199                         var elid = dds[dd].dragElId;
39200                         dds[dd].unreg();
39201                         Roo.get(elid).remove();
39202                     } else if(dds[dd].config.isTarget){
39203                         dds[dd].proxyTop.remove();
39204                         dds[dd].proxyBottom.remove();
39205                         dds[dd].unreg();
39206                     }
39207                     if(Roo.dd.DDM.locationCache[dd]){
39208                         delete Roo.dd.DDM.locationCache[dd];
39209                     }
39210                 }
39211                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39212             }
39213         }
39214         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39215         this.bind(null, null);
39216         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39217     },
39218
39219     handleLockChange : function(){
39220         this.refresh(true);
39221     },
39222
39223     onDenyColumnLock : function(){
39224
39225     },
39226
39227     onDenyColumnHide : function(){
39228
39229     },
39230
39231     handleHdMenuClick : function(item){
39232         var index = this.hdCtxIndex;
39233         var cm = this.cm, ds = this.ds;
39234         switch(item.id){
39235             case "asc":
39236                 ds.sort(cm.getDataIndex(index), "ASC");
39237                 break;
39238             case "desc":
39239                 ds.sort(cm.getDataIndex(index), "DESC");
39240                 break;
39241             case "lock":
39242                 var lc = cm.getLockedCount();
39243                 if(cm.getColumnCount(true) <= lc+1){
39244                     this.onDenyColumnLock();
39245                     return;
39246                 }
39247                 if(lc != index){
39248                     cm.setLocked(index, true, true);
39249                     cm.moveColumn(index, lc);
39250                     this.grid.fireEvent("columnmove", index, lc);
39251                 }else{
39252                     cm.setLocked(index, true);
39253                 }
39254             break;
39255             case "unlock":
39256                 var lc = cm.getLockedCount();
39257                 if((lc-1) != index){
39258                     cm.setLocked(index, false, true);
39259                     cm.moveColumn(index, lc-1);
39260                     this.grid.fireEvent("columnmove", index, lc-1);
39261                 }else{
39262                     cm.setLocked(index, false);
39263                 }
39264             break;
39265             case 'wider': // used to expand cols on touch..
39266             case 'narrow':
39267                 var cw = cm.getColumnWidth(index);
39268                 cw += (item.id == 'wider' ? 1 : -1) * 50;
39269                 cw = Math.max(0, cw);
39270                 cw = Math.min(cw,4000);
39271                 cm.setColumnWidth(index, cw);
39272                 break;
39273                 
39274             default:
39275                 index = cm.getIndexById(item.id.substr(4));
39276                 if(index != -1){
39277                     if(item.checked && cm.getColumnCount(true) <= 1){
39278                         this.onDenyColumnHide();
39279                         return false;
39280                     }
39281                     cm.setHidden(index, item.checked);
39282                 }
39283         }
39284         return true;
39285     },
39286
39287     beforeColMenuShow : function(){
39288         var cm = this.cm,  colCount = cm.getColumnCount();
39289         this.colMenu.removeAll();
39290         
39291         var items = [];
39292         for(var i = 0; i < colCount; i++){
39293             items.push({
39294                 id: "col-"+cm.getColumnId(i),
39295                 text: cm.getColumnHeader(i),
39296                 checked: !cm.isHidden(i),
39297                 hideOnClick:false
39298             });
39299         }
39300         
39301         if (this.grid.sortColMenu) {
39302             items.sort(function(a,b) {
39303                 if (a.text == b.text) {
39304                     return 0;
39305                 }
39306                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39307             });
39308         }
39309         
39310         for(var i = 0; i < colCount; i++){
39311             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39312         }
39313     },
39314
39315     handleHdCtx : function(g, index, e){
39316         e.stopEvent();
39317         var hd = this.getHeaderCell(index);
39318         this.hdCtxIndex = index;
39319         var ms = this.hmenu.items, cm = this.cm;
39320         ms.get("asc").setDisabled(!cm.isSortable(index));
39321         ms.get("desc").setDisabled(!cm.isSortable(index));
39322         if(this.grid.enableColLock !== false){
39323             ms.get("lock").setDisabled(cm.isLocked(index));
39324             ms.get("unlock").setDisabled(!cm.isLocked(index));
39325         }
39326         this.hmenu.show(hd, "tl-bl");
39327     },
39328
39329     handleHdOver : function(e){
39330         var hd = this.findHeaderCell(e.getTarget());
39331         if(hd && !this.headersDisabled){
39332             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39333                this.fly(hd).addClass("x-grid-hd-over");
39334             }
39335         }
39336     },
39337
39338     handleHdOut : function(e){
39339         var hd = this.findHeaderCell(e.getTarget());
39340         if(hd){
39341             this.fly(hd).removeClass("x-grid-hd-over");
39342         }
39343     },
39344
39345     handleSplitDblClick : function(e, t){
39346         var i = this.getCellIndex(t);
39347         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39348             this.autoSizeColumn(i, true);
39349             this.layout();
39350         }
39351     },
39352
39353     render : function(){
39354
39355         var cm = this.cm;
39356         var colCount = cm.getColumnCount();
39357
39358         if(this.grid.monitorWindowResize === true){
39359             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39360         }
39361         var header = this.renderHeaders();
39362         var body = this.templates.body.apply({rows:""});
39363         var html = this.templates.master.apply({
39364             lockedBody: body,
39365             body: body,
39366             lockedHeader: header[0],
39367             header: header[1]
39368         });
39369
39370         //this.updateColumns();
39371
39372         this.grid.getGridEl().dom.innerHTML = html;
39373
39374         this.initElements();
39375         
39376         // a kludge to fix the random scolling effect in webkit
39377         this.el.on("scroll", function() {
39378             this.el.dom.scrollTop=0; // hopefully not recursive..
39379         },this);
39380
39381         this.scroller.on("scroll", this.handleScroll, this);
39382         this.lockedBody.on("mousewheel", this.handleWheel, this);
39383         this.mainBody.on("mousewheel", this.handleWheel, this);
39384
39385         this.mainHd.on("mouseover", this.handleHdOver, this);
39386         this.mainHd.on("mouseout", this.handleHdOut, this);
39387         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39388                 {delegate: "."+this.splitClass});
39389
39390         this.lockedHd.on("mouseover", this.handleHdOver, this);
39391         this.lockedHd.on("mouseout", this.handleHdOut, this);
39392         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39393                 {delegate: "."+this.splitClass});
39394
39395         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39396             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39397         }
39398
39399         this.updateSplitters();
39400
39401         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39402             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39403             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39404         }
39405
39406         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39407             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39408             this.hmenu.add(
39409                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39410                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39411             );
39412             if(this.grid.enableColLock !== false){
39413                 this.hmenu.add('-',
39414                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39415                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39416                 );
39417             }
39418             if (Roo.isTouch) {
39419                  this.hmenu.add('-',
39420                     {id:"wider", text: this.columnsWiderText},
39421                     {id:"narrow", text: this.columnsNarrowText }
39422                 );
39423                 
39424                  
39425             }
39426             
39427             if(this.grid.enableColumnHide !== false){
39428
39429                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39430                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39431                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39432
39433                 this.hmenu.add('-',
39434                     {id:"columns", text: this.columnsText, menu: this.colMenu}
39435                 );
39436             }
39437             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39438
39439             this.grid.on("headercontextmenu", this.handleHdCtx, this);
39440         }
39441
39442         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39443             this.dd = new Roo.grid.GridDragZone(this.grid, {
39444                 ddGroup : this.grid.ddGroup || 'GridDD'
39445             });
39446             
39447         }
39448
39449         /*
39450         for(var i = 0; i < colCount; i++){
39451             if(cm.isHidden(i)){
39452                 this.hideColumn(i);
39453             }
39454             if(cm.config[i].align){
39455                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39456                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39457             }
39458         }*/
39459         
39460         this.updateHeaderSortState();
39461
39462         this.beforeInitialResize();
39463         this.layout(true);
39464
39465         // two part rendering gives faster view to the user
39466         this.renderPhase2.defer(1, this);
39467     },
39468
39469     renderPhase2 : function(){
39470         // render the rows now
39471         this.refresh();
39472         if(this.grid.autoSizeColumns){
39473             this.autoSizeColumns();
39474         }
39475     },
39476
39477     beforeInitialResize : function(){
39478
39479     },
39480
39481     onColumnSplitterMoved : function(i, w){
39482         this.userResized = true;
39483         var cm = this.grid.colModel;
39484         cm.setColumnWidth(i, w, true);
39485         var cid = cm.getColumnId(i);
39486         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39487         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39488         this.updateSplitters();
39489         this.layout();
39490         this.grid.fireEvent("columnresize", i, w);
39491     },
39492
39493     syncRowHeights : function(startIndex, endIndex){
39494         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39495             startIndex = startIndex || 0;
39496             var mrows = this.getBodyTable().rows;
39497             var lrows = this.getLockedTable().rows;
39498             var len = mrows.length-1;
39499             endIndex = Math.min(endIndex || len, len);
39500             for(var i = startIndex; i <= endIndex; i++){
39501                 var m = mrows[i], l = lrows[i];
39502                 var h = Math.max(m.offsetHeight, l.offsetHeight);
39503                 m.style.height = l.style.height = h + "px";
39504             }
39505         }
39506     },
39507
39508     layout : function(initialRender, is2ndPass)
39509     {
39510         var g = this.grid;
39511         var auto = g.autoHeight;
39512         var scrollOffset = 16;
39513         var c = g.getGridEl(), cm = this.cm,
39514                 expandCol = g.autoExpandColumn,
39515                 gv = this;
39516         //c.beginMeasure();
39517
39518         if(!c.dom.offsetWidth){ // display:none?
39519             if(initialRender){
39520                 this.lockedWrap.show();
39521                 this.mainWrap.show();
39522             }
39523             return;
39524         }
39525
39526         var hasLock = this.cm.isLocked(0);
39527
39528         var tbh = this.headerPanel.getHeight();
39529         var bbh = this.footerPanel.getHeight();
39530
39531         if(auto){
39532             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39533             var newHeight = ch + c.getBorderWidth("tb");
39534             if(g.maxHeight){
39535                 newHeight = Math.min(g.maxHeight, newHeight);
39536             }
39537             c.setHeight(newHeight);
39538         }
39539
39540         if(g.autoWidth){
39541             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39542         }
39543
39544         var s = this.scroller;
39545
39546         var csize = c.getSize(true);
39547
39548         this.el.setSize(csize.width, csize.height);
39549
39550         this.headerPanel.setWidth(csize.width);
39551         this.footerPanel.setWidth(csize.width);
39552
39553         var hdHeight = this.mainHd.getHeight();
39554         var vw = csize.width;
39555         var vh = csize.height - (tbh + bbh);
39556
39557         s.setSize(vw, vh);
39558
39559         var bt = this.getBodyTable();
39560         
39561         if(cm.getLockedCount() == cm.config.length){
39562             bt = this.getLockedTable();
39563         }
39564         
39565         var ltWidth = hasLock ?
39566                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39567
39568         var scrollHeight = bt.offsetHeight;
39569         var scrollWidth = ltWidth + bt.offsetWidth;
39570         var vscroll = false, hscroll = false;
39571
39572         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39573
39574         var lw = this.lockedWrap, mw = this.mainWrap;
39575         var lb = this.lockedBody, mb = this.mainBody;
39576
39577         setTimeout(function(){
39578             var t = s.dom.offsetTop;
39579             var w = s.dom.clientWidth,
39580                 h = s.dom.clientHeight;
39581
39582             lw.setTop(t);
39583             lw.setSize(ltWidth, h);
39584
39585             mw.setLeftTop(ltWidth, t);
39586             mw.setSize(w-ltWidth, h);
39587
39588             lb.setHeight(h-hdHeight);
39589             mb.setHeight(h-hdHeight);
39590
39591             if(is2ndPass !== true && !gv.userResized && expandCol){
39592                 // high speed resize without full column calculation
39593                 
39594                 var ci = cm.getIndexById(expandCol);
39595                 if (ci < 0) {
39596                     ci = cm.findColumnIndex(expandCol);
39597                 }
39598                 ci = Math.max(0, ci); // make sure it's got at least the first col.
39599                 var expandId = cm.getColumnId(ci);
39600                 var  tw = cm.getTotalWidth(false);
39601                 var currentWidth = cm.getColumnWidth(ci);
39602                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39603                 if(currentWidth != cw){
39604                     cm.setColumnWidth(ci, cw, true);
39605                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39606                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39607                     gv.updateSplitters();
39608                     gv.layout(false, true);
39609                 }
39610             }
39611
39612             if(initialRender){
39613                 lw.show();
39614                 mw.show();
39615             }
39616             //c.endMeasure();
39617         }, 10);
39618     },
39619
39620     onWindowResize : function(){
39621         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39622             return;
39623         }
39624         this.layout();
39625     },
39626
39627     appendFooter : function(parentEl){
39628         return null;
39629     },
39630
39631     sortAscText : "Sort Ascending",
39632     sortDescText : "Sort Descending",
39633     lockText : "Lock Column",
39634     unlockText : "Unlock Column",
39635     columnsText : "Columns",
39636  
39637     columnsWiderText : "Wider",
39638     columnsNarrowText : "Thinner"
39639 });
39640
39641
39642 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39643     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39644     this.proxy.el.addClass('x-grid3-col-dd');
39645 };
39646
39647 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39648     handleMouseDown : function(e){
39649
39650     },
39651
39652     callHandleMouseDown : function(e){
39653         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39654     }
39655 });
39656 /*
39657  * Based on:
39658  * Ext JS Library 1.1.1
39659  * Copyright(c) 2006-2007, Ext JS, LLC.
39660  *
39661  * Originally Released Under LGPL - original licence link has changed is not relivant.
39662  *
39663  * Fork - LGPL
39664  * <script type="text/javascript">
39665  */
39666  /**
39667  * @extends Roo.dd.DDProxy
39668  * @class Roo.grid.SplitDragZone
39669  * Support for Column Header resizing
39670  * @constructor
39671  * @param {Object} config
39672  */
39673 // private
39674 // This is a support class used internally by the Grid components
39675 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39676     this.grid = grid;
39677     this.view = grid.getView();
39678     this.proxy = this.view.resizeProxy;
39679     Roo.grid.SplitDragZone.superclass.constructor.call(
39680         this,
39681         hd, // ID
39682         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39683         {  // CONFIG
39684             dragElId : Roo.id(this.proxy.dom),
39685             resizeFrame:false
39686         }
39687     );
39688     
39689     this.setHandleElId(Roo.id(hd));
39690     if (hd2 !== false) {
39691         this.setOuterHandleElId(Roo.id(hd2));
39692     }
39693     
39694     this.scroll = false;
39695 };
39696 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39697     fly: Roo.Element.fly,
39698
39699     b4StartDrag : function(x, y){
39700         this.view.headersDisabled = true;
39701         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39702                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39703         );
39704         this.proxy.setHeight(h);
39705         
39706         // for old system colWidth really stored the actual width?
39707         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39708         // which in reality did not work.. - it worked only for fixed sizes
39709         // for resizable we need to use actual sizes.
39710         var w = this.cm.getColumnWidth(this.cellIndex);
39711         if (!this.view.mainWrap) {
39712             // bootstrap.
39713             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39714         }
39715         
39716         
39717         
39718         // this was w-this.grid.minColumnWidth;
39719         // doesnt really make sense? - w = thie curren width or the rendered one?
39720         var minw = Math.max(w-this.grid.minColumnWidth, 0);
39721         this.resetConstraints();
39722         this.setXConstraint(minw, 1000);
39723         this.setYConstraint(0, 0);
39724         this.minX = x - minw;
39725         this.maxX = x + 1000;
39726         this.startPos = x;
39727         if (!this.view.mainWrap) { // this is Bootstrap code..
39728             this.getDragEl().style.display='block';
39729         }
39730         
39731         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39732     },
39733
39734
39735     handleMouseDown : function(e){
39736         ev = Roo.EventObject.setEvent(e);
39737         var t = this.fly(ev.getTarget());
39738         if(t.hasClass("x-grid-split")){
39739             this.cellIndex = this.view.getCellIndex(t.dom);
39740             this.split = t.dom;
39741             this.cm = this.grid.colModel;
39742             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39743                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39744             }
39745         }
39746     },
39747
39748     endDrag : function(e){
39749         this.view.headersDisabled = false;
39750         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39751         var diff = endX - this.startPos;
39752         // 
39753         var w = this.cm.getColumnWidth(this.cellIndex);
39754         if (!this.view.mainWrap) {
39755             w = 0;
39756         }
39757         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39758     },
39759
39760     autoOffset : function(){
39761         this.setDelta(0,0);
39762     }
39763 });/*
39764  * Based on:
39765  * Ext JS Library 1.1.1
39766  * Copyright(c) 2006-2007, Ext JS, LLC.
39767  *
39768  * Originally Released Under LGPL - original licence link has changed is not relivant.
39769  *
39770  * Fork - LGPL
39771  * <script type="text/javascript">
39772  */
39773  
39774 // private
39775 // This is a support class used internally by the Grid components
39776 Roo.grid.GridDragZone = function(grid, config){
39777     this.view = grid.getView();
39778     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39779     if(this.view.lockedBody){
39780         this.setHandleElId(Roo.id(this.view.mainBody.dom));
39781         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39782     }
39783     this.scroll = false;
39784     this.grid = grid;
39785     this.ddel = document.createElement('div');
39786     this.ddel.className = 'x-grid-dd-wrap';
39787 };
39788
39789 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39790     ddGroup : "GridDD",
39791
39792     getDragData : function(e){
39793         var t = Roo.lib.Event.getTarget(e);
39794         var rowIndex = this.view.findRowIndex(t);
39795         var sm = this.grid.selModel;
39796             
39797         //Roo.log(rowIndex);
39798         
39799         if (sm.getSelectedCell) {
39800             // cell selection..
39801             if (!sm.getSelectedCell()) {
39802                 return false;
39803             }
39804             if (rowIndex != sm.getSelectedCell()[0]) {
39805                 return false;
39806             }
39807         
39808         }
39809         if (sm.getSelections && sm.getSelections().length < 1) {
39810             return false;
39811         }
39812         
39813         
39814         // before it used to all dragging of unseleted... - now we dont do that.
39815         if(rowIndex !== false){
39816             
39817             // if editorgrid.. 
39818             
39819             
39820             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39821                
39822             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39823               //  
39824             //}
39825             if (e.hasModifier()){
39826                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39827             }
39828             
39829             Roo.log("getDragData");
39830             
39831             return {
39832                 grid: this.grid,
39833                 ddel: this.ddel,
39834                 rowIndex: rowIndex,
39835                 selections: sm.getSelections ? sm.getSelections() : (
39836                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39837             };
39838         }
39839         return false;
39840     },
39841     
39842     
39843     onInitDrag : function(e){
39844         var data = this.dragData;
39845         this.ddel.innerHTML = this.grid.getDragDropText();
39846         this.proxy.update(this.ddel);
39847         // fire start drag?
39848     },
39849
39850     afterRepair : function(){
39851         this.dragging = false;
39852     },
39853
39854     getRepairXY : function(e, data){
39855         return false;
39856     },
39857
39858     onEndDrag : function(data, e){
39859         // fire end drag?
39860     },
39861
39862     onValidDrop : function(dd, e, id){
39863         // fire drag drop?
39864         this.hideProxy();
39865     },
39866
39867     beforeInvalidDrop : function(e, id){
39868
39869     }
39870 });/*
39871  * Based on:
39872  * Ext JS Library 1.1.1
39873  * Copyright(c) 2006-2007, Ext JS, LLC.
39874  *
39875  * Originally Released Under LGPL - original licence link has changed is not relivant.
39876  *
39877  * Fork - LGPL
39878  * <script type="text/javascript">
39879  */
39880  
39881
39882 /**
39883  * @class Roo.grid.ColumnModel
39884  * @extends Roo.util.Observable
39885  * This is the default implementation of a ColumnModel used by the Grid. It defines
39886  * the columns in the grid.
39887  * <br>Usage:<br>
39888  <pre><code>
39889  var colModel = new Roo.grid.ColumnModel([
39890         {header: "Ticker", width: 60, sortable: true, locked: true},
39891         {header: "Company Name", width: 150, sortable: true},
39892         {header: "Market Cap.", width: 100, sortable: true},
39893         {header: "$ Sales", width: 100, sortable: true, renderer: money},
39894         {header: "Employees", width: 100, sortable: true, resizable: false}
39895  ]);
39896  </code></pre>
39897  * <p>
39898  
39899  * The config options listed for this class are options which may appear in each
39900  * individual column definition.
39901  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39902  * @constructor
39903  * @param {Object} config An Array of column config objects. See this class's
39904  * config objects for details.
39905 */
39906 Roo.grid.ColumnModel = function(config){
39907         /**
39908      * The config passed into the constructor
39909      */
39910     this.config = []; //config;
39911     this.lookup = {};
39912
39913     // if no id, create one
39914     // if the column does not have a dataIndex mapping,
39915     // map it to the order it is in the config
39916     for(var i = 0, len = config.length; i < len; i++){
39917         this.addColumn(config[i]);
39918         
39919     }
39920
39921     /**
39922      * The width of columns which have no width specified (defaults to 100)
39923      * @type Number
39924      */
39925     this.defaultWidth = 100;
39926
39927     /**
39928      * Default sortable of columns which have no sortable specified (defaults to false)
39929      * @type Boolean
39930      */
39931     this.defaultSortable = false;
39932
39933     this.addEvents({
39934         /**
39935              * @event widthchange
39936              * Fires when the width of a column changes.
39937              * @param {ColumnModel} this
39938              * @param {Number} columnIndex The column index
39939              * @param {Number} newWidth The new width
39940              */
39941             "widthchange": true,
39942         /**
39943              * @event headerchange
39944              * Fires when the text of a header changes.
39945              * @param {ColumnModel} this
39946              * @param {Number} columnIndex The column index
39947              * @param {Number} newText The new header text
39948              */
39949             "headerchange": true,
39950         /**
39951              * @event hiddenchange
39952              * Fires when a column is hidden or "unhidden".
39953              * @param {ColumnModel} this
39954              * @param {Number} columnIndex The column index
39955              * @param {Boolean} hidden true if hidden, false otherwise
39956              */
39957             "hiddenchange": true,
39958             /**
39959          * @event columnmoved
39960          * Fires when a column is moved.
39961          * @param {ColumnModel} this
39962          * @param {Number} oldIndex
39963          * @param {Number} newIndex
39964          */
39965         "columnmoved" : true,
39966         /**
39967          * @event columlockchange
39968          * Fires when a column's locked state is changed
39969          * @param {ColumnModel} this
39970          * @param {Number} colIndex
39971          * @param {Boolean} locked true if locked
39972          */
39973         "columnlockchange" : true
39974     });
39975     Roo.grid.ColumnModel.superclass.constructor.call(this);
39976 };
39977 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39978     /**
39979      * @cfg {String} header [required] The header text to display in the Grid view.
39980      */
39981         /**
39982      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39983      */
39984         /**
39985      * @cfg {String} smHeader Header at Bootsrap Small width
39986      */
39987         /**
39988      * @cfg {String} mdHeader Header at Bootsrap Medium width
39989      */
39990         /**
39991      * @cfg {String} lgHeader Header at Bootsrap Large width
39992      */
39993         /**
39994      * @cfg {String} xlHeader Header at Bootsrap extra Large width
39995      */
39996     /**
39997      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
39998      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39999      * specified, the column's index is used as an index into the Record's data Array.
40000      */
40001     /**
40002      * @cfg {Number} width  The initial width in pixels of the column. Using this
40003      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
40004      */
40005     /**
40006      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
40007      * Defaults to the value of the {@link #defaultSortable} property.
40008      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
40009      */
40010     /**
40011      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
40012      */
40013     /**
40014      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
40015      */
40016     /**
40017      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
40018      */
40019     /**
40020      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
40021      */
40022     /**
40023      * @cfg {Function} renderer A function used to generate HTML markup for a cell
40024      * given the cell's data value. See {@link #setRenderer}. If not specified, the
40025      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
40026      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
40027      */
40028        /**
40029      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
40030      */
40031     /**
40032      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
40033      */
40034     /**
40035      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
40036      */
40037     /**
40038      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
40039      */
40040     /**
40041      * @cfg {String} tooltip mouse over tooltip text
40042      */
40043     /**
40044      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
40045      */
40046     /**
40047      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
40048      */
40049     /**
40050      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
40051      */
40052     /**
40053      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
40054      */
40055         /**
40056      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
40057      */
40058     /**
40059      * Returns the id of the column at the specified index.
40060      * @param {Number} index The column index
40061      * @return {String} the id
40062      */
40063     getColumnId : function(index){
40064         return this.config[index].id;
40065     },
40066
40067     /**
40068      * Returns the column for a specified id.
40069      * @param {String} id The column id
40070      * @return {Object} the column
40071      */
40072     getColumnById : function(id){
40073         return this.lookup[id];
40074     },
40075
40076     
40077     /**
40078      * Returns the column Object for a specified dataIndex.
40079      * @param {String} dataIndex The column dataIndex
40080      * @return {Object|Boolean} the column or false if not found
40081      */
40082     getColumnByDataIndex: function(dataIndex){
40083         var index = this.findColumnIndex(dataIndex);
40084         return index > -1 ? this.config[index] : false;
40085     },
40086     
40087     /**
40088      * Returns the index for a specified column id.
40089      * @param {String} id The column id
40090      * @return {Number} the index, or -1 if not found
40091      */
40092     getIndexById : function(id){
40093         for(var i = 0, len = this.config.length; i < len; i++){
40094             if(this.config[i].id == id){
40095                 return i;
40096             }
40097         }
40098         return -1;
40099     },
40100     
40101     /**
40102      * Returns the index for a specified column dataIndex.
40103      * @param {String} dataIndex The column dataIndex
40104      * @return {Number} the index, or -1 if not found
40105      */
40106     
40107     findColumnIndex : function(dataIndex){
40108         for(var i = 0, len = this.config.length; i < len; i++){
40109             if(this.config[i].dataIndex == dataIndex){
40110                 return i;
40111             }
40112         }
40113         return -1;
40114     },
40115     
40116     
40117     moveColumn : function(oldIndex, newIndex){
40118         var c = this.config[oldIndex];
40119         this.config.splice(oldIndex, 1);
40120         this.config.splice(newIndex, 0, c);
40121         this.dataMap = null;
40122         this.fireEvent("columnmoved", this, oldIndex, newIndex);
40123     },
40124
40125     isLocked : function(colIndex){
40126         return this.config[colIndex].locked === true;
40127     },
40128
40129     setLocked : function(colIndex, value, suppressEvent){
40130         if(this.isLocked(colIndex) == value){
40131             return;
40132         }
40133         this.config[colIndex].locked = value;
40134         if(!suppressEvent){
40135             this.fireEvent("columnlockchange", this, colIndex, value);
40136         }
40137     },
40138
40139     getTotalLockedWidth : function(){
40140         var totalWidth = 0;
40141         for(var i = 0; i < this.config.length; i++){
40142             if(this.isLocked(i) && !this.isHidden(i)){
40143                 this.totalWidth += this.getColumnWidth(i);
40144             }
40145         }
40146         return totalWidth;
40147     },
40148
40149     getLockedCount : function(){
40150         for(var i = 0, len = this.config.length; i < len; i++){
40151             if(!this.isLocked(i)){
40152                 return i;
40153             }
40154         }
40155         
40156         return this.config.length;
40157     },
40158
40159     /**
40160      * Returns the number of columns.
40161      * @return {Number}
40162      */
40163     getColumnCount : function(visibleOnly){
40164         if(visibleOnly === true){
40165             var c = 0;
40166             for(var i = 0, len = this.config.length; i < len; i++){
40167                 if(!this.isHidden(i)){
40168                     c++;
40169                 }
40170             }
40171             return c;
40172         }
40173         return this.config.length;
40174     },
40175
40176     /**
40177      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
40178      * @param {Function} fn
40179      * @param {Object} scope (optional)
40180      * @return {Array} result
40181      */
40182     getColumnsBy : function(fn, scope){
40183         var r = [];
40184         for(var i = 0, len = this.config.length; i < len; i++){
40185             var c = this.config[i];
40186             if(fn.call(scope||this, c, i) === true){
40187                 r[r.length] = c;
40188             }
40189         }
40190         return r;
40191     },
40192
40193     /**
40194      * Returns true if the specified column is sortable.
40195      * @param {Number} col The column index
40196      * @return {Boolean}
40197      */
40198     isSortable : function(col){
40199         if(typeof this.config[col].sortable == "undefined"){
40200             return this.defaultSortable;
40201         }
40202         return this.config[col].sortable;
40203     },
40204
40205     /**
40206      * Returns the rendering (formatting) function defined for the column.
40207      * @param {Number} col The column index.
40208      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40209      */
40210     getRenderer : function(col){
40211         if(!this.config[col].renderer){
40212             return Roo.grid.ColumnModel.defaultRenderer;
40213         }
40214         return this.config[col].renderer;
40215     },
40216
40217     /**
40218      * Sets the rendering (formatting) function for a column.
40219      * @param {Number} col The column index
40220      * @param {Function} fn The function to use to process the cell's raw data
40221      * to return HTML markup for the grid view. The render function is called with
40222      * the following parameters:<ul>
40223      * <li>Data value.</li>
40224      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40225      * <li>css A CSS style string to apply to the table cell.</li>
40226      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40227      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40228      * <li>Row index</li>
40229      * <li>Column index</li>
40230      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40231      */
40232     setRenderer : function(col, fn){
40233         this.config[col].renderer = fn;
40234     },
40235
40236     /**
40237      * Returns the width for the specified column.
40238      * @param {Number} col The column index
40239      * @param (optional) {String} gridSize bootstrap width size.
40240      * @return {Number}
40241      */
40242     getColumnWidth : function(col, gridSize)
40243         {
40244                 var cfg = this.config[col];
40245                 
40246                 if (typeof(gridSize) == 'undefined') {
40247                         return cfg.width * 1 || this.defaultWidth;
40248                 }
40249                 if (gridSize === false) { // if we set it..
40250                         return cfg.width || false;
40251                 }
40252                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40253                 
40254                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40255                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40256                                 continue;
40257                         }
40258                         return cfg[ sizes[i] ];
40259                 }
40260                 return 1;
40261                 
40262     },
40263
40264     /**
40265      * Sets the width for a column.
40266      * @param {Number} col The column index
40267      * @param {Number} width The new width
40268      */
40269     setColumnWidth : function(col, width, suppressEvent){
40270         this.config[col].width = width;
40271         this.totalWidth = null;
40272         if(!suppressEvent){
40273              this.fireEvent("widthchange", this, col, width);
40274         }
40275     },
40276
40277     /**
40278      * Returns the total width of all columns.
40279      * @param {Boolean} includeHidden True to include hidden column widths
40280      * @return {Number}
40281      */
40282     getTotalWidth : function(includeHidden){
40283         if(!this.totalWidth){
40284             this.totalWidth = 0;
40285             for(var i = 0, len = this.config.length; i < len; i++){
40286                 if(includeHidden || !this.isHidden(i)){
40287                     this.totalWidth += this.getColumnWidth(i);
40288                 }
40289             }
40290         }
40291         return this.totalWidth;
40292     },
40293
40294     /**
40295      * Returns the header for the specified column.
40296      * @param {Number} col The column index
40297      * @return {String}
40298      */
40299     getColumnHeader : function(col){
40300         return this.config[col].header;
40301     },
40302
40303     /**
40304      * Sets the header for a column.
40305      * @param {Number} col The column index
40306      * @param {String} header The new header
40307      */
40308     setColumnHeader : function(col, header){
40309         this.config[col].header = header;
40310         this.fireEvent("headerchange", this, col, header);
40311     },
40312
40313     /**
40314      * Returns the tooltip for the specified column.
40315      * @param {Number} col The column index
40316      * @return {String}
40317      */
40318     getColumnTooltip : function(col){
40319             return this.config[col].tooltip;
40320     },
40321     /**
40322      * Sets the tooltip for a column.
40323      * @param {Number} col The column index
40324      * @param {String} tooltip The new tooltip
40325      */
40326     setColumnTooltip : function(col, tooltip){
40327             this.config[col].tooltip = tooltip;
40328     },
40329
40330     /**
40331      * Returns the dataIndex for the specified column.
40332      * @param {Number} col The column index
40333      * @return {Number}
40334      */
40335     getDataIndex : function(col){
40336         return this.config[col].dataIndex;
40337     },
40338
40339     /**
40340      * Sets the dataIndex for a column.
40341      * @param {Number} col The column index
40342      * @param {Number} dataIndex The new dataIndex
40343      */
40344     setDataIndex : function(col, dataIndex){
40345         this.config[col].dataIndex = dataIndex;
40346     },
40347
40348     
40349     
40350     /**
40351      * Returns true if the cell is editable.
40352      * @param {Number} colIndex The column index
40353      * @param {Number} rowIndex The row index - this is nto actually used..?
40354      * @return {Boolean}
40355      */
40356     isCellEditable : function(colIndex, rowIndex){
40357         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40358     },
40359
40360     /**
40361      * Returns the editor defined for the cell/column.
40362      * return false or null to disable editing.
40363      * @param {Number} colIndex The column index
40364      * @param {Number} rowIndex The row index
40365      * @return {Object}
40366      */
40367     getCellEditor : function(colIndex, rowIndex){
40368         return this.config[colIndex].editor;
40369     },
40370
40371     /**
40372      * Sets if a column is editable.
40373      * @param {Number} col The column index
40374      * @param {Boolean} editable True if the column is editable
40375      */
40376     setEditable : function(col, editable){
40377         this.config[col].editable = editable;
40378     },
40379
40380
40381     /**
40382      * Returns true if the column is hidden.
40383      * @param {Number} colIndex The column index
40384      * @return {Boolean}
40385      */
40386     isHidden : function(colIndex){
40387         return this.config[colIndex].hidden;
40388     },
40389
40390
40391     /**
40392      * Returns true if the column width cannot be changed
40393      */
40394     isFixed : function(colIndex){
40395         return this.config[colIndex].fixed;
40396     },
40397
40398     /**
40399      * Returns true if the column can be resized
40400      * @return {Boolean}
40401      */
40402     isResizable : function(colIndex){
40403         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40404     },
40405     /**
40406      * Sets if a column is hidden.
40407      * @param {Number} colIndex The column index
40408      * @param {Boolean} hidden True if the column is hidden
40409      */
40410     setHidden : function(colIndex, hidden){
40411         this.config[colIndex].hidden = hidden;
40412         this.totalWidth = null;
40413         this.fireEvent("hiddenchange", this, colIndex, hidden);
40414     },
40415
40416     /**
40417      * Sets the editor for a column.
40418      * @param {Number} col The column index
40419      * @param {Object} editor The editor object
40420      */
40421     setEditor : function(col, editor){
40422         this.config[col].editor = editor;
40423     },
40424     /**
40425      * Add a column (experimental...) - defaults to adding to the end..
40426      * @param {Object} config 
40427     */
40428     addColumn : function(c)
40429     {
40430     
40431         var i = this.config.length;
40432         this.config[i] = c;
40433         
40434         if(typeof c.dataIndex == "undefined"){
40435             c.dataIndex = i;
40436         }
40437         if(typeof c.renderer == "string"){
40438             c.renderer = Roo.util.Format[c.renderer];
40439         }
40440         if(typeof c.id == "undefined"){
40441             c.id = Roo.id();
40442         }
40443         if(c.editor && c.editor.xtype){
40444             c.editor  = Roo.factory(c.editor, Roo.grid);
40445         }
40446         if(c.editor && c.editor.isFormField){
40447             c.editor = new Roo.grid.GridEditor(c.editor);
40448         }
40449         this.lookup[c.id] = c;
40450     }
40451     
40452 });
40453
40454 Roo.grid.ColumnModel.defaultRenderer = function(value)
40455 {
40456     if(typeof value == "object") {
40457         return value;
40458     }
40459         if(typeof value == "string" && value.length < 1){
40460             return "&#160;";
40461         }
40462     
40463         return String.format("{0}", value);
40464 };
40465
40466 // Alias for backwards compatibility
40467 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40468 /*
40469  * Based on:
40470  * Ext JS Library 1.1.1
40471  * Copyright(c) 2006-2007, Ext JS, LLC.
40472  *
40473  * Originally Released Under LGPL - original licence link has changed is not relivant.
40474  *
40475  * Fork - LGPL
40476  * <script type="text/javascript">
40477  */
40478
40479 /**
40480  * @class Roo.grid.AbstractSelectionModel
40481  * @extends Roo.util.Observable
40482  * @abstract
40483  * Abstract base class for grid SelectionModels.  It provides the interface that should be
40484  * implemented by descendant classes.  This class should not be directly instantiated.
40485  * @constructor
40486  */
40487 Roo.grid.AbstractSelectionModel = function(){
40488     this.locked = false;
40489     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40490 };
40491
40492 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
40493     /** @ignore Called by the grid automatically. Do not call directly. */
40494     init : function(grid){
40495         this.grid = grid;
40496         this.initEvents();
40497     },
40498
40499     /**
40500      * Locks the selections.
40501      */
40502     lock : function(){
40503         this.locked = true;
40504     },
40505
40506     /**
40507      * Unlocks the selections.
40508      */
40509     unlock : function(){
40510         this.locked = false;
40511     },
40512
40513     /**
40514      * Returns true if the selections are locked.
40515      * @return {Boolean}
40516      */
40517     isLocked : function(){
40518         return this.locked;
40519     }
40520 });/*
40521  * Based on:
40522  * Ext JS Library 1.1.1
40523  * Copyright(c) 2006-2007, Ext JS, LLC.
40524  *
40525  * Originally Released Under LGPL - original licence link has changed is not relivant.
40526  *
40527  * Fork - LGPL
40528  * <script type="text/javascript">
40529  */
40530 /**
40531  * @extends Roo.grid.AbstractSelectionModel
40532  * @class Roo.grid.RowSelectionModel
40533  * The default SelectionModel used by {@link Roo.grid.Grid}.
40534  * It supports multiple selections and keyboard selection/navigation. 
40535  * @constructor
40536  * @param {Object} config
40537  */
40538 Roo.grid.RowSelectionModel = function(config){
40539     Roo.apply(this, config);
40540     this.selections = new Roo.util.MixedCollection(false, function(o){
40541         return o.id;
40542     });
40543
40544     this.last = false;
40545     this.lastActive = false;
40546
40547     this.addEvents({
40548         /**
40549         * @event selectionchange
40550         * Fires when the selection changes
40551         * @param {SelectionModel} this
40552         */
40553        "selectionchange" : true,
40554        /**
40555         * @event afterselectionchange
40556         * Fires after the selection changes (eg. by key press or clicking)
40557         * @param {SelectionModel} this
40558         */
40559        "afterselectionchange" : true,
40560        /**
40561         * @event beforerowselect
40562         * Fires when a row is selected being selected, return false to cancel.
40563         * @param {SelectionModel} this
40564         * @param {Number} rowIndex The selected index
40565         * @param {Boolean} keepExisting False if other selections will be cleared
40566         */
40567        "beforerowselect" : true,
40568        /**
40569         * @event rowselect
40570         * Fires when a row is selected.
40571         * @param {SelectionModel} this
40572         * @param {Number} rowIndex The selected index
40573         * @param {Roo.data.Record} r The record
40574         */
40575        "rowselect" : true,
40576        /**
40577         * @event rowdeselect
40578         * Fires when a row is deselected.
40579         * @param {SelectionModel} this
40580         * @param {Number} rowIndex The selected index
40581         */
40582         "rowdeselect" : true
40583     });
40584     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40585     this.locked = false;
40586 };
40587
40588 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
40589     /**
40590      * @cfg {Boolean} singleSelect
40591      * True to allow selection of only one row at a time (defaults to false)
40592      */
40593     singleSelect : false,
40594
40595     // private
40596     initEvents : function(){
40597
40598         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40599             this.grid.on("mousedown", this.handleMouseDown, this);
40600         }else{ // allow click to work like normal
40601             this.grid.on("rowclick", this.handleDragableRowClick, this);
40602         }
40603         // bootstrap does not have a view..
40604         var view = this.grid.view ? this.grid.view : this.grid;
40605         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40606             "up" : function(e){
40607                 if(!e.shiftKey){
40608                     this.selectPrevious(e.shiftKey);
40609                 }else if(this.last !== false && this.lastActive !== false){
40610                     var last = this.last;
40611                     this.selectRange(this.last,  this.lastActive-1);
40612                     view.focusRow(this.lastActive);
40613                     if(last !== false){
40614                         this.last = last;
40615                     }
40616                 }else{
40617                     this.selectFirstRow();
40618                 }
40619                 this.fireEvent("afterselectionchange", this);
40620             },
40621             "down" : function(e){
40622                 if(!e.shiftKey){
40623                     this.selectNext(e.shiftKey);
40624                 }else if(this.last !== false && this.lastActive !== false){
40625                     var last = this.last;
40626                     this.selectRange(this.last,  this.lastActive+1);
40627                     view.focusRow(this.lastActive);
40628                     if(last !== false){
40629                         this.last = last;
40630                     }
40631                 }else{
40632                     this.selectFirstRow();
40633                 }
40634                 this.fireEvent("afterselectionchange", this);
40635             },
40636             scope: this
40637         });
40638
40639          
40640         view.on("refresh", this.onRefresh, this);
40641         view.on("rowupdated", this.onRowUpdated, this);
40642         view.on("rowremoved", this.onRemove, this);
40643     },
40644
40645     // private
40646     onRefresh : function(){
40647         var ds = this.grid.ds, i, v = this.grid.view;
40648         var s = this.selections;
40649         s.each(function(r){
40650             if((i = ds.indexOfId(r.id)) != -1){
40651                 v.onRowSelect(i);
40652                 s.add(ds.getAt(i)); // updating the selection relate data
40653             }else{
40654                 s.remove(r);
40655             }
40656         });
40657     },
40658
40659     // private
40660     onRemove : function(v, index, r){
40661         this.selections.remove(r);
40662     },
40663
40664     // private
40665     onRowUpdated : function(v, index, r){
40666         if(this.isSelected(r)){
40667             v.onRowSelect(index);
40668         }
40669     },
40670
40671     /**
40672      * Select records.
40673      * @param {Array} records The records to select
40674      * @param {Boolean} keepExisting (optional) True to keep existing selections
40675      */
40676     selectRecords : function(records, keepExisting){
40677         if(!keepExisting){
40678             this.clearSelections();
40679         }
40680         var ds = this.grid.ds;
40681         for(var i = 0, len = records.length; i < len; i++){
40682             this.selectRow(ds.indexOf(records[i]), true);
40683         }
40684     },
40685
40686     /**
40687      * Gets the number of selected rows.
40688      * @return {Number}
40689      */
40690     getCount : function(){
40691         return this.selections.length;
40692     },
40693
40694     /**
40695      * Selects the first row in the grid.
40696      */
40697     selectFirstRow : function(){
40698         this.selectRow(0);
40699     },
40700
40701     /**
40702      * Select the last row.
40703      * @param {Boolean} keepExisting (optional) True to keep existing selections
40704      */
40705     selectLastRow : function(keepExisting){
40706         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40707     },
40708
40709     /**
40710      * Selects the row immediately following the last selected row.
40711      * @param {Boolean} keepExisting (optional) True to keep existing selections
40712      */
40713     selectNext : function(keepExisting){
40714         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40715             this.selectRow(this.last+1, keepExisting);
40716             var view = this.grid.view ? this.grid.view : this.grid;
40717             view.focusRow(this.last);
40718         }
40719     },
40720
40721     /**
40722      * Selects the row that precedes the last selected row.
40723      * @param {Boolean} keepExisting (optional) True to keep existing selections
40724      */
40725     selectPrevious : function(keepExisting){
40726         if(this.last){
40727             this.selectRow(this.last-1, keepExisting);
40728             var view = this.grid.view ? this.grid.view : this.grid;
40729             view.focusRow(this.last);
40730         }
40731     },
40732
40733     /**
40734      * Returns the selected records
40735      * @return {Array} Array of selected records
40736      */
40737     getSelections : function(){
40738         return [].concat(this.selections.items);
40739     },
40740
40741     /**
40742      * Returns the first selected record.
40743      * @return {Record}
40744      */
40745     getSelected : function(){
40746         return this.selections.itemAt(0);
40747     },
40748
40749
40750     /**
40751      * Clears all selections.
40752      */
40753     clearSelections : function(fast){
40754         if(this.locked) {
40755             return;
40756         }
40757         if(fast !== true){
40758             var ds = this.grid.ds;
40759             var s = this.selections;
40760             s.each(function(r){
40761                 this.deselectRow(ds.indexOfId(r.id));
40762             }, this);
40763             s.clear();
40764         }else{
40765             this.selections.clear();
40766         }
40767         this.last = false;
40768     },
40769
40770
40771     /**
40772      * Selects all rows.
40773      */
40774     selectAll : function(){
40775         if(this.locked) {
40776             return;
40777         }
40778         this.selections.clear();
40779         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40780             this.selectRow(i, true);
40781         }
40782     },
40783
40784     /**
40785      * Returns True if there is a selection.
40786      * @return {Boolean}
40787      */
40788     hasSelection : function(){
40789         return this.selections.length > 0;
40790     },
40791
40792     /**
40793      * Returns True if the specified row is selected.
40794      * @param {Number/Record} record The record or index of the record to check
40795      * @return {Boolean}
40796      */
40797     isSelected : function(index){
40798         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40799         return (r && this.selections.key(r.id) ? true : false);
40800     },
40801
40802     /**
40803      * Returns True if the specified record id is selected.
40804      * @param {String} id The id of record to check
40805      * @return {Boolean}
40806      */
40807     isIdSelected : function(id){
40808         return (this.selections.key(id) ? true : false);
40809     },
40810
40811     // private
40812     handleMouseDown : function(e, t)
40813     {
40814         var view = this.grid.view ? this.grid.view : this.grid;
40815         var rowIndex;
40816         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40817             return;
40818         };
40819         if(e.shiftKey && this.last !== false){
40820             var last = this.last;
40821             this.selectRange(last, rowIndex, e.ctrlKey);
40822             this.last = last; // reset the last
40823             view.focusRow(rowIndex);
40824         }else{
40825             var isSelected = this.isSelected(rowIndex);
40826             if(e.button !== 0 && isSelected){
40827                 view.focusRow(rowIndex);
40828             }else if(e.ctrlKey && isSelected){
40829                 this.deselectRow(rowIndex);
40830             }else if(!isSelected){
40831                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40832                 view.focusRow(rowIndex);
40833             }
40834         }
40835         this.fireEvent("afterselectionchange", this);
40836     },
40837     // private
40838     handleDragableRowClick :  function(grid, rowIndex, e) 
40839     {
40840         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40841             this.selectRow(rowIndex, false);
40842             var view = this.grid.view ? this.grid.view : this.grid;
40843             view.focusRow(rowIndex);
40844              this.fireEvent("afterselectionchange", this);
40845         }
40846     },
40847     
40848     /**
40849      * Selects multiple rows.
40850      * @param {Array} rows Array of the indexes of the row to select
40851      * @param {Boolean} keepExisting (optional) True to keep existing selections
40852      */
40853     selectRows : function(rows, keepExisting){
40854         if(!keepExisting){
40855             this.clearSelections();
40856         }
40857         for(var i = 0, len = rows.length; i < len; i++){
40858             this.selectRow(rows[i], true);
40859         }
40860     },
40861
40862     /**
40863      * Selects a range of rows. All rows in between startRow and endRow are also selected.
40864      * @param {Number} startRow The index of the first row in the range
40865      * @param {Number} endRow The index of the last row in the range
40866      * @param {Boolean} keepExisting (optional) True to retain existing selections
40867      */
40868     selectRange : function(startRow, endRow, keepExisting){
40869         if(this.locked) {
40870             return;
40871         }
40872         if(!keepExisting){
40873             this.clearSelections();
40874         }
40875         if(startRow <= endRow){
40876             for(var i = startRow; i <= endRow; i++){
40877                 this.selectRow(i, true);
40878             }
40879         }else{
40880             for(var i = startRow; i >= endRow; i--){
40881                 this.selectRow(i, true);
40882             }
40883         }
40884     },
40885
40886     /**
40887      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40888      * @param {Number} startRow The index of the first row in the range
40889      * @param {Number} endRow The index of the last row in the range
40890      */
40891     deselectRange : function(startRow, endRow, preventViewNotify){
40892         if(this.locked) {
40893             return;
40894         }
40895         for(var i = startRow; i <= endRow; i++){
40896             this.deselectRow(i, preventViewNotify);
40897         }
40898     },
40899
40900     /**
40901      * Selects a row.
40902      * @param {Number} row The index of the row to select
40903      * @param {Boolean} keepExisting (optional) True to keep existing selections
40904      */
40905     selectRow : function(index, keepExisting, preventViewNotify){
40906         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40907             return;
40908         }
40909         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40910             if(!keepExisting || this.singleSelect){
40911                 this.clearSelections();
40912             }
40913             var r = this.grid.ds.getAt(index);
40914             this.selections.add(r);
40915             this.last = this.lastActive = index;
40916             if(!preventViewNotify){
40917                 var view = this.grid.view ? this.grid.view : this.grid;
40918                 view.onRowSelect(index);
40919             }
40920             this.fireEvent("rowselect", this, index, r);
40921             this.fireEvent("selectionchange", this);
40922         }
40923     },
40924
40925     /**
40926      * Deselects a row.
40927      * @param {Number} row The index of the row to deselect
40928      */
40929     deselectRow : function(index, preventViewNotify){
40930         if(this.locked) {
40931             return;
40932         }
40933         if(this.last == index){
40934             this.last = false;
40935         }
40936         if(this.lastActive == index){
40937             this.lastActive = false;
40938         }
40939         var r = this.grid.ds.getAt(index);
40940         this.selections.remove(r);
40941         if(!preventViewNotify){
40942             var view = this.grid.view ? this.grid.view : this.grid;
40943             view.onRowDeselect(index);
40944         }
40945         this.fireEvent("rowdeselect", this, index);
40946         this.fireEvent("selectionchange", this);
40947     },
40948
40949     // private
40950     restoreLast : function(){
40951         if(this._last){
40952             this.last = this._last;
40953         }
40954     },
40955
40956     // private
40957     acceptsNav : function(row, col, cm){
40958         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40959     },
40960
40961     // private
40962     onEditorKey : function(field, e){
40963         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40964         if(k == e.TAB){
40965             e.stopEvent();
40966             ed.completeEdit();
40967             if(e.shiftKey){
40968                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40969             }else{
40970                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40971             }
40972         }else if(k == e.ENTER && !e.ctrlKey){
40973             e.stopEvent();
40974             ed.completeEdit();
40975             if(e.shiftKey){
40976                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40977             }else{
40978                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40979             }
40980         }else if(k == e.ESC){
40981             ed.cancelEdit();
40982         }
40983         if(newCell){
40984             g.startEditing(newCell[0], newCell[1]);
40985         }
40986     }
40987 });/*
40988  * Based on:
40989  * Ext JS Library 1.1.1
40990  * Copyright(c) 2006-2007, Ext JS, LLC.
40991  *
40992  * Originally Released Under LGPL - original licence link has changed is not relivant.
40993  *
40994  * Fork - LGPL
40995  * <script type="text/javascript">
40996  */
40997 /**
40998  * @class Roo.grid.CellSelectionModel
40999  * @extends Roo.grid.AbstractSelectionModel
41000  * This class provides the basic implementation for cell selection in a grid.
41001  * @constructor
41002  * @param {Object} config The object containing the configuration of this model.
41003  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
41004  */
41005 Roo.grid.CellSelectionModel = function(config){
41006     Roo.apply(this, config);
41007
41008     this.selection = null;
41009
41010     this.addEvents({
41011         /**
41012              * @event beforerowselect
41013              * Fires before a cell is selected.
41014              * @param {SelectionModel} this
41015              * @param {Number} rowIndex The selected row index
41016              * @param {Number} colIndex The selected cell index
41017              */
41018             "beforecellselect" : true,
41019         /**
41020              * @event cellselect
41021              * Fires when a cell is selected.
41022              * @param {SelectionModel} this
41023              * @param {Number} rowIndex The selected row index
41024              * @param {Number} colIndex The selected cell index
41025              */
41026             "cellselect" : true,
41027         /**
41028              * @event selectionchange
41029              * Fires when the active selection changes.
41030              * @param {SelectionModel} this
41031              * @param {Object} selection null for no selection or an object (o) with two properties
41032                 <ul>
41033                 <li>o.record: the record object for the row the selection is in</li>
41034                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
41035                 </ul>
41036              */
41037             "selectionchange" : true,
41038         /**
41039              * @event tabend
41040              * Fires when the tab (or enter) was pressed on the last editable cell
41041              * You can use this to trigger add new row.
41042              * @param {SelectionModel} this
41043              */
41044             "tabend" : true,
41045          /**
41046              * @event beforeeditnext
41047              * Fires before the next editable sell is made active
41048              * You can use this to skip to another cell or fire the tabend
41049              *    if you set cell to false
41050              * @param {Object} eventdata object : { cell : [ row, col ] } 
41051              */
41052             "beforeeditnext" : true
41053     });
41054     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
41055 };
41056
41057 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
41058     
41059     enter_is_tab: false,
41060
41061     /** @ignore */
41062     initEvents : function(){
41063         this.grid.on("mousedown", this.handleMouseDown, this);
41064         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
41065         var view = this.grid.view;
41066         view.on("refresh", this.onViewChange, this);
41067         view.on("rowupdated", this.onRowUpdated, this);
41068         view.on("beforerowremoved", this.clearSelections, this);
41069         view.on("beforerowsinserted", this.clearSelections, this);
41070         if(this.grid.isEditor){
41071             this.grid.on("beforeedit", this.beforeEdit,  this);
41072         }
41073     },
41074
41075         //private
41076     beforeEdit : function(e){
41077         this.select(e.row, e.column, false, true, e.record);
41078     },
41079
41080         //private
41081     onRowUpdated : function(v, index, r){
41082         if(this.selection && this.selection.record == r){
41083             v.onCellSelect(index, this.selection.cell[1]);
41084         }
41085     },
41086
41087         //private
41088     onViewChange : function(){
41089         this.clearSelections(true);
41090     },
41091
41092         /**
41093          * Returns the currently selected cell,.
41094          * @return {Array} The selected cell (row, column) or null if none selected.
41095          */
41096     getSelectedCell : function(){
41097         return this.selection ? this.selection.cell : null;
41098     },
41099
41100     /**
41101      * Clears all selections.
41102      * @param {Boolean} true to prevent the gridview from being notified about the change.
41103      */
41104     clearSelections : function(preventNotify){
41105         var s = this.selection;
41106         if(s){
41107             if(preventNotify !== true){
41108                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
41109             }
41110             this.selection = null;
41111             this.fireEvent("selectionchange", this, null);
41112         }
41113     },
41114
41115     /**
41116      * Returns true if there is a selection.
41117      * @return {Boolean}
41118      */
41119     hasSelection : function(){
41120         return this.selection ? true : false;
41121     },
41122
41123     /** @ignore */
41124     handleMouseDown : function(e, t){
41125         var v = this.grid.getView();
41126         if(this.isLocked()){
41127             return;
41128         };
41129         var row = v.findRowIndex(t);
41130         var cell = v.findCellIndex(t);
41131         if(row !== false && cell !== false){
41132             this.select(row, cell);
41133         }
41134     },
41135
41136     /**
41137      * Selects a cell.
41138      * @param {Number} rowIndex
41139      * @param {Number} collIndex
41140      */
41141     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
41142         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
41143             this.clearSelections();
41144             r = r || this.grid.dataSource.getAt(rowIndex);
41145             this.selection = {
41146                 record : r,
41147                 cell : [rowIndex, colIndex]
41148             };
41149             if(!preventViewNotify){
41150                 var v = this.grid.getView();
41151                 v.onCellSelect(rowIndex, colIndex);
41152                 if(preventFocus !== true){
41153                     v.focusCell(rowIndex, colIndex);
41154                 }
41155             }
41156             this.fireEvent("cellselect", this, rowIndex, colIndex);
41157             this.fireEvent("selectionchange", this, this.selection);
41158         }
41159     },
41160
41161         //private
41162     isSelectable : function(rowIndex, colIndex, cm){
41163         return !cm.isHidden(colIndex);
41164     },
41165
41166     /** @ignore */
41167     handleKeyDown : function(e){
41168         //Roo.log('Cell Sel Model handleKeyDown');
41169         if(!e.isNavKeyPress()){
41170             return;
41171         }
41172         var g = this.grid, s = this.selection;
41173         if(!s){
41174             e.stopEvent();
41175             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
41176             if(cell){
41177                 this.select(cell[0], cell[1]);
41178             }
41179             return;
41180         }
41181         var sm = this;
41182         var walk = function(row, col, step){
41183             return g.walkCells(row, col, step, sm.isSelectable,  sm);
41184         };
41185         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
41186         var newCell;
41187
41188       
41189
41190         switch(k){
41191             case e.TAB:
41192                 // handled by onEditorKey
41193                 if (g.isEditor && g.editing) {
41194                     return;
41195                 }
41196                 if(e.shiftKey) {
41197                     newCell = walk(r, c-1, -1);
41198                 } else {
41199                     newCell = walk(r, c+1, 1);
41200                 }
41201                 break;
41202             
41203             case e.DOWN:
41204                newCell = walk(r+1, c, 1);
41205                 break;
41206             
41207             case e.UP:
41208                 newCell = walk(r-1, c, -1);
41209                 break;
41210             
41211             case e.RIGHT:
41212                 newCell = walk(r, c+1, 1);
41213                 break;
41214             
41215             case e.LEFT:
41216                 newCell = walk(r, c-1, -1);
41217                 break;
41218             
41219             case e.ENTER:
41220                 
41221                 if(g.isEditor && !g.editing){
41222                    g.startEditing(r, c);
41223                    e.stopEvent();
41224                    return;
41225                 }
41226                 
41227                 
41228              break;
41229         };
41230         if(newCell){
41231             this.select(newCell[0], newCell[1]);
41232             e.stopEvent();
41233             
41234         }
41235     },
41236
41237     acceptsNav : function(row, col, cm){
41238         return !cm.isHidden(col) && cm.isCellEditable(col, row);
41239     },
41240     /**
41241      * Selects a cell.
41242      * @param {Number} field (not used) - as it's normally used as a listener
41243      * @param {Number} e - event - fake it by using
41244      *
41245      * var e = Roo.EventObjectImpl.prototype;
41246      * e.keyCode = e.TAB
41247      *
41248      * 
41249      */
41250     onEditorKey : function(field, e){
41251         
41252         var k = e.getKey(),
41253             newCell,
41254             g = this.grid,
41255             ed = g.activeEditor,
41256             forward = false;
41257         ///Roo.log('onEditorKey' + k);
41258         
41259         
41260         if (this.enter_is_tab && k == e.ENTER) {
41261             k = e.TAB;
41262         }
41263         
41264         if(k == e.TAB){
41265             if(e.shiftKey){
41266                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41267             }else{
41268                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41269                 forward = true;
41270             }
41271             
41272             e.stopEvent();
41273             
41274         } else if(k == e.ENTER &&  !e.ctrlKey){
41275             ed.completeEdit();
41276             e.stopEvent();
41277             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41278         
41279                 } else if(k == e.ESC){
41280             ed.cancelEdit();
41281         }
41282                 
41283         if (newCell) {
41284             var ecall = { cell : newCell, forward : forward };
41285             this.fireEvent('beforeeditnext', ecall );
41286             newCell = ecall.cell;
41287                         forward = ecall.forward;
41288         }
41289                 
41290         if(newCell){
41291             //Roo.log('next cell after edit');
41292             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41293         } else if (forward) {
41294             // tabbed past last
41295             this.fireEvent.defer(100, this, ['tabend',this]);
41296         }
41297     }
41298 });/*
41299  * Based on:
41300  * Ext JS Library 1.1.1
41301  * Copyright(c) 2006-2007, Ext JS, LLC.
41302  *
41303  * Originally Released Under LGPL - original licence link has changed is not relivant.
41304  *
41305  * Fork - LGPL
41306  * <script type="text/javascript">
41307  */
41308  
41309 /**
41310  * @class Roo.grid.EditorGrid
41311  * @extends Roo.grid.Grid
41312  * Class for creating and editable grid.
41313  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
41314  * The container MUST have some type of size defined for the grid to fill. The container will be 
41315  * automatically set to position relative if it isn't already.
41316  * @param {Object} dataSource The data model to bind to
41317  * @param {Object} colModel The column model with info about this grid's columns
41318  */
41319 Roo.grid.EditorGrid = function(container, config){
41320     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41321     this.getGridEl().addClass("xedit-grid");
41322
41323     if(!this.selModel){
41324         this.selModel = new Roo.grid.CellSelectionModel();
41325     }
41326
41327     this.activeEditor = null;
41328
41329         this.addEvents({
41330             /**
41331              * @event beforeedit
41332              * Fires before cell editing is triggered. The edit event object has the following properties <br />
41333              * <ul style="padding:5px;padding-left:16px;">
41334              * <li>grid - This grid</li>
41335              * <li>record - The record being edited</li>
41336              * <li>field - The field name being edited</li>
41337              * <li>value - The value for the field being edited.</li>
41338              * <li>row - The grid row index</li>
41339              * <li>column - The grid column index</li>
41340              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41341              * </ul>
41342              * @param {Object} e An edit event (see above for description)
41343              */
41344             "beforeedit" : true,
41345             /**
41346              * @event afteredit
41347              * Fires after a cell is edited. <br />
41348              * <ul style="padding:5px;padding-left:16px;">
41349              * <li>grid - This grid</li>
41350              * <li>record - The record being edited</li>
41351              * <li>field - The field name being edited</li>
41352              * <li>value - The value being set</li>
41353              * <li>originalValue - The original value for the field, before the edit.</li>
41354              * <li>row - The grid row index</li>
41355              * <li>column - The grid column index</li>
41356              * </ul>
41357              * @param {Object} e An edit event (see above for description)
41358              */
41359             "afteredit" : true,
41360             /**
41361              * @event validateedit
41362              * Fires after a cell is edited, but before the value is set in the record. 
41363          * You can use this to modify the value being set in the field, Return false
41364              * to cancel the change. The edit event object has the following properties <br />
41365              * <ul style="padding:5px;padding-left:16px;">
41366          * <li>editor - This editor</li>
41367              * <li>grid - This grid</li>
41368              * <li>record - The record being edited</li>
41369              * <li>field - The field name being edited</li>
41370              * <li>value - The value being set</li>
41371              * <li>originalValue - The original value for the field, before the edit.</li>
41372              * <li>row - The grid row index</li>
41373              * <li>column - The grid column index</li>
41374              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41375              * </ul>
41376              * @param {Object} e An edit event (see above for description)
41377              */
41378             "validateedit" : true
41379         });
41380     this.on("bodyscroll", this.stopEditing,  this);
41381     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
41382 };
41383
41384 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41385     /**
41386      * @cfg {Number} clicksToEdit
41387      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41388      */
41389     clicksToEdit: 2,
41390
41391     // private
41392     isEditor : true,
41393     // private
41394     trackMouseOver: false, // causes very odd FF errors
41395
41396     onCellDblClick : function(g, row, col){
41397         this.startEditing(row, col);
41398     },
41399
41400     onEditComplete : function(ed, value, startValue){
41401         this.editing = false;
41402         this.activeEditor = null;
41403         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41404         var r = ed.record;
41405         var field = this.colModel.getDataIndex(ed.col);
41406         var e = {
41407             grid: this,
41408             record: r,
41409             field: field,
41410             originalValue: startValue,
41411             value: value,
41412             row: ed.row,
41413             column: ed.col,
41414             cancel:false,
41415             editor: ed
41416         };
41417         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41418         cell.show();
41419           
41420         if(String(value) !== String(startValue)){
41421             
41422             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41423                 r.set(field, e.value);
41424                 // if we are dealing with a combo box..
41425                 // then we also set the 'name' colum to be the displayField
41426                 if (ed.field.displayField && ed.field.name) {
41427                     r.set(ed.field.name, ed.field.el.dom.value);
41428                 }
41429                 
41430                 delete e.cancel; //?? why!!!
41431                 this.fireEvent("afteredit", e);
41432             }
41433         } else {
41434             this.fireEvent("afteredit", e); // always fire it!
41435         }
41436         this.view.focusCell(ed.row, ed.col);
41437     },
41438
41439     /**
41440      * Starts editing the specified for the specified row/column
41441      * @param {Number} rowIndex
41442      * @param {Number} colIndex
41443      */
41444     startEditing : function(row, col){
41445         this.stopEditing();
41446         if(this.colModel.isCellEditable(col, row)){
41447             this.view.ensureVisible(row, col, true);
41448           
41449             var r = this.dataSource.getAt(row);
41450             var field = this.colModel.getDataIndex(col);
41451             var cell = Roo.get(this.view.getCell(row,col));
41452             var e = {
41453                 grid: this,
41454                 record: r,
41455                 field: field,
41456                 value: r.data[field],
41457                 row: row,
41458                 column: col,
41459                 cancel:false 
41460             };
41461             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41462                 this.editing = true;
41463                 var ed = this.colModel.getCellEditor(col, row);
41464                 
41465                 if (!ed) {
41466                     return;
41467                 }
41468                 if(!ed.rendered){
41469                     ed.render(ed.parentEl || document.body);
41470                 }
41471                 ed.field.reset();
41472                
41473                 cell.hide();
41474                 
41475                 (function(){ // complex but required for focus issues in safari, ie and opera
41476                     ed.row = row;
41477                     ed.col = col;
41478                     ed.record = r;
41479                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
41480                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
41481                     this.activeEditor = ed;
41482                     var v = r.data[field];
41483                     ed.startEdit(this.view.getCell(row, col), v);
41484                     // combo's with 'displayField and name set
41485                     if (ed.field.displayField && ed.field.name) {
41486                         ed.field.el.dom.value = r.data[ed.field.name];
41487                     }
41488                     
41489                     
41490                 }).defer(50, this);
41491             }
41492         }
41493     },
41494         
41495     /**
41496      * Stops any active editing
41497      */
41498     stopEditing : function(){
41499         if(this.activeEditor){
41500             this.activeEditor.completeEdit();
41501         }
41502         this.activeEditor = null;
41503     },
41504         
41505          /**
41506      * Called to get grid's drag proxy text, by default returns this.ddText.
41507      * @return {String}
41508      */
41509     getDragDropText : function(){
41510         var count = this.selModel.getSelectedCell() ? 1 : 0;
41511         return String.format(this.ddText, count, count == 1 ? '' : 's');
41512     }
41513         
41514 });/*
41515  * Based on:
41516  * Ext JS Library 1.1.1
41517  * Copyright(c) 2006-2007, Ext JS, LLC.
41518  *
41519  * Originally Released Under LGPL - original licence link has changed is not relivant.
41520  *
41521  * Fork - LGPL
41522  * <script type="text/javascript">
41523  */
41524
41525 // private - not really -- you end up using it !
41526 // This is a support class used internally by the Grid components
41527
41528 /**
41529  * @class Roo.grid.GridEditor
41530  * @extends Roo.Editor
41531  * Class for creating and editable grid elements.
41532  * @param {Object} config any settings (must include field)
41533  */
41534 Roo.grid.GridEditor = function(field, config){
41535     if (!config && field.field) {
41536         config = field;
41537         field = Roo.factory(config.field, Roo.form);
41538     }
41539     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41540     field.monitorTab = false;
41541 };
41542
41543 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41544     
41545     /**
41546      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41547      */
41548     
41549     alignment: "tl-tl",
41550     autoSize: "width",
41551     hideEl : false,
41552     cls: "x-small-editor x-grid-editor",
41553     shim:false,
41554     shadow:"frame"
41555 });/*
41556  * Based on:
41557  * Ext JS Library 1.1.1
41558  * Copyright(c) 2006-2007, Ext JS, LLC.
41559  *
41560  * Originally Released Under LGPL - original licence link has changed is not relivant.
41561  *
41562  * Fork - LGPL
41563  * <script type="text/javascript">
41564  */
41565   
41566
41567   
41568 Roo.grid.PropertyRecord = Roo.data.Record.create([
41569     {name:'name',type:'string'},  'value'
41570 ]);
41571
41572
41573 Roo.grid.PropertyStore = function(grid, source){
41574     this.grid = grid;
41575     this.store = new Roo.data.Store({
41576         recordType : Roo.grid.PropertyRecord
41577     });
41578     this.store.on('update', this.onUpdate,  this);
41579     if(source){
41580         this.setSource(source);
41581     }
41582     Roo.grid.PropertyStore.superclass.constructor.call(this);
41583 };
41584
41585
41586
41587 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41588     setSource : function(o){
41589         this.source = o;
41590         this.store.removeAll();
41591         var data = [];
41592         for(var k in o){
41593             if(this.isEditableValue(o[k])){
41594                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41595             }
41596         }
41597         this.store.loadRecords({records: data}, {}, true);
41598     },
41599
41600     onUpdate : function(ds, record, type){
41601         if(type == Roo.data.Record.EDIT){
41602             var v = record.data['value'];
41603             var oldValue = record.modified['value'];
41604             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41605                 this.source[record.id] = v;
41606                 record.commit();
41607                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41608             }else{
41609                 record.reject();
41610             }
41611         }
41612     },
41613
41614     getProperty : function(row){
41615        return this.store.getAt(row);
41616     },
41617
41618     isEditableValue: function(val){
41619         if(val && val instanceof Date){
41620             return true;
41621         }else if(typeof val == 'object' || typeof val == 'function'){
41622             return false;
41623         }
41624         return true;
41625     },
41626
41627     setValue : function(prop, value){
41628         this.source[prop] = value;
41629         this.store.getById(prop).set('value', value);
41630     },
41631
41632     getSource : function(){
41633         return this.source;
41634     }
41635 });
41636
41637 Roo.grid.PropertyColumnModel = function(grid, store){
41638     this.grid = grid;
41639     var g = Roo.grid;
41640     g.PropertyColumnModel.superclass.constructor.call(this, [
41641         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41642         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41643     ]);
41644     this.store = store;
41645     this.bselect = Roo.DomHelper.append(document.body, {
41646         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41647             {tag: 'option', value: 'true', html: 'true'},
41648             {tag: 'option', value: 'false', html: 'false'}
41649         ]
41650     });
41651     Roo.id(this.bselect);
41652     var f = Roo.form;
41653     this.editors = {
41654         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41655         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41656         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41657         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41658         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41659     };
41660     this.renderCellDelegate = this.renderCell.createDelegate(this);
41661     this.renderPropDelegate = this.renderProp.createDelegate(this);
41662 };
41663
41664 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41665     
41666     
41667     nameText : 'Name',
41668     valueText : 'Value',
41669     
41670     dateFormat : 'm/j/Y',
41671     
41672     
41673     renderDate : function(dateVal){
41674         return dateVal.dateFormat(this.dateFormat);
41675     },
41676
41677     renderBool : function(bVal){
41678         return bVal ? 'true' : 'false';
41679     },
41680
41681     isCellEditable : function(colIndex, rowIndex){
41682         return colIndex == 1;
41683     },
41684
41685     getRenderer : function(col){
41686         return col == 1 ?
41687             this.renderCellDelegate : this.renderPropDelegate;
41688     },
41689
41690     renderProp : function(v){
41691         return this.getPropertyName(v);
41692     },
41693
41694     renderCell : function(val){
41695         var rv = val;
41696         if(val instanceof Date){
41697             rv = this.renderDate(val);
41698         }else if(typeof val == 'boolean'){
41699             rv = this.renderBool(val);
41700         }
41701         return Roo.util.Format.htmlEncode(rv);
41702     },
41703
41704     getPropertyName : function(name){
41705         var pn = this.grid.propertyNames;
41706         return pn && pn[name] ? pn[name] : name;
41707     },
41708
41709     getCellEditor : function(colIndex, rowIndex){
41710         var p = this.store.getProperty(rowIndex);
41711         var n = p.data['name'], val = p.data['value'];
41712         
41713         if(typeof(this.grid.customEditors[n]) == 'string'){
41714             return this.editors[this.grid.customEditors[n]];
41715         }
41716         if(typeof(this.grid.customEditors[n]) != 'undefined'){
41717             return this.grid.customEditors[n];
41718         }
41719         if(val instanceof Date){
41720             return this.editors['date'];
41721         }else if(typeof val == 'number'){
41722             return this.editors['number'];
41723         }else if(typeof val == 'boolean'){
41724             return this.editors['boolean'];
41725         }else{
41726             return this.editors['string'];
41727         }
41728     }
41729 });
41730
41731 /**
41732  * @class Roo.grid.PropertyGrid
41733  * @extends Roo.grid.EditorGrid
41734  * This class represents the  interface of a component based property grid control.
41735  * <br><br>Usage:<pre><code>
41736  var grid = new Roo.grid.PropertyGrid("my-container-id", {
41737       
41738  });
41739  // set any options
41740  grid.render();
41741  * </code></pre>
41742   
41743  * @constructor
41744  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41745  * The container MUST have some type of size defined for the grid to fill. The container will be
41746  * automatically set to position relative if it isn't already.
41747  * @param {Object} config A config object that sets properties on this grid.
41748  */
41749 Roo.grid.PropertyGrid = function(container, config){
41750     config = config || {};
41751     var store = new Roo.grid.PropertyStore(this);
41752     this.store = store;
41753     var cm = new Roo.grid.PropertyColumnModel(this, store);
41754     store.store.sort('name', 'ASC');
41755     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41756         ds: store.store,
41757         cm: cm,
41758         enableColLock:false,
41759         enableColumnMove:false,
41760         stripeRows:false,
41761         trackMouseOver: false,
41762         clicksToEdit:1
41763     }, config));
41764     this.getGridEl().addClass('x-props-grid');
41765     this.lastEditRow = null;
41766     this.on('columnresize', this.onColumnResize, this);
41767     this.addEvents({
41768          /**
41769              * @event beforepropertychange
41770              * Fires before a property changes (return false to stop?)
41771              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41772              * @param {String} id Record Id
41773              * @param {String} newval New Value
41774          * @param {String} oldval Old Value
41775              */
41776         "beforepropertychange": true,
41777         /**
41778              * @event propertychange
41779              * Fires after a property changes
41780              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41781              * @param {String} id Record Id
41782              * @param {String} newval New Value
41783          * @param {String} oldval Old Value
41784              */
41785         "propertychange": true
41786     });
41787     this.customEditors = this.customEditors || {};
41788 };
41789 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41790     
41791      /**
41792      * @cfg {Object} customEditors map of colnames=> custom editors.
41793      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41794      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41795      * false disables editing of the field.
41796          */
41797     
41798       /**
41799      * @cfg {Object} propertyNames map of property Names to their displayed value
41800          */
41801     
41802     render : function(){
41803         Roo.grid.PropertyGrid.superclass.render.call(this);
41804         this.autoSize.defer(100, this);
41805     },
41806
41807     autoSize : function(){
41808         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41809         if(this.view){
41810             this.view.fitColumns();
41811         }
41812     },
41813
41814     onColumnResize : function(){
41815         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41816         this.autoSize();
41817     },
41818     /**
41819      * Sets the data for the Grid
41820      * accepts a Key => Value object of all the elements avaiable.
41821      * @param {Object} data  to appear in grid.
41822      */
41823     setSource : function(source){
41824         this.store.setSource(source);
41825         //this.autoSize();
41826     },
41827     /**
41828      * Gets all the data from the grid.
41829      * @return {Object} data  data stored in grid
41830      */
41831     getSource : function(){
41832         return this.store.getSource();
41833     }
41834 });/*
41835   
41836  * Licence LGPL
41837  
41838  */
41839  
41840 /**
41841  * @class Roo.grid.Calendar
41842  * @extends Roo.grid.Grid
41843  * This class extends the Grid to provide a calendar widget
41844  * <br><br>Usage:<pre><code>
41845  var grid = new Roo.grid.Calendar("my-container-id", {
41846      ds: myDataStore,
41847      cm: myColModel,
41848      selModel: mySelectionModel,
41849      autoSizeColumns: true,
41850      monitorWindowResize: false,
41851      trackMouseOver: true
41852      eventstore : real data store..
41853  });
41854  // set any options
41855  grid.render();
41856   
41857   * @constructor
41858  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41859  * The container MUST have some type of size defined for the grid to fill. The container will be
41860  * automatically set to position relative if it isn't already.
41861  * @param {Object} config A config object that sets properties on this grid.
41862  */
41863 Roo.grid.Calendar = function(container, config){
41864         // initialize the container
41865         this.container = Roo.get(container);
41866         this.container.update("");
41867         this.container.setStyle("overflow", "hidden");
41868     this.container.addClass('x-grid-container');
41869
41870     this.id = this.container.id;
41871
41872     Roo.apply(this, config);
41873     // check and correct shorthanded configs
41874     
41875     var rows = [];
41876     var d =1;
41877     for (var r = 0;r < 6;r++) {
41878         
41879         rows[r]=[];
41880         for (var c =0;c < 7;c++) {
41881             rows[r][c]= '';
41882         }
41883     }
41884     if (this.eventStore) {
41885         this.eventStore= Roo.factory(this.eventStore, Roo.data);
41886         this.eventStore.on('load',this.onLoad, this);
41887         this.eventStore.on('beforeload',this.clearEvents, this);
41888          
41889     }
41890     
41891     this.dataSource = new Roo.data.Store({
41892             proxy: new Roo.data.MemoryProxy(rows),
41893             reader: new Roo.data.ArrayReader({}, [
41894                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41895     });
41896
41897     this.dataSource.load();
41898     this.ds = this.dataSource;
41899     this.ds.xmodule = this.xmodule || false;
41900     
41901     
41902     var cellRender = function(v,x,r)
41903     {
41904         return String.format(
41905             '<div class="fc-day  fc-widget-content"><div>' +
41906                 '<div class="fc-event-container"></div>' +
41907                 '<div class="fc-day-number">{0}</div>'+
41908                 
41909                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41910             '</div></div>', v);
41911     
41912     }
41913     
41914     
41915     this.colModel = new Roo.grid.ColumnModel( [
41916         {
41917             xtype: 'ColumnModel',
41918             xns: Roo.grid,
41919             dataIndex : 'weekday0',
41920             header : 'Sunday',
41921             renderer : cellRender
41922         },
41923         {
41924             xtype: 'ColumnModel',
41925             xns: Roo.grid,
41926             dataIndex : 'weekday1',
41927             header : 'Monday',
41928             renderer : cellRender
41929         },
41930         {
41931             xtype: 'ColumnModel',
41932             xns: Roo.grid,
41933             dataIndex : 'weekday2',
41934             header : 'Tuesday',
41935             renderer : cellRender
41936         },
41937         {
41938             xtype: 'ColumnModel',
41939             xns: Roo.grid,
41940             dataIndex : 'weekday3',
41941             header : 'Wednesday',
41942             renderer : cellRender
41943         },
41944         {
41945             xtype: 'ColumnModel',
41946             xns: Roo.grid,
41947             dataIndex : 'weekday4',
41948             header : 'Thursday',
41949             renderer : cellRender
41950         },
41951         {
41952             xtype: 'ColumnModel',
41953             xns: Roo.grid,
41954             dataIndex : 'weekday5',
41955             header : 'Friday',
41956             renderer : cellRender
41957         },
41958         {
41959             xtype: 'ColumnModel',
41960             xns: Roo.grid,
41961             dataIndex : 'weekday6',
41962             header : 'Saturday',
41963             renderer : cellRender
41964         }
41965     ]);
41966     this.cm = this.colModel;
41967     this.cm.xmodule = this.xmodule || false;
41968  
41969         
41970           
41971     //this.selModel = new Roo.grid.CellSelectionModel();
41972     //this.sm = this.selModel;
41973     //this.selModel.init(this);
41974     
41975     
41976     if(this.width){
41977         this.container.setWidth(this.width);
41978     }
41979
41980     if(this.height){
41981         this.container.setHeight(this.height);
41982     }
41983     /** @private */
41984         this.addEvents({
41985         // raw events
41986         /**
41987          * @event click
41988          * The raw click event for the entire grid.
41989          * @param {Roo.EventObject} e
41990          */
41991         "click" : true,
41992         /**
41993          * @event dblclick
41994          * The raw dblclick event for the entire grid.
41995          * @param {Roo.EventObject} e
41996          */
41997         "dblclick" : true,
41998         /**
41999          * @event contextmenu
42000          * The raw contextmenu event for the entire grid.
42001          * @param {Roo.EventObject} e
42002          */
42003         "contextmenu" : true,
42004         /**
42005          * @event mousedown
42006          * The raw mousedown event for the entire grid.
42007          * @param {Roo.EventObject} e
42008          */
42009         "mousedown" : true,
42010         /**
42011          * @event mouseup
42012          * The raw mouseup event for the entire grid.
42013          * @param {Roo.EventObject} e
42014          */
42015         "mouseup" : true,
42016         /**
42017          * @event mouseover
42018          * The raw mouseover event for the entire grid.
42019          * @param {Roo.EventObject} e
42020          */
42021         "mouseover" : true,
42022         /**
42023          * @event mouseout
42024          * The raw mouseout event for the entire grid.
42025          * @param {Roo.EventObject} e
42026          */
42027         "mouseout" : true,
42028         /**
42029          * @event keypress
42030          * The raw keypress event for the entire grid.
42031          * @param {Roo.EventObject} e
42032          */
42033         "keypress" : true,
42034         /**
42035          * @event keydown
42036          * The raw keydown event for the entire grid.
42037          * @param {Roo.EventObject} e
42038          */
42039         "keydown" : true,
42040
42041         // custom events
42042
42043         /**
42044          * @event cellclick
42045          * Fires when a cell is clicked
42046          * @param {Grid} this
42047          * @param {Number} rowIndex
42048          * @param {Number} columnIndex
42049          * @param {Roo.EventObject} e
42050          */
42051         "cellclick" : true,
42052         /**
42053          * @event celldblclick
42054          * Fires when a cell is double clicked
42055          * @param {Grid} this
42056          * @param {Number} rowIndex
42057          * @param {Number} columnIndex
42058          * @param {Roo.EventObject} e
42059          */
42060         "celldblclick" : true,
42061         /**
42062          * @event rowclick
42063          * Fires when a row is clicked
42064          * @param {Grid} this
42065          * @param {Number} rowIndex
42066          * @param {Roo.EventObject} e
42067          */
42068         "rowclick" : true,
42069         /**
42070          * @event rowdblclick
42071          * Fires when a row is double clicked
42072          * @param {Grid} this
42073          * @param {Number} rowIndex
42074          * @param {Roo.EventObject} e
42075          */
42076         "rowdblclick" : true,
42077         /**
42078          * @event headerclick
42079          * Fires when a header is clicked
42080          * @param {Grid} this
42081          * @param {Number} columnIndex
42082          * @param {Roo.EventObject} e
42083          */
42084         "headerclick" : true,
42085         /**
42086          * @event headerdblclick
42087          * Fires when a header cell is double clicked
42088          * @param {Grid} this
42089          * @param {Number} columnIndex
42090          * @param {Roo.EventObject} e
42091          */
42092         "headerdblclick" : true,
42093         /**
42094          * @event rowcontextmenu
42095          * Fires when a row is right clicked
42096          * @param {Grid} this
42097          * @param {Number} rowIndex
42098          * @param {Roo.EventObject} e
42099          */
42100         "rowcontextmenu" : true,
42101         /**
42102          * @event cellcontextmenu
42103          * Fires when a cell is right clicked
42104          * @param {Grid} this
42105          * @param {Number} rowIndex
42106          * @param {Number} cellIndex
42107          * @param {Roo.EventObject} e
42108          */
42109          "cellcontextmenu" : true,
42110         /**
42111          * @event headercontextmenu
42112          * Fires when a header is right clicked
42113          * @param {Grid} this
42114          * @param {Number} columnIndex
42115          * @param {Roo.EventObject} e
42116          */
42117         "headercontextmenu" : true,
42118         /**
42119          * @event bodyscroll
42120          * Fires when the body element is scrolled
42121          * @param {Number} scrollLeft
42122          * @param {Number} scrollTop
42123          */
42124         "bodyscroll" : true,
42125         /**
42126          * @event columnresize
42127          * Fires when the user resizes a column
42128          * @param {Number} columnIndex
42129          * @param {Number} newSize
42130          */
42131         "columnresize" : true,
42132         /**
42133          * @event columnmove
42134          * Fires when the user moves a column
42135          * @param {Number} oldIndex
42136          * @param {Number} newIndex
42137          */
42138         "columnmove" : true,
42139         /**
42140          * @event startdrag
42141          * Fires when row(s) start being dragged
42142          * @param {Grid} this
42143          * @param {Roo.GridDD} dd The drag drop object
42144          * @param {event} e The raw browser event
42145          */
42146         "startdrag" : true,
42147         /**
42148          * @event enddrag
42149          * Fires when a drag operation is complete
42150          * @param {Grid} this
42151          * @param {Roo.GridDD} dd The drag drop object
42152          * @param {event} e The raw browser event
42153          */
42154         "enddrag" : true,
42155         /**
42156          * @event dragdrop
42157          * Fires when dragged row(s) are dropped on a valid DD target
42158          * @param {Grid} this
42159          * @param {Roo.GridDD} dd The drag drop object
42160          * @param {String} targetId The target drag drop object
42161          * @param {event} e The raw browser event
42162          */
42163         "dragdrop" : true,
42164         /**
42165          * @event dragover
42166          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
42167          * @param {Grid} this
42168          * @param {Roo.GridDD} dd The drag drop object
42169          * @param {String} targetId The target drag drop object
42170          * @param {event} e The raw browser event
42171          */
42172         "dragover" : true,
42173         /**
42174          * @event dragenter
42175          *  Fires when the dragged row(s) first cross another DD target while being dragged
42176          * @param {Grid} this
42177          * @param {Roo.GridDD} dd The drag drop object
42178          * @param {String} targetId The target drag drop object
42179          * @param {event} e The raw browser event
42180          */
42181         "dragenter" : true,
42182         /**
42183          * @event dragout
42184          * Fires when the dragged row(s) leave another DD target while being dragged
42185          * @param {Grid} this
42186          * @param {Roo.GridDD} dd The drag drop object
42187          * @param {String} targetId The target drag drop object
42188          * @param {event} e The raw browser event
42189          */
42190         "dragout" : true,
42191         /**
42192          * @event rowclass
42193          * Fires when a row is rendered, so you can change add a style to it.
42194          * @param {GridView} gridview   The grid view
42195          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
42196          */
42197         'rowclass' : true,
42198
42199         /**
42200          * @event render
42201          * Fires when the grid is rendered
42202          * @param {Grid} grid
42203          */
42204         'render' : true,
42205             /**
42206              * @event select
42207              * Fires when a date is selected
42208              * @param {DatePicker} this
42209              * @param {Date} date The selected date
42210              */
42211         'select': true,
42212         /**
42213              * @event monthchange
42214              * Fires when the displayed month changes 
42215              * @param {DatePicker} this
42216              * @param {Date} date The selected month
42217              */
42218         'monthchange': true,
42219         /**
42220              * @event evententer
42221              * Fires when mouse over an event
42222              * @param {Calendar} this
42223              * @param {event} Event
42224              */
42225         'evententer': true,
42226         /**
42227              * @event eventleave
42228              * Fires when the mouse leaves an
42229              * @param {Calendar} this
42230              * @param {event}
42231              */
42232         'eventleave': true,
42233         /**
42234              * @event eventclick
42235              * Fires when the mouse click an
42236              * @param {Calendar} this
42237              * @param {event}
42238              */
42239         'eventclick': true,
42240         /**
42241              * @event eventrender
42242              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42243              * @param {Calendar} this
42244              * @param {data} data to be modified
42245              */
42246         'eventrender': true
42247         
42248     });
42249
42250     Roo.grid.Grid.superclass.constructor.call(this);
42251     this.on('render', function() {
42252         this.view.el.addClass('x-grid-cal'); 
42253         
42254         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42255
42256     },this);
42257     
42258     if (!Roo.grid.Calendar.style) {
42259         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42260             
42261             
42262             '.x-grid-cal .x-grid-col' :  {
42263                 height: 'auto !important',
42264                 'vertical-align': 'top'
42265             },
42266             '.x-grid-cal  .fc-event-hori' : {
42267                 height: '14px'
42268             }
42269              
42270             
42271         }, Roo.id());
42272     }
42273
42274     
42275     
42276 };
42277 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42278     /**
42279      * @cfg {Store} eventStore The store that loads events.
42280      */
42281     eventStore : 25,
42282
42283      
42284     activeDate : false,
42285     startDay : 0,
42286     autoWidth : true,
42287     monitorWindowResize : false,
42288
42289     
42290     resizeColumns : function() {
42291         var col = (this.view.el.getWidth() / 7) - 3;
42292         // loop through cols, and setWidth
42293         for(var i =0 ; i < 7 ; i++){
42294             this.cm.setColumnWidth(i, col);
42295         }
42296     },
42297      setDate :function(date) {
42298         
42299         Roo.log('setDate?');
42300         
42301         this.resizeColumns();
42302         var vd = this.activeDate;
42303         this.activeDate = date;
42304 //        if(vd && this.el){
42305 //            var t = date.getTime();
42306 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42307 //                Roo.log('using add remove');
42308 //                
42309 //                this.fireEvent('monthchange', this, date);
42310 //                
42311 //                this.cells.removeClass("fc-state-highlight");
42312 //                this.cells.each(function(c){
42313 //                   if(c.dateValue == t){
42314 //                       c.addClass("fc-state-highlight");
42315 //                       setTimeout(function(){
42316 //                            try{c.dom.firstChild.focus();}catch(e){}
42317 //                       }, 50);
42318 //                       return false;
42319 //                   }
42320 //                   return true;
42321 //                });
42322 //                return;
42323 //            }
42324 //        }
42325         
42326         var days = date.getDaysInMonth();
42327         
42328         var firstOfMonth = date.getFirstDateOfMonth();
42329         var startingPos = firstOfMonth.getDay()-this.startDay;
42330         
42331         if(startingPos < this.startDay){
42332             startingPos += 7;
42333         }
42334         
42335         var pm = date.add(Date.MONTH, -1);
42336         var prevStart = pm.getDaysInMonth()-startingPos;
42337 //        
42338         
42339         
42340         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42341         
42342         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42343         //this.cells.addClassOnOver('fc-state-hover');
42344         
42345         var cells = this.cells.elements;
42346         var textEls = this.textNodes;
42347         
42348         //Roo.each(cells, function(cell){
42349         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42350         //});
42351         
42352         days += startingPos;
42353
42354         // convert everything to numbers so it's fast
42355         var day = 86400000;
42356         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42357         //Roo.log(d);
42358         //Roo.log(pm);
42359         //Roo.log(prevStart);
42360         
42361         var today = new Date().clearTime().getTime();
42362         var sel = date.clearTime().getTime();
42363         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42364         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42365         var ddMatch = this.disabledDatesRE;
42366         var ddText = this.disabledDatesText;
42367         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42368         var ddaysText = this.disabledDaysText;
42369         var format = this.format;
42370         
42371         var setCellClass = function(cal, cell){
42372             
42373             //Roo.log('set Cell Class');
42374             cell.title = "";
42375             var t = d.getTime();
42376             
42377             //Roo.log(d);
42378             
42379             
42380             cell.dateValue = t;
42381             if(t == today){
42382                 cell.className += " fc-today";
42383                 cell.className += " fc-state-highlight";
42384                 cell.title = cal.todayText;
42385             }
42386             if(t == sel){
42387                 // disable highlight in other month..
42388                 cell.className += " fc-state-highlight";
42389                 
42390             }
42391             // disabling
42392             if(t < min) {
42393                 //cell.className = " fc-state-disabled";
42394                 cell.title = cal.minText;
42395                 return;
42396             }
42397             if(t > max) {
42398                 //cell.className = " fc-state-disabled";
42399                 cell.title = cal.maxText;
42400                 return;
42401             }
42402             if(ddays){
42403                 if(ddays.indexOf(d.getDay()) != -1){
42404                     // cell.title = ddaysText;
42405                    // cell.className = " fc-state-disabled";
42406                 }
42407             }
42408             if(ddMatch && format){
42409                 var fvalue = d.dateFormat(format);
42410                 if(ddMatch.test(fvalue)){
42411                     cell.title = ddText.replace("%0", fvalue);
42412                    cell.className = " fc-state-disabled";
42413                 }
42414             }
42415             
42416             if (!cell.initialClassName) {
42417                 cell.initialClassName = cell.dom.className;
42418             }
42419             
42420             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
42421         };
42422
42423         var i = 0;
42424         
42425         for(; i < startingPos; i++) {
42426             cells[i].dayName =  (++prevStart);
42427             Roo.log(textEls[i]);
42428             d.setDate(d.getDate()+1);
42429             
42430             //cells[i].className = "fc-past fc-other-month";
42431             setCellClass(this, cells[i]);
42432         }
42433         
42434         var intDay = 0;
42435         
42436         for(; i < days; i++){
42437             intDay = i - startingPos + 1;
42438             cells[i].dayName =  (intDay);
42439             d.setDate(d.getDate()+1);
42440             
42441             cells[i].className = ''; // "x-date-active";
42442             setCellClass(this, cells[i]);
42443         }
42444         var extraDays = 0;
42445         
42446         for(; i < 42; i++) {
42447             //textEls[i].innerHTML = (++extraDays);
42448             
42449             d.setDate(d.getDate()+1);
42450             cells[i].dayName = (++extraDays);
42451             cells[i].className = "fc-future fc-other-month";
42452             setCellClass(this, cells[i]);
42453         }
42454         
42455         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42456         
42457         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42458         
42459         // this will cause all the cells to mis
42460         var rows= [];
42461         var i =0;
42462         for (var r = 0;r < 6;r++) {
42463             for (var c =0;c < 7;c++) {
42464                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42465             }    
42466         }
42467         
42468         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42469         for(i=0;i<cells.length;i++) {
42470             
42471             this.cells.elements[i].dayName = cells[i].dayName ;
42472             this.cells.elements[i].className = cells[i].className;
42473             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42474             this.cells.elements[i].title = cells[i].title ;
42475             this.cells.elements[i].dateValue = cells[i].dateValue ;
42476         }
42477         
42478         
42479         
42480         
42481         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42482         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42483         
42484         ////if(totalRows != 6){
42485             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42486            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42487        // }
42488         
42489         this.fireEvent('monthchange', this, date);
42490         
42491         
42492     },
42493  /**
42494      * Returns the grid's SelectionModel.
42495      * @return {SelectionModel}
42496      */
42497     getSelectionModel : function(){
42498         if(!this.selModel){
42499             this.selModel = new Roo.grid.CellSelectionModel();
42500         }
42501         return this.selModel;
42502     },
42503
42504     load: function() {
42505         this.eventStore.load()
42506         
42507         
42508         
42509     },
42510     
42511     findCell : function(dt) {
42512         dt = dt.clearTime().getTime();
42513         var ret = false;
42514         this.cells.each(function(c){
42515             //Roo.log("check " +c.dateValue + '?=' + dt);
42516             if(c.dateValue == dt){
42517                 ret = c;
42518                 return false;
42519             }
42520             return true;
42521         });
42522         
42523         return ret;
42524     },
42525     
42526     findCells : function(rec) {
42527         var s = rec.data.start_dt.clone().clearTime().getTime();
42528        // Roo.log(s);
42529         var e= rec.data.end_dt.clone().clearTime().getTime();
42530        // Roo.log(e);
42531         var ret = [];
42532         this.cells.each(function(c){
42533              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42534             
42535             if(c.dateValue > e){
42536                 return ;
42537             }
42538             if(c.dateValue < s){
42539                 return ;
42540             }
42541             ret.push(c);
42542         });
42543         
42544         return ret;    
42545     },
42546     
42547     findBestRow: function(cells)
42548     {
42549         var ret = 0;
42550         
42551         for (var i =0 ; i < cells.length;i++) {
42552             ret  = Math.max(cells[i].rows || 0,ret);
42553         }
42554         return ret;
42555         
42556     },
42557     
42558     
42559     addItem : function(rec)
42560     {
42561         // look for vertical location slot in
42562         var cells = this.findCells(rec);
42563         
42564         rec.row = this.findBestRow(cells);
42565         
42566         // work out the location.
42567         
42568         var crow = false;
42569         var rows = [];
42570         for(var i =0; i < cells.length; i++) {
42571             if (!crow) {
42572                 crow = {
42573                     start : cells[i],
42574                     end :  cells[i]
42575                 };
42576                 continue;
42577             }
42578             if (crow.start.getY() == cells[i].getY()) {
42579                 // on same row.
42580                 crow.end = cells[i];
42581                 continue;
42582             }
42583             // different row.
42584             rows.push(crow);
42585             crow = {
42586                 start: cells[i],
42587                 end : cells[i]
42588             };
42589             
42590         }
42591         
42592         rows.push(crow);
42593         rec.els = [];
42594         rec.rows = rows;
42595         rec.cells = cells;
42596         for (var i = 0; i < cells.length;i++) {
42597             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42598             
42599         }
42600         
42601         
42602     },
42603     
42604     clearEvents: function() {
42605         
42606         if (!this.eventStore.getCount()) {
42607             return;
42608         }
42609         // reset number of rows in cells.
42610         Roo.each(this.cells.elements, function(c){
42611             c.rows = 0;
42612         });
42613         
42614         this.eventStore.each(function(e) {
42615             this.clearEvent(e);
42616         },this);
42617         
42618     },
42619     
42620     clearEvent : function(ev)
42621     {
42622         if (ev.els) {
42623             Roo.each(ev.els, function(el) {
42624                 el.un('mouseenter' ,this.onEventEnter, this);
42625                 el.un('mouseleave' ,this.onEventLeave, this);
42626                 el.remove();
42627             },this);
42628             ev.els = [];
42629         }
42630     },
42631     
42632     
42633     renderEvent : function(ev,ctr) {
42634         if (!ctr) {
42635              ctr = this.view.el.select('.fc-event-container',true).first();
42636         }
42637         
42638          
42639         this.clearEvent(ev);
42640             //code
42641        
42642         
42643         
42644         ev.els = [];
42645         var cells = ev.cells;
42646         var rows = ev.rows;
42647         this.fireEvent('eventrender', this, ev);
42648         
42649         for(var i =0; i < rows.length; i++) {
42650             
42651             cls = '';
42652             if (i == 0) {
42653                 cls += ' fc-event-start';
42654             }
42655             if ((i+1) == rows.length) {
42656                 cls += ' fc-event-end';
42657             }
42658             
42659             //Roo.log(ev.data);
42660             // how many rows should it span..
42661             var cg = this.eventTmpl.append(ctr,Roo.apply({
42662                 fccls : cls
42663                 
42664             }, ev.data) , true);
42665             
42666             
42667             cg.on('mouseenter' ,this.onEventEnter, this, ev);
42668             cg.on('mouseleave' ,this.onEventLeave, this, ev);
42669             cg.on('click', this.onEventClick, this, ev);
42670             
42671             ev.els.push(cg);
42672             
42673             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42674             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42675             //Roo.log(cg);
42676              
42677             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
42678             cg.setWidth(ebox.right - sbox.x -2);
42679         }
42680     },
42681     
42682     renderEvents: function()
42683     {   
42684         // first make sure there is enough space..
42685         
42686         if (!this.eventTmpl) {
42687             this.eventTmpl = new Roo.Template(
42688                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
42689                     '<div class="fc-event-inner">' +
42690                         '<span class="fc-event-time">{time}</span>' +
42691                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42692                     '</div>' +
42693                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
42694                 '</div>'
42695             );
42696                 
42697         }
42698                
42699         
42700         
42701         this.cells.each(function(c) {
42702             //Roo.log(c.select('.fc-day-content div',true).first());
42703             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42704         });
42705         
42706         var ctr = this.view.el.select('.fc-event-container',true).first();
42707         
42708         var cls;
42709         this.eventStore.each(function(ev){
42710             
42711             this.renderEvent(ev);
42712              
42713              
42714         }, this);
42715         this.view.layout();
42716         
42717     },
42718     
42719     onEventEnter: function (e, el,event,d) {
42720         this.fireEvent('evententer', this, el, event);
42721     },
42722     
42723     onEventLeave: function (e, el,event,d) {
42724         this.fireEvent('eventleave', this, el, event);
42725     },
42726     
42727     onEventClick: function (e, el,event,d) {
42728         this.fireEvent('eventclick', this, el, event);
42729     },
42730     
42731     onMonthChange: function () {
42732         this.store.load();
42733     },
42734     
42735     onLoad: function () {
42736         
42737         //Roo.log('calendar onload');
42738 //         
42739         if(this.eventStore.getCount() > 0){
42740             
42741            
42742             
42743             this.eventStore.each(function(d){
42744                 
42745                 
42746                 // FIXME..
42747                 var add =   d.data;
42748                 if (typeof(add.end_dt) == 'undefined')  {
42749                     Roo.log("Missing End time in calendar data: ");
42750                     Roo.log(d);
42751                     return;
42752                 }
42753                 if (typeof(add.start_dt) == 'undefined')  {
42754                     Roo.log("Missing Start time in calendar data: ");
42755                     Roo.log(d);
42756                     return;
42757                 }
42758                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42759                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42760                 add.id = add.id || d.id;
42761                 add.title = add.title || '??';
42762                 
42763                 this.addItem(d);
42764                 
42765              
42766             },this);
42767         }
42768         
42769         this.renderEvents();
42770     }
42771     
42772
42773 });
42774 /*
42775  grid : {
42776                 xtype: 'Grid',
42777                 xns: Roo.grid,
42778                 listeners : {
42779                     render : function ()
42780                     {
42781                         _this.grid = this;
42782                         
42783                         if (!this.view.el.hasClass('course-timesheet')) {
42784                             this.view.el.addClass('course-timesheet');
42785                         }
42786                         if (this.tsStyle) {
42787                             this.ds.load({});
42788                             return; 
42789                         }
42790                         Roo.log('width');
42791                         Roo.log(_this.grid.view.el.getWidth());
42792                         
42793                         
42794                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
42795                             '.course-timesheet .x-grid-row' : {
42796                                 height: '80px'
42797                             },
42798                             '.x-grid-row td' : {
42799                                 'vertical-align' : 0
42800                             },
42801                             '.course-edit-link' : {
42802                                 'color' : 'blue',
42803                                 'text-overflow' : 'ellipsis',
42804                                 'overflow' : 'hidden',
42805                                 'white-space' : 'nowrap',
42806                                 'cursor' : 'pointer'
42807                             },
42808                             '.sub-link' : {
42809                                 'color' : 'green'
42810                             },
42811                             '.de-act-sup-link' : {
42812                                 'color' : 'purple',
42813                                 'text-decoration' : 'line-through'
42814                             },
42815                             '.de-act-link' : {
42816                                 'color' : 'red',
42817                                 'text-decoration' : 'line-through'
42818                             },
42819                             '.course-timesheet .course-highlight' : {
42820                                 'border-top-style': 'dashed !important',
42821                                 'border-bottom-bottom': 'dashed !important'
42822                             },
42823                             '.course-timesheet .course-item' : {
42824                                 'font-family'   : 'tahoma, arial, helvetica',
42825                                 'font-size'     : '11px',
42826                                 'overflow'      : 'hidden',
42827                                 'padding-left'  : '10px',
42828                                 'padding-right' : '10px',
42829                                 'padding-top' : '10px' 
42830                             }
42831                             
42832                         }, Roo.id());
42833                                 this.ds.load({});
42834                     }
42835                 },
42836                 autoWidth : true,
42837                 monitorWindowResize : false,
42838                 cellrenderer : function(v,x,r)
42839                 {
42840                     return v;
42841                 },
42842                 sm : {
42843                     xtype: 'CellSelectionModel',
42844                     xns: Roo.grid
42845                 },
42846                 dataSource : {
42847                     xtype: 'Store',
42848                     xns: Roo.data,
42849                     listeners : {
42850                         beforeload : function (_self, options)
42851                         {
42852                             options.params = options.params || {};
42853                             options.params._month = _this.monthField.getValue();
42854                             options.params.limit = 9999;
42855                             options.params['sort'] = 'when_dt';    
42856                             options.params['dir'] = 'ASC';    
42857                             this.proxy.loadResponse = this.loadResponse;
42858                             Roo.log("load?");
42859                             //this.addColumns();
42860                         },
42861                         load : function (_self, records, options)
42862                         {
42863                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42864                                 // if you click on the translation.. you can edit it...
42865                                 var el = Roo.get(this);
42866                                 var id = el.dom.getAttribute('data-id');
42867                                 var d = el.dom.getAttribute('data-date');
42868                                 var t = el.dom.getAttribute('data-time');
42869                                 //var id = this.child('span').dom.textContent;
42870                                 
42871                                 //Roo.log(this);
42872                                 Pman.Dialog.CourseCalendar.show({
42873                                     id : id,
42874                                     when_d : d,
42875                                     when_t : t,
42876                                     productitem_active : id ? 1 : 0
42877                                 }, function() {
42878                                     _this.grid.ds.load({});
42879                                 });
42880                            
42881                            });
42882                            
42883                            _this.panel.fireEvent('resize', [ '', '' ]);
42884                         }
42885                     },
42886                     loadResponse : function(o, success, response){
42887                             // this is overridden on before load..
42888                             
42889                             Roo.log("our code?");       
42890                             //Roo.log(success);
42891                             //Roo.log(response)
42892                             delete this.activeRequest;
42893                             if(!success){
42894                                 this.fireEvent("loadexception", this, o, response);
42895                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42896                                 return;
42897                             }
42898                             var result;
42899                             try {
42900                                 result = o.reader.read(response);
42901                             }catch(e){
42902                                 Roo.log("load exception?");
42903                                 this.fireEvent("loadexception", this, o, response, e);
42904                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42905                                 return;
42906                             }
42907                             Roo.log("ready...");        
42908                             // loop through result.records;
42909                             // and set this.tdate[date] = [] << array of records..
42910                             _this.tdata  = {};
42911                             Roo.each(result.records, function(r){
42912                                 //Roo.log(r.data);
42913                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42914                                     _this.tdata[r.data.when_dt.format('j')] = [];
42915                                 }
42916                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42917                             });
42918                             
42919                             //Roo.log(_this.tdata);
42920                             
42921                             result.records = [];
42922                             result.totalRecords = 6;
42923                     
42924                             // let's generate some duumy records for the rows.
42925                             //var st = _this.dateField.getValue();
42926                             
42927                             // work out monday..
42928                             //st = st.add(Date.DAY, -1 * st.format('w'));
42929                             
42930                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42931                             
42932                             var firstOfMonth = date.getFirstDayOfMonth();
42933                             var days = date.getDaysInMonth();
42934                             var d = 1;
42935                             var firstAdded = false;
42936                             for (var i = 0; i < result.totalRecords ; i++) {
42937                                 //var d= st.add(Date.DAY, i);
42938                                 var row = {};
42939                                 var added = 0;
42940                                 for(var w = 0 ; w < 7 ; w++){
42941                                     if(!firstAdded && firstOfMonth != w){
42942                                         continue;
42943                                     }
42944                                     if(d > days){
42945                                         continue;
42946                                     }
42947                                     firstAdded = true;
42948                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
42949                                     row['weekday'+w] = String.format(
42950                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
42951                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42952                                                     d,
42953                                                     date.format('Y-m-')+dd
42954                                                 );
42955                                     added++;
42956                                     if(typeof(_this.tdata[d]) != 'undefined'){
42957                                         Roo.each(_this.tdata[d], function(r){
42958                                             var is_sub = '';
42959                                             var deactive = '';
42960                                             var id = r.id;
42961                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42962                                             if(r.parent_id*1>0){
42963                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42964                                                 id = r.parent_id;
42965                                             }
42966                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42967                                                 deactive = 'de-act-link';
42968                                             }
42969                                             
42970                                             row['weekday'+w] += String.format(
42971                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42972                                                     id, //0
42973                                                     r.product_id_name, //1
42974                                                     r.when_dt.format('h:ia'), //2
42975                                                     is_sub, //3
42976                                                     deactive, //4
42977                                                     desc // 5
42978                                             );
42979                                         });
42980                                     }
42981                                     d++;
42982                                 }
42983                                 
42984                                 // only do this if something added..
42985                                 if(added > 0){ 
42986                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42987                                 }
42988                                 
42989                                 
42990                                 // push it twice. (second one with an hour..
42991                                 
42992                             }
42993                             //Roo.log(result);
42994                             this.fireEvent("load", this, o, o.request.arg);
42995                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42996                         },
42997                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42998                     proxy : {
42999                         xtype: 'HttpProxy',
43000                         xns: Roo.data,
43001                         method : 'GET',
43002                         url : baseURL + '/Roo/Shop_course.php'
43003                     },
43004                     reader : {
43005                         xtype: 'JsonReader',
43006                         xns: Roo.data,
43007                         id : 'id',
43008                         fields : [
43009                             {
43010                                 'name': 'id',
43011                                 'type': 'int'
43012                             },
43013                             {
43014                                 'name': 'when_dt',
43015                                 'type': 'string'
43016                             },
43017                             {
43018                                 'name': 'end_dt',
43019                                 'type': 'string'
43020                             },
43021                             {
43022                                 'name': 'parent_id',
43023                                 'type': 'int'
43024                             },
43025                             {
43026                                 'name': 'product_id',
43027                                 'type': 'int'
43028                             },
43029                             {
43030                                 'name': 'productitem_id',
43031                                 'type': 'int'
43032                             },
43033                             {
43034                                 'name': 'guid',
43035                                 'type': 'int'
43036                             }
43037                         ]
43038                     }
43039                 },
43040                 toolbar : {
43041                     xtype: 'Toolbar',
43042                     xns: Roo,
43043                     items : [
43044                         {
43045                             xtype: 'Button',
43046                             xns: Roo.Toolbar,
43047                             listeners : {
43048                                 click : function (_self, e)
43049                                 {
43050                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43051                                     sd.setMonth(sd.getMonth()-1);
43052                                     _this.monthField.setValue(sd.format('Y-m-d'));
43053                                     _this.grid.ds.load({});
43054                                 }
43055                             },
43056                             text : "Back"
43057                         },
43058                         {
43059                             xtype: 'Separator',
43060                             xns: Roo.Toolbar
43061                         },
43062                         {
43063                             xtype: 'MonthField',
43064                             xns: Roo.form,
43065                             listeners : {
43066                                 render : function (_self)
43067                                 {
43068                                     _this.monthField = _self;
43069                                    // _this.monthField.set  today
43070                                 },
43071                                 select : function (combo, date)
43072                                 {
43073                                     _this.grid.ds.load({});
43074                                 }
43075                             },
43076                             value : (function() { return new Date(); })()
43077                         },
43078                         {
43079                             xtype: 'Separator',
43080                             xns: Roo.Toolbar
43081                         },
43082                         {
43083                             xtype: 'TextItem',
43084                             xns: Roo.Toolbar,
43085                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
43086                         },
43087                         {
43088                             xtype: 'Fill',
43089                             xns: Roo.Toolbar
43090                         },
43091                         {
43092                             xtype: 'Button',
43093                             xns: Roo.Toolbar,
43094                             listeners : {
43095                                 click : function (_self, e)
43096                                 {
43097                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43098                                     sd.setMonth(sd.getMonth()+1);
43099                                     _this.monthField.setValue(sd.format('Y-m-d'));
43100                                     _this.grid.ds.load({});
43101                                 }
43102                             },
43103                             text : "Next"
43104                         }
43105                     ]
43106                 },
43107                  
43108             }
43109         };
43110         
43111         *//*
43112  * Based on:
43113  * Ext JS Library 1.1.1
43114  * Copyright(c) 2006-2007, Ext JS, LLC.
43115  *
43116  * Originally Released Under LGPL - original licence link has changed is not relivant.
43117  *
43118  * Fork - LGPL
43119  * <script type="text/javascript">
43120  */
43121  
43122 /**
43123  * @class Roo.LoadMask
43124  * A simple utility class for generically masking elements while loading data.  If the element being masked has
43125  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
43126  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
43127  * element's UpdateManager load indicator and will be destroyed after the initial load.
43128  * @constructor
43129  * Create a new LoadMask
43130  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
43131  * @param {Object} config The config object
43132  */
43133 Roo.LoadMask = function(el, config){
43134     this.el = Roo.get(el);
43135     Roo.apply(this, config);
43136     if(this.store){
43137         this.store.on('beforeload', this.onBeforeLoad, this);
43138         this.store.on('load', this.onLoad, this);
43139         this.store.on('loadexception', this.onLoadException, this);
43140         this.removeMask = false;
43141     }else{
43142         var um = this.el.getUpdateManager();
43143         um.showLoadIndicator = false; // disable the default indicator
43144         um.on('beforeupdate', this.onBeforeLoad, this);
43145         um.on('update', this.onLoad, this);
43146         um.on('failure', this.onLoad, this);
43147         this.removeMask = true;
43148     }
43149 };
43150
43151 Roo.LoadMask.prototype = {
43152     /**
43153      * @cfg {Boolean} removeMask
43154      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
43155      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
43156      */
43157     removeMask : false,
43158     /**
43159      * @cfg {String} msg
43160      * The text to display in a centered loading message box (defaults to 'Loading...')
43161      */
43162     msg : 'Loading...',
43163     /**
43164      * @cfg {String} msgCls
43165      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
43166      */
43167     msgCls : 'x-mask-loading',
43168
43169     /**
43170      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
43171      * @type Boolean
43172      */
43173     disabled: false,
43174
43175     /**
43176      * Disables the mask to prevent it from being displayed
43177      */
43178     disable : function(){
43179        this.disabled = true;
43180     },
43181
43182     /**
43183      * Enables the mask so that it can be displayed
43184      */
43185     enable : function(){
43186         this.disabled = false;
43187     },
43188     
43189     onLoadException : function()
43190     {
43191         Roo.log(arguments);
43192         
43193         if (typeof(arguments[3]) != 'undefined') {
43194             Roo.MessageBox.alert("Error loading",arguments[3]);
43195         } 
43196         /*
43197         try {
43198             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43199                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43200             }   
43201         } catch(e) {
43202             
43203         }
43204         */
43205     
43206         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43207     },
43208     // private
43209     onLoad : function()
43210     {
43211         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43212     },
43213
43214     // private
43215     onBeforeLoad : function(){
43216         if(!this.disabled){
43217             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43218         }
43219     },
43220
43221     // private
43222     destroy : function(){
43223         if(this.store){
43224             this.store.un('beforeload', this.onBeforeLoad, this);
43225             this.store.un('load', this.onLoad, this);
43226             this.store.un('loadexception', this.onLoadException, this);
43227         }else{
43228             var um = this.el.getUpdateManager();
43229             um.un('beforeupdate', this.onBeforeLoad, this);
43230             um.un('update', this.onLoad, this);
43231             um.un('failure', this.onLoad, this);
43232         }
43233     }
43234 };/*
43235  * Based on:
43236  * Ext JS Library 1.1.1
43237  * Copyright(c) 2006-2007, Ext JS, LLC.
43238  *
43239  * Originally Released Under LGPL - original licence link has changed is not relivant.
43240  *
43241  * Fork - LGPL
43242  * <script type="text/javascript">
43243  */
43244
43245
43246 /**
43247  * @class Roo.XTemplate
43248  * @extends Roo.Template
43249  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43250 <pre><code>
43251 var t = new Roo.XTemplate(
43252         '&lt;select name="{name}"&gt;',
43253                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
43254         '&lt;/select&gt;'
43255 );
43256  
43257 // then append, applying the master template values
43258  </code></pre>
43259  *
43260  * Supported features:
43261  *
43262  *  Tags:
43263
43264 <pre><code>
43265       {a_variable} - output encoded.
43266       {a_variable.format:("Y-m-d")} - call a method on the variable
43267       {a_variable:raw} - unencoded output
43268       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43269       {a_variable:this.method_on_template(...)} - call a method on the template object.
43270  
43271 </code></pre>
43272  *  The tpl tag:
43273 <pre><code>
43274         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
43275         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
43276         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
43277         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
43278   
43279         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
43280         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
43281 </code></pre>
43282  *      
43283  */
43284 Roo.XTemplate = function()
43285 {
43286     Roo.XTemplate.superclass.constructor.apply(this, arguments);
43287     if (this.html) {
43288         this.compile();
43289     }
43290 };
43291
43292
43293 Roo.extend(Roo.XTemplate, Roo.Template, {
43294
43295     /**
43296      * The various sub templates
43297      */
43298     tpls : false,
43299     /**
43300      *
43301      * basic tag replacing syntax
43302      * WORD:WORD()
43303      *
43304      * // you can fake an object call by doing this
43305      *  x.t:(test,tesT) 
43306      * 
43307      */
43308     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43309
43310     /**
43311      * compile the template
43312      *
43313      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43314      *
43315      */
43316     compile: function()
43317     {
43318         var s = this.html;
43319      
43320         s = ['<tpl>', s, '</tpl>'].join('');
43321     
43322         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43323             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43324             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
43325             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43326             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
43327             m,
43328             id     = 0,
43329             tpls   = [];
43330     
43331         while(true == !!(m = s.match(re))){
43332             var forMatch   = m[0].match(nameRe),
43333                 ifMatch   = m[0].match(ifRe),
43334                 execMatch   = m[0].match(execRe),
43335                 namedMatch   = m[0].match(namedRe),
43336                 
43337                 exp  = null, 
43338                 fn   = null,
43339                 exec = null,
43340                 name = forMatch && forMatch[1] ? forMatch[1] : '';
43341                 
43342             if (ifMatch) {
43343                 // if - puts fn into test..
43344                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43345                 if(exp){
43346                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43347                 }
43348             }
43349             
43350             if (execMatch) {
43351                 // exec - calls a function... returns empty if true is  returned.
43352                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43353                 if(exp){
43354                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43355                 }
43356             }
43357             
43358             
43359             if (name) {
43360                 // for = 
43361                 switch(name){
43362                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43363                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43364                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43365                 }
43366             }
43367             var uid = namedMatch ? namedMatch[1] : id;
43368             
43369             
43370             tpls.push({
43371                 id:     namedMatch ? namedMatch[1] : id,
43372                 target: name,
43373                 exec:   exec,
43374                 test:   fn,
43375                 body:   m[1] || ''
43376             });
43377             if (namedMatch) {
43378                 s = s.replace(m[0], '');
43379             } else { 
43380                 s = s.replace(m[0], '{xtpl'+ id + '}');
43381             }
43382             ++id;
43383         }
43384         this.tpls = [];
43385         for(var i = tpls.length-1; i >= 0; --i){
43386             this.compileTpl(tpls[i]);
43387             this.tpls[tpls[i].id] = tpls[i];
43388         }
43389         this.master = tpls[tpls.length-1];
43390         return this;
43391     },
43392     /**
43393      * same as applyTemplate, except it's done to one of the subTemplates
43394      * when using named templates, you can do:
43395      *
43396      * var str = pl.applySubTemplate('your-name', values);
43397      *
43398      * 
43399      * @param {Number} id of the template
43400      * @param {Object} values to apply to template
43401      * @param {Object} parent (normaly the instance of this object)
43402      */
43403     applySubTemplate : function(id, values, parent)
43404     {
43405         
43406         
43407         var t = this.tpls[id];
43408         
43409         
43410         try { 
43411             if(t.test && !t.test.call(this, values, parent)){
43412                 return '';
43413             }
43414         } catch(e) {
43415             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43416             Roo.log(e.toString());
43417             Roo.log(t.test);
43418             return ''
43419         }
43420         try { 
43421             
43422             if(t.exec && t.exec.call(this, values, parent)){
43423                 return '';
43424             }
43425         } catch(e) {
43426             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43427             Roo.log(e.toString());
43428             Roo.log(t.exec);
43429             return ''
43430         }
43431         try {
43432             var vs = t.target ? t.target.call(this, values, parent) : values;
43433             parent = t.target ? values : parent;
43434             if(t.target && vs instanceof Array){
43435                 var buf = [];
43436                 for(var i = 0, len = vs.length; i < len; i++){
43437                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
43438                 }
43439                 return buf.join('');
43440             }
43441             return t.compiled.call(this, vs, parent);
43442         } catch (e) {
43443             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43444             Roo.log(e.toString());
43445             Roo.log(t.compiled);
43446             return '';
43447         }
43448     },
43449
43450     compileTpl : function(tpl)
43451     {
43452         var fm = Roo.util.Format;
43453         var useF = this.disableFormats !== true;
43454         var sep = Roo.isGecko ? "+" : ",";
43455         var undef = function(str) {
43456             Roo.log("Property not found :"  + str);
43457             return '';
43458         };
43459         
43460         var fn = function(m, name, format, args)
43461         {
43462             //Roo.log(arguments);
43463             args = args ? args.replace(/\\'/g,"'") : args;
43464             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43465             if (typeof(format) == 'undefined') {
43466                 format= 'htmlEncode';
43467             }
43468             if (format == 'raw' ) {
43469                 format = false;
43470             }
43471             
43472             if(name.substr(0, 4) == 'xtpl'){
43473                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43474             }
43475             
43476             // build an array of options to determine if value is undefined..
43477             
43478             // basically get 'xxxx.yyyy' then do
43479             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43480             //    (function () { Roo.log("Property not found"); return ''; })() :
43481             //    ......
43482             
43483             var udef_ar = [];
43484             var lookfor = '';
43485             Roo.each(name.split('.'), function(st) {
43486                 lookfor += (lookfor.length ? '.': '') + st;
43487                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
43488             });
43489             
43490             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43491             
43492             
43493             if(format && useF){
43494                 
43495                 args = args ? ',' + args : "";
43496                  
43497                 if(format.substr(0, 5) != "this."){
43498                     format = "fm." + format + '(';
43499                 }else{
43500                     format = 'this.call("'+ format.substr(5) + '", ';
43501                     args = ", values";
43502                 }
43503                 
43504                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
43505             }
43506              
43507             if (args.length) {
43508                 // called with xxyx.yuu:(test,test)
43509                 // change to ()
43510                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
43511             }
43512             // raw.. - :raw modifier..
43513             return "'"+ sep + udef_st  + name + ")"+sep+"'";
43514             
43515         };
43516         var body;
43517         // branched to use + in gecko and [].join() in others
43518         if(Roo.isGecko){
43519             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
43520                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43521                     "';};};";
43522         }else{
43523             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
43524             body.push(tpl.body.replace(/(\r\n|\n)/g,
43525                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43526             body.push("'].join('');};};");
43527             body = body.join('');
43528         }
43529         
43530         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43531        
43532         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
43533         eval(body);
43534         
43535         return this;
43536     },
43537
43538     applyTemplate : function(values){
43539         return this.master.compiled.call(this, values, {});
43540         //var s = this.subs;
43541     },
43542
43543     apply : function(){
43544         return this.applyTemplate.apply(this, arguments);
43545     }
43546
43547  });
43548
43549 Roo.XTemplate.from = function(el){
43550     el = Roo.getDom(el);
43551     return new Roo.XTemplate(el.value || el.innerHTML);
43552 };